ids = trackables.keySet();
+ for (String id: ids) {
+ ARTrackable t = trackables.get(id);
+ if (t.isStopped()) trackables.remove(id);
+ }
+ }
+
+ protected void remove(int idx) {
+ int id = g.trackableId(idx);
+ String sid = String.valueOf(id);
+ remove(sid);
+ }
+
+ protected void remove(String id) {
+ trackables.remove(id);
+ }
+
+ protected void setEventHandler() {
+ try {
+ trackableEventMethod = p.getClass().getMethod("trackableEvent", ARTrackable.class);
+ return;
+ } catch (Exception e) {
+ // no such method, or an error... which is fine, just ignore
+ }
+
+ // trackableEvent can alternatively be defined as receiving an Object, to allow
+ // Processing mode implementors to support the video library without linking
+ // to it at build-time.
+ try {
+ trackableEventMethod = p.getClass().getMethod("trackableEvent", Object.class);
+ } catch (Exception e) {
+ // no such method, or an error... which is fine, just ignore
+ }
+ }
+}
diff --git a/libs/processing-ar/src/main/java/processing/ar/BackgroundRenderer.java b/libs/processing-ar/src/main/java/processing/ar/BackgroundRenderer.java
new file mode 100644
index 000000000..f00464866
--- /dev/null
+++ b/libs/processing-ar/src/main/java/processing/ar/BackgroundRenderer.java
@@ -0,0 +1,152 @@
+package processing.ar;
+
+import android.content.Context;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+
+import com.google.ar.core.Frame;
+
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+public class BackgroundRenderer {
+
+ private static final int COORDS_PER_VERTEX = 3;
+ private static final int TEXCOORDS_PER_VERTEX = 2;
+ private static final int FLOAT_SIZE = 4;
+
+ private FloatBuffer quadVertices;
+ private FloatBuffer quadTexCoord;
+ private FloatBuffer quadTexCoordTransformed;
+
+ private int quadProgram;
+
+ private int quadPositionParam;
+ private int quadTexCoordParam;
+ private int textureId = -1;
+
+ static private URL screenquad_vertex =
+ BackgroundRenderer.class.getResource("/assets/shaders/BackgroundVert.glsl");
+ static private URL screenquad_fragment =
+ BackgroundRenderer.class.getResource("/assets/shaders/BackgroundFrag.glsl");
+
+ private String VERTICES_ERROR = "Unexpected number of vertices in BackgroundRenderer";
+ private String ERROR_TAG = "Error";
+ private String CREATION_ERROR = "Program creation";
+ private String PARAMETERS_ERROR = "Program parameters";
+ private String DRAW_ERROR = "Draw";
+
+ public BackgroundRenderer(Context context) {
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ textureId = textures[0];
+ int textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
+ GLES20.glBindTexture(textureTarget, textureId);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+
+ int numVertices = 4;
+ if (numVertices != QUAD_COORDS.length / COORDS_PER_VERTEX) {
+ throw new RuntimeException(VERTICES_ERROR);
+ }
+
+ ByteBuffer bbVertices = ByteBuffer.allocateDirect(QUAD_COORDS.length * FLOAT_SIZE);
+ bbVertices.order(ByteOrder.nativeOrder());
+ quadVertices = bbVertices.asFloatBuffer();
+ quadVertices.put(QUAD_COORDS);
+ quadVertices.position(0);
+
+ ByteBuffer bbTexCoords =
+ ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
+ bbTexCoords.order(ByteOrder.nativeOrder());
+ quadTexCoord = bbTexCoords.asFloatBuffer();
+ quadTexCoord.put(QUAD_TEXCOORDS);
+ quadTexCoord.position(0);
+
+ ByteBuffer bbTexCoordsTransformed =
+ ByteBuffer.allocateDirect(numVertices * TEXCOORDS_PER_VERTEX * FLOAT_SIZE);
+ bbTexCoordsTransformed.order(ByteOrder.nativeOrder());
+ quadTexCoordTransformed = bbTexCoordsTransformed.asFloatBuffer();
+
+ int vertexShader =
+ ShaderUtils.loadGLShader(ERROR_TAG, context, GLES20.GL_VERTEX_SHADER, screenquad_vertex);
+ int fragmentShader =
+ ShaderUtils.loadGLShader(
+ ERROR_TAG, context, GLES20.GL_FRAGMENT_SHADER, screenquad_fragment);
+
+ quadProgram = GLES20.glCreateProgram();
+ GLES20.glAttachShader(quadProgram, vertexShader);
+ GLES20.glAttachShader(quadProgram, fragmentShader);
+ GLES20.glLinkProgram(quadProgram);
+ GLES20.glUseProgram(quadProgram);
+
+ ShaderUtils.checkGLError(ERROR_TAG, CREATION_ERROR);
+
+ quadPositionParam = GLES20.glGetAttribLocation(quadProgram, "a_Position");
+ quadTexCoordParam = GLES20.glGetAttribLocation(quadProgram, "a_TexCoord");
+
+ ShaderUtils.checkGLError(ERROR_TAG, PARAMETERS_ERROR);
+ }
+
+ public int getTextureId() {
+ return textureId;
+ }
+
+ public void draw(Frame frame) {
+
+ if (frame.hasDisplayGeometryChanged()) {
+ frame.transformDisplayUvCoords(quadTexCoord, quadTexCoordTransformed);
+ }
+
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+ GLES20.glDepthMask(false);
+
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
+
+ GLES20.glUseProgram(quadProgram);
+
+ GLES20.glVertexAttribPointer(
+ quadPositionParam, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, quadVertices);
+
+ GLES20.glVertexAttribPointer(
+ quadTexCoordParam,
+ TEXCOORDS_PER_VERTEX,
+ GLES20.GL_FLOAT,
+ false,
+ 0,
+ quadTexCoordTransformed);
+
+ GLES20.glEnableVertexAttribArray(quadPositionParam);
+ GLES20.glEnableVertexAttribArray(quadTexCoordParam);
+
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+
+ GLES20.glDisableVertexAttribArray(quadPositionParam);
+ GLES20.glDisableVertexAttribArray(quadTexCoordParam);
+
+ GLES20.glDepthMask(true);
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+
+ ShaderUtils.checkGLError(ERROR_TAG, DRAW_ERROR);
+ }
+
+ private static final float[] QUAD_COORDS =
+ new float[]{
+ -1.0f, -1.0f, 0.0f,
+ -1.0f, +1.0f, 0.0f,
+ +1.0f, -1.0f, 0.0f,
+ +1.0f, +1.0f, 0.0f,
+ };
+
+ private static final float[] QUAD_TEXCOORDS =
+ new float[]{
+ 0.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+ };
+}
diff --git a/libs/processing-ar/src/main/java/processing/ar/RotationHandler.java b/libs/processing-ar/src/main/java/processing/ar/RotationHandler.java
new file mode 100644
index 000000000..e11c4b8e3
--- /dev/null
+++ b/libs/processing-ar/src/main/java/processing/ar/RotationHandler.java
@@ -0,0 +1,63 @@
+package processing.ar;
+
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.google.ar.core.Session;
+
+
+public class RotationHandler implements DisplayManager.DisplayListener {
+ private boolean viewportChanged;
+ private int viewportWidth;
+ private int viewportHeight;
+ private final Context context;
+ private final Display display;
+
+ public RotationHandler(Context context) {
+ this.context = context;
+ WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ display = windowManager.getDefaultDisplay();
+ }
+
+ public void onResume() {
+ ((DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE)).registerDisplayListener(this, null);
+ }
+
+ public void onPause() {
+ ((DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE)).unregisterDisplayListener(this);
+ }
+
+ public void onSurfaceChanged(int width, int height) {
+ viewportWidth = width;
+ viewportHeight = height;
+ viewportChanged = true;
+ }
+
+ public void updateSessionIfNeeded(Session session) {
+ if (viewportChanged) {
+ int displayRotation = display.getRotation();
+ session.setDisplayGeometry(displayRotation, viewportWidth, viewportHeight);
+ viewportChanged = false;
+ }
+ }
+
+ public int getRotation() {
+ return display.getRotation();
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ viewportChanged = true;
+ }
+}
\ No newline at end of file
diff --git a/libs/processing-ar/src/main/java/processing/ar/ShaderUtils.java b/libs/processing-ar/src/main/java/processing/ar/ShaderUtils.java
new file mode 100644
index 000000000..97209d51b
--- /dev/null
+++ b/libs/processing-ar/src/main/java/processing/ar/ShaderUtils.java
@@ -0,0 +1,60 @@
+package processing.ar;
+
+import android.content.Context;
+import android.opengl.GLES20;
+
+import processing.core.PApplet;
+import processing.core.PGraphics;
+
+import java.io.IOException;
+import java.net.URL;
+
+public class ShaderUtils {
+ public static int loadGLShader(String tag, Context context, int type, URL resUrl) {
+ String code = readRawTextFile(resUrl);
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, code);
+ GLES20.glCompileShader(shader);
+
+ final int[] compileStatus = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
+
+ if (compileStatus[0] == 0) {
+ PGraphics.showWarning("Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+
+ if (shader == 0) {
+ throw new RuntimeException("Error creating shader.");
+ }
+
+ return shader;
+ }
+
+ public static void checkGLError(String tag, String label) {
+ int lastError = GLES20.GL_NO_ERROR;
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ PGraphics.showWarning(label + ": glError " + error);
+ lastError = error;
+ }
+ if (lastError != GLES20.GL_NO_ERROR) {
+ throw new RuntimeException(label + ": glError " + lastError);
+ }
+ }
+
+ private static String readRawTextFile(URL url) {
+ try {
+ String[] sample = PApplet.loadStrings(url.openStream());
+ StringBuilder stringBuilder = new StringBuilder();
+ for (String sam : sample) {
+ stringBuilder.append(sam).append("\n");
+ }
+ return stringBuilder.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/libs/processing-core/build.gradle b/libs/processing-core/build.gradle
new file mode 100644
index 000000000..b7952ec4d
--- /dev/null
+++ b/libs/processing-core/build.gradle
@@ -0,0 +1,33 @@
+plugins {
+ id 'com.android.library'
+}
+
+android {
+
+ namespace "processing.core"
+
+ defaultConfig {
+ minSdkVersion 17
+ targetSdkVersion 33
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ testImplementation 'junit:junit:4.13.2'
+
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'com.google.android.support:wearable:2.9.0'
+ compileOnly 'com.google.android.wearable:wearable:2.9.0'
+}
\ No newline at end of file
diff --git a/libs/processing-core/proguard-rules.pro b/libs/processing-core/proguard-rules.pro
new file mode 100644
index 000000000..f1b424510
--- /dev/null
+++ b/libs/processing-core/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/libs/processing-core/src/main/AndroidManifest.xml b/libs/processing-core/src/main/AndroidManifest.xml
new file mode 100755
index 000000000..97330b776
--- /dev/null
+++ b/libs/processing-core/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/core/src/processing/opengl/ColorFrag.glsl b/libs/processing-core/src/main/assets/shaders/ColorFrag.glsl
similarity index 77%
rename from core/src/processing/opengl/ColorFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/ColorFrag.glsl
index d5d23ae22..5f86d92b4 100644
--- a/core/src/processing/opengl/ColorFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/ColorFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,7 +18,7 @@
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
- */
+*/
#ifdef GL_ES
precision mediump float;
diff --git a/core/src/processing/opengl/ColorVert.glsl b/libs/processing-core/src/main/assets/shaders/ColorVert.glsl
similarity index 78%
rename from core/src/processing/opengl/ColorVert.glsl
rename to libs/processing-core/src/main/assets/shaders/ColorVert.glsl
index 3736ffd2e..65fd55716 100644
--- a/core/src/processing/opengl/ColorVert.glsl
+++ b/libs/processing-core/src/main/assets/shaders/ColorVert.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,9 +18,7 @@
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
- */
-
-#define PROCESSING_COLOR_SHADER
+*/
uniform mat4 transformMatrix;
diff --git a/core/src/processing/opengl/LightFrag.glsl b/libs/processing-core/src/main/assets/shaders/LightFrag.glsl
similarity index 78%
rename from core/src/processing/opengl/LightFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/LightFrag.glsl
index b566c8e5b..6b60d4039 100644
--- a/core/src/processing/opengl/LightFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/LightFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,8 +18,8 @@
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
- */
-
+*/
+
#ifdef GL_ES
precision mediump float;
precision mediump int;
diff --git a/core/src/processing/opengl/LightVert.glsl b/libs/processing-core/src/main/assets/shaders/LightVert.glsl
similarity index 94%
rename from core/src/processing/opengl/LightVert.glsl
rename to libs/processing-core/src/main/assets/shaders/LightVert.glsl
index 8d75f970a..adc4bf9ad 100644
--- a/core/src/processing/opengl/LightVert.glsl
+++ b/libs/processing-core/src/main/assets/shaders/LightVert.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,9 +18,7 @@
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
- */
-
-#define PROCESSING_LIGHT_SHADER
+*/
uniform mat4 modelviewMatrix;
uniform mat4 transformMatrix;
@@ -98,7 +98,7 @@ void main() {
if (lightCount == i) break;
vec3 lightPos = lightPosition[i].xyz;
- bool isDir = zero_float < lightPosition[i].w;
+ bool isDir = lightPosition[i].w < one_float;
float spotCos = lightSpot[i].x;
float spotExp = lightSpot[i].y;
diff --git a/core/src/processing/opengl/LineFrag.glsl b/libs/processing-core/src/main/assets/shaders/LineFrag.glsl
similarity index 77%
rename from core/src/processing/opengl/LineFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/LineFrag.glsl
index dc08a3fa2..9046e17e9 100644
--- a/core/src/processing/opengl/LineFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/LineFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,7 +18,7 @@
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
- */
+*/
#ifdef GL_ES
precision mediump float;
diff --git a/libs/processing-core/src/main/assets/shaders/LineVert.glsl b/libs/processing-core/src/main/assets/shaders/LineVert.glsl
new file mode 100644
index 000000000..15e7bf951
--- /dev/null
+++ b/libs/processing-core/src/main/assets/shaders/LineVert.glsl
@@ -0,0 +1,99 @@
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-17 The Processing Foundation
+ Copyright (c) 2004-12 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, version 2.1.
+
+ 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
+*/
+
+#define PROCESSING_LINE_SHADER
+
+uniform mat4 modelviewMatrix;
+uniform mat4 projectionMatrix;
+
+uniform vec4 viewport;
+uniform int perspective;
+uniform vec3 scale;
+
+attribute vec4 position;
+attribute vec4 color;
+attribute vec4 direction;
+
+varying vec4 vertColor;
+
+void main() {
+ vec4 posp = modelviewMatrix * position;
+ vec4 posq = modelviewMatrix * (position + vec4(direction.xyz, 0));
+
+ // Moving vertices slightly toward the camera
+ // to avoid depth-fighting with the fill triangles.
+ // Discussed here:
+ // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848
+ posp.xyz = posp.xyz * scale;
+ posq.xyz = posq.xyz * scale;
+
+ vec4 p = projectionMatrix * posp;
+ vec4 q = projectionMatrix * posq;
+
+ // formula to convert from clip space (range -1..1) to screen space (range 0..[width or height])
+ // screen_p = (p.xy/p.w + <1,1>) * 0.5 * viewport.zw
+
+ // prevent division by W by transforming the tangent formula (div by 0 causes
+ // the line to disappear, see https://github.com/processing/processing/issues/5183)
+ // t = screen_q - screen_p
+ //
+ // tangent is normalized and we don't care which direction it points to (+-)
+ // t = +- normalize( screen_q - screen_p )
+ // t = +- normalize( (q.xy/q.w+<1,1>)*0.5*viewport.zw - (p.xy/p.w+<1,1>)*0.5*viewport.zw )
+ //
+ // extract common factor, <1,1> - <1,1> cancels out
+ // t = +- normalize( (q.xy/q.w - p.xy/p.w) * 0.5 * viewport.zw )
+ //
+ // convert to common divisor
+ // t = +- normalize( ((q.xy*p.w - p.xy*q.w) / (p.w*q.w)) * 0.5 * viewport.zw )
+ //
+ // remove the common scalar divisor/factor, not needed due to normalize and +-
+ // (keep viewport - can't remove because it has different components for x and y
+ // and corrects for aspect ratio, see https://github.com/processing/processing/issues/5181)
+ // t = +- normalize( (q.xy*p.w - p.xy*q.w) * viewport.zw )
+
+ vec2 tangent = (q.xy*p.w - p.xy*q.w) * viewport.zw;
+ // don't normalize zero vector (line join triangles and lines perpendicular to the eye plane)
+ tangent = length(tangent) == 0.0 ? vec2(0.0, 0.0) : normalize(tangent);
+
+ // flip tangent to normal (it's already normalized)
+ vec2 normal = vec2(-tangent.y, tangent.x);
+
+ float thickness = direction.w;
+ vec2 offset = normal * thickness;
+
+ // Perspective ---
+ // convert from world to clip by multiplying with projection scaling factor
+ // to get the right thickness (see https://github.com/processing/processing/issues/5182)
+ // invert Y, projections in Processing invert Y
+ vec2 perspScale = (projectionMatrix * vec4(1, -1, 0, 0)).xy;
+
+ // No Perspective ---
+ // multiply by W (to cancel out division by W later in the pipeline) and
+ // convert from screen to clip (derived from clip to screen above)
+ vec2 noPerspScale = p.w / (0.5 * viewport.zw);
+
+ gl_Position.xy = p.xy + offset.xy * mix(noPerspScale, perspScale, float(perspective > 0));
+ gl_Position.zw = p.zw;
+
+ vertColor = color;
+}
diff --git a/core/src/processing/opengl/MaskFrag.glsl b/libs/processing-core/src/main/assets/shaders/MaskFrag.glsl
similarity index 82%
rename from core/src/processing/opengl/MaskFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/MaskFrag.glsl
index 508a39649..3d7e7dbf1 100644
--- a/core/src/processing/opengl/MaskFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/MaskFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,7 +18,7 @@
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
- */
+*/
#ifdef GL_ES
precision mediump float;
diff --git a/libs/processing-core/src/main/assets/shaders/P2DFrag.glsl b/libs/processing-core/src/main/assets/shaders/P2DFrag.glsl
new file mode 100644
index 000000000..ce3a3fb27
--- /dev/null
+++ b/libs/processing-core/src/main/assets/shaders/P2DFrag.glsl
@@ -0,0 +1,14 @@
+#ifdef GL_ES
+precision mediump float;
+precision mediump int;
+#endif
+
+varying vec4 vertColor;
+varying vec2 vertTexCoord;
+varying float vertTexFactor;
+
+uniform sampler2D texture;
+
+void main() {
+ gl_FragColor = mix(vertColor, vertColor * texture2D(texture, vertTexCoord), vertTexFactor);
+}
diff --git a/libs/processing-core/src/main/assets/shaders/P2DVert.glsl b/libs/processing-core/src/main/assets/shaders/P2DVert.glsl
new file mode 100644
index 000000000..4e9a1f7e1
--- /dev/null
+++ b/libs/processing-core/src/main/assets/shaders/P2DVert.glsl
@@ -0,0 +1,23 @@
+attribute vec3 position;
+attribute vec4 color;
+attribute vec2 texCoord;
+attribute float texFactor;
+
+varying vec4 vertColor;
+varying vec2 vertTexCoord;
+varying float vertTexFactor;
+
+uniform mat4 transform;
+uniform vec2 texScale;
+
+void main() {
+ gl_Position = transform * vec4(position, 1);
+
+ //we avoid affecting the Z component by the transform
+ //because it would mess up our depth testing
+ gl_Position.z = position.z;
+
+ vertColor = color.zyxw;
+ vertTexCoord = texCoord * texScale;
+ vertTexFactor = texFactor;
+}
diff --git a/core/src/processing/opengl/PointFrag.glsl b/libs/processing-core/src/main/assets/shaders/PointFrag.glsl
similarity index 77%
rename from core/src/processing/opengl/PointFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/PointFrag.glsl
index d5d23ae22..5f86d92b4 100644
--- a/core/src/processing/opengl/PointFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/PointFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,7 +18,7 @@
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
- */
+*/
#ifdef GL_ES
precision mediump float;
diff --git a/core/src/processing/opengl/PointVert.glsl b/libs/processing-core/src/main/assets/shaders/PointVert.glsl
similarity index 51%
rename from core/src/processing/opengl/PointVert.glsl
rename to libs/processing-core/src/main/assets/shaders/PointVert.glsl
index c677ce75b..292674615 100644
--- a/core/src/processing/opengl/PointVert.glsl
+++ b/libs/processing-core/src/main/assets/shaders/PointVert.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-17 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,9 +18,7 @@
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
- */
-
-#define PROCESSING_POINT_SHADER
+*/
uniform mat4 projectionMatrix;
uniform mat4 modelviewMatrix;
@@ -32,24 +32,25 @@ attribute vec2 offset;
varying vec4 vertColor;
-vec4 windowToClipVector(vec2 window, vec4 viewport, float clipw) {
- vec2 xypos = (window / viewport.zw) * 2.0;
- return vec4(xypos, 0.0, 0.0) * clipw;
-}
-
void main() {
vec4 pos = modelviewMatrix * position;
vec4 clip = projectionMatrix * pos;
-
- if (0 < perspective) {
- // Perspective correction (points will look thiner as they move away
- // from the view position).
- gl_Position = clip + projectionMatrix * vec4(offset.xy, 0, 0);
- } else {
- // No perspective correction.
- vec4 offset = windowToClipVector(offset.xy, viewport, clip.w);
- gl_Position = clip + offset;
- }
+
+ // Perspective ---
+ // convert from world to clip by multiplying with projection scaling factor
+ // invert Y, projections in Processing invert Y
+ vec2 perspScale = (projectionMatrix * vec4(1, -1, 0, 0)).xy;
+
+ // formula to convert from clip space (range -1..1) to screen space (range 0..[width or height])
+ // screen_p = (p.xy/p.w + <1,1>) * 0.5 * viewport.zw
+
+ // No Perspective ---
+ // multiply by W (to cancel out division by W later in the pipeline) and
+ // convert from screen to clip (derived from clip to screen above)
+ vec2 noPerspScale = clip.w / (0.5 * viewport.zw);
+
+ gl_Position.xy = clip.xy + offset.xy * mix(noPerspScale, perspScale, float(perspective > 0));
+ gl_Position.zw = clip.zw;
vertColor = color;
}
\ No newline at end of file
diff --git a/core/src/processing/opengl/TextureFrag.glsl b/libs/processing-core/src/main/assets/shaders/TexFrag.glsl
similarity index 79%
rename from core/src/processing/opengl/TextureFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/TexFrag.glsl
index f041e61d1..c8e183d49 100644
--- a/core/src/processing/opengl/TextureFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/TexFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,7 +18,7 @@
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
- */
+*/
#ifdef GL_ES
precision mediump float;
diff --git a/core/src/processing/opengl/TexlightFrag.glsl b/libs/processing-core/src/main/assets/shaders/TexLightFrag.glsl
similarity index 80%
rename from core/src/processing/opengl/TexlightFrag.glsl
rename to libs/processing-core/src/main/assets/shaders/TexLightFrag.glsl
index f423e49d3..fd9b61576 100644
--- a/core/src/processing/opengl/TexlightFrag.glsl
+++ b/libs/processing-core/src/main/assets/shaders/TexLightFrag.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,8 +18,7 @@
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
- */
-
+*/
#ifdef GL_ES
precision mediump float;
precision mediump int;
diff --git a/core/src/processing/opengl/TexlightVert.glsl b/libs/processing-core/src/main/assets/shaders/TexLightVert.glsl
similarity index 94%
rename from core/src/processing/opengl/TexlightVert.glsl
rename to libs/processing-core/src/main/assets/shaders/TexLightVert.glsl
index d9f2cde3a..57551edce 100644
--- a/core/src/processing/opengl/TexlightVert.glsl
+++ b/libs/processing-core/src/main/assets/shaders/TexLightVert.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,9 +18,7 @@
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
- */
-
-#define PROCESSING_TEXLIGHT_SHADER
+*/
uniform mat4 modelviewMatrix;
uniform mat4 transformMatrix;
@@ -101,7 +101,7 @@ void main() {
if (lightCount == i) break;
vec3 lightPos = lightPosition[i].xyz;
- bool isDir = zero_float < lightPosition[i].w;
+ bool isDir = lightPosition[i].w < one_float;
float spotCos = lightSpot[i].x;
float spotExp = lightSpot[i].y;
diff --git a/core/src/processing/opengl/TextureVert.glsl b/libs/processing-core/src/main/assets/shaders/TexVert.glsl
similarity index 80%
rename from core/src/processing/opengl/TextureVert.glsl
rename to libs/processing-core/src/main/assets/shaders/TexVert.glsl
index 87f101536..c07dc30d7 100644
--- a/core/src/processing/opengl/TextureVert.glsl
+++ b/libs/processing-core/src/main/assets/shaders/TexVert.glsl
@@ -1,11 +1,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-16 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,9 +18,7 @@
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
- */
-
-#define PROCESSING_TEXTURE_SHADER
+*/
uniform mat4 transformMatrix;
uniform mat4 texMatrix;
diff --git a/core/src/processing/core/PGraphicsAndroid2D.java b/libs/processing-core/src/main/java/processing/a2d/PGraphicsAndroid2D.java
similarity index 76%
rename from core/src/processing/core/PGraphicsAndroid2D.java
rename to libs/processing-core/src/main/java/processing/a2d/PGraphicsAndroid2D.java
index 2916846c0..b82ced644 100644
--- a/core/src/processing/core/PGraphicsAndroid2D.java
+++ b/libs/processing-core/src/main/java/processing/a2d/PGraphicsAndroid2D.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2005-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2005-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -20,18 +21,42 @@
Boston, MA 02111-1307 USA
*/
-package processing.core;
+package processing.a2d;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
-
-import processing.data.XML;
+import android.annotation.SuppressLint;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
+import android.content.Context;
import android.graphics.*;
import android.graphics.Bitmap.Config;
import android.graphics.Paint.Style;
+import android.os.Build;
+import android.os.Environment;
+import android.view.SurfaceHolder;
+import static android.os.Environment.isExternalStorageRemovable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.nio.ByteBuffer;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import processing.android.AppComponent;
+import processing.core.PApplet;
+import processing.core.PFont;
+import processing.core.PGraphics;
+import processing.core.PImage;
+import processing.core.PMatrix;
+import processing.core.PMatrix2D;
+import processing.core.PMatrix3D;
+import processing.core.PShape;
+import processing.core.PShapeSVG;
+import processing.core.PSurface;
+import processing.data.XML;
/**
* Subclass for PGraphics that implements the graphics API using
@@ -39,6 +64,7 @@
* with the original (desktop) version of Processing.
*/
public class PGraphicsAndroid2D extends PGraphics {
+ static public boolean useBitmap = true;
public Canvas canvas; // like g2 for PGraphicsJava2D
@@ -51,9 +77,12 @@ public class PGraphicsAndroid2D extends PGraphics {
float[] curveDrawX;
float[] curveDrawY;
-// int transformCount;
-// Matrix[] transformStack;
- float[] transform;
+ static protected final int MATRIX_STACK_DEPTH = 32;
+ protected float[][] transformStack;
+ public PMatrix2D transform;
+ protected Matrix transformMatrix;
+ protected float[] transformArray;
+ int transformCount;
// Line2D.Float line = new Line2D.Float();
// Ellipse2D.Float ellipse = new Ellipse2D.Float();
@@ -86,15 +115,27 @@ public class PGraphicsAndroid2D extends PGraphics {
Paint tintPaint;
+ /**
+ * Marks when changes to the size have occurred, so that the backing bitmap
+ * can be recreated.
+ */
+ protected boolean sized;
+
+ /**
+ * Marks when some changes have occurred, to the surface view.
+ */
+ protected boolean changed;
+
//////////////////////////////////////////////////////////////
// INTERNAL
public PGraphicsAndroid2D() {
-// transformStack = new Matrix[MATRIX_STACK_DEPTH];
-// transform = new float[6];
- transform = new float[9];
+ transformStack = new float[MATRIX_STACK_DEPTH][6];
+ transform = new PMatrix2D();
+ transformMatrix = new Matrix();
+ transformArray = new float[9];
path = new Path();
rect = new RectF();
@@ -115,74 +156,57 @@ public PGraphicsAndroid2D() {
//public void setPath(String path)
-
- /**
- * Called in response 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.
- */
@Override
- public void setSize(int iwidth, int iheight) { // ignore
- width = iwidth;
- height = iheight;
- width1 = width - 1;
- height1 = height - 1;
-
- allocate();
- reapplySettings();
+ public void surfaceChanged() {
+ changed = true;
}
@Override
- protected void allocate() {
- if (bitmap != null) bitmap.recycle();
- bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
- canvas = new Canvas(bitmap);
-// image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-// canvas = (Graphics2D) image.getGraphics();
+ public void setSize(int iwidth, int iheight) {
+ sized = iwidth != width || iheight != height;
+ super.setSize(iwidth, iheight);
}
@Override
public void dispose() {
- bitmap.recycle();
+ if (bitmap != null) bitmap.recycle();
}
+ @Override
+ public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore
+ return new PSurfaceAndroid2D(this, component, holder);
+ }
+
//////////////////////////////////////////////////////////////
// FRAME
- /*
- public void requestDraw() {
- parent.surfaceView.requestRender();
- }
- */
-
-// public boolean canDraw() {
-// return true;
-// }
-
-
- @Override
- public void requestDraw() {
- parent.handleDraw();
+ @SuppressLint("NewApi")
+ protected Canvas checkCanvas() {
+ if ((canvas == null || sized) && (useBitmap || !primaryGraphics)) {
+ if (bitmap == null || bitmap.getWidth() * bitmap.getHeight() < width * height ||
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ if (bitmap != null) bitmap.recycle();
+ bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+ } else {
+ // reconfigure is only available in API level 19 or higher.
+ bitmap.reconfigure(width, height, bitmap.getConfig());
+ }
+ canvas = new Canvas(bitmap);
+ sized = false;
+ }
+ restoreSurface();
+ return canvas;
}
@Override
public void beginDraw() {
-// if (primarySurface) {
-// canvas = parent.getSurfaceHolder().lockCanvas(null);
-// if (canvas == null) {
-// throw new RuntimeException("canvas is still null");
-// }
-// } else {
-// throw new RuntimeException("not primary surface");
-// }
+ canvas = checkCanvas();
checkSettings();
@@ -195,26 +219,27 @@ public void beginDraw() {
@Override
public void endDraw() {
- // hm, mark pixels as changed, because this will instantly do a full
- // copy of all the pixels to the surface.. so that's kind of a mess.
- //updatePixels();
-
-// if (primarySurface) {
-// if (canvas != null) {
-// parent.getSurfaceHolder().unlockCanvasAndPost(canvas);
-// }
-// }
-
- if (primarySurface) {
- Canvas screen = null;
- try {
- screen = parent.getSurfaceHolder().lockCanvas(null);
- if (screen != null) {
- screen.drawBitmap(bitmap, new Matrix(), null);
- }
- } finally {
- if (screen != null) {
- parent.getSurfaceHolder().unlockCanvasAndPost(screen);
+ if (bitmap == null) return;
+
+ if (primaryGraphics) {
+ SurfaceHolder holder = parent.getSurface().getSurfaceHolder();
+ if (holder != null) {
+ Canvas screen = null;
+ try {
+ screen = holder.lockCanvas(null);
+ if (screen != null) {
+ screen.drawBitmap(bitmap, new Matrix(), null);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (screen != null) {
+ try {
+ holder.unlockCanvasAndPost(screen);
+ } catch (IllegalStateException ex) {
+ } catch (IllegalArgumentException ex) {
+ }
+ }
}
}
} else {
@@ -349,6 +374,14 @@ public void vertex(float x, float y) {
}
break;
+ case LINE_STRIP:
+ case LINE_LOOP:
+ if (vertexCount >= 2) {
+ line(vertices[vertexCount-2][X],
+ vertices[vertexCount-2][Y], x, y);
+ }
+ break;
+
case TRIANGLES:
if ((vertexCount % 3) == 0) {
triangle(vertices[vertexCount - 3][X],
@@ -420,6 +453,12 @@ public void vertex(float x, float y, float z) {
}
+ @Override
+ public void vertex(float[] v) {
+ vertex(v[X], v[Y]);
+ }
+
+
@Override
public void vertex(float x, float y, float u, float v) {
showVariationWarning("vertex(x, y, u, v)");
@@ -441,7 +480,7 @@ public void breakShape() {
@Override
public void endShape(int mode) {
if (shape == POINTS && stroke && vertexCount > 0) {
- Matrix m = canvas.getMatrix();
+ Matrix m = getMatrixImp();
if (strokeWeight == 1 && m.isIdentity()) {
if (screenPoint == null) {
screenPoint = new float[2];
@@ -474,11 +513,32 @@ public void endShape(int mode) {
}
drawPath();
}
+ } else if (shape == LINE_LOOP && vertexCount >= 2) {
+ line(vertices[vertexCount-1][X],
+ vertices[vertexCount-1][Y],
+ vertices[0][X],
+ vertices[0][Y]);
}
shape = 0;
}
+ //////////////////////////////////////////////////////////////
+
+ // CLIPPING
+
+
+ @Override
+ protected void clipImpl(float x1, float y1, float x2, float y2) {
+ canvas.clipRect(x1, y1, x2, y2);
+ }
+
+
+ @Override
+ public void noClip() {
+ canvas.clipRect(0, 0, width, height, Region.Op.REPLACE);
+ }
+
//////////////////////////////////////////////////////////////
@@ -754,14 +814,43 @@ protected void arcImpl(float x, float y, float w, float h,
}
} else if (mode == OPEN) {
if (fill) {
- showMissingWarning("arc");
+ // Android does not support stroke and fill with different color
+ // after drawing the arc,draw the arc with Paint.Style.Stroke style
+ // again
+ canvas.drawArc(rect, start, sweep, false, fillPaint);
+ canvas.drawArc(rect, start, sweep, false, strokePaint);
}
if (stroke) {
canvas.drawArc(rect, start, sweep, false, strokePaint);
}
} else if (mode == CHORD) {
- showMissingWarning("arc");
+ // Draw an extra line between start angle point and end point to
+ // achieve the chord
+ float endAngle = start + sweep;
+ float halfRectWidth = rect.width()/2;
+ float halfRectHeight = rect.height()/2;
+ float centerX = rect.centerX();
+ float centerY = rect.centerY();
+
+ float startX = (float) (halfRectWidth* Math.cos(Math.toRadians(start))) + centerX;
+ float startY = (float) (halfRectHeight * Math.sin(Math.toRadians(start))) + centerY;
+ float endX = (float) (halfRectWidth * Math.cos(Math.toRadians(endAngle))) + centerX;
+ float endY = (float) (halfRectHeight * Math.sin(Math.toRadians(endAngle))) + centerY;
+ if (fill) {
+ // draw the fill arc
+ canvas.drawArc(rect,start,sweep,false,fillPaint);
+ // draw the arc round border
+ canvas.drawArc(rect,start,sweep,false,strokePaint);
+ // draw the straight border
+ canvas.drawLine(startX,startY,endX,endY,strokePaint);
+ }
+ if (stroke) {
+ // draw the arc
+ canvas.drawArc(rect,start,sweep,false,strokePaint);
+ // draw the straight border
+ canvas.drawLine(startX,startY,endX,endY,strokePaint);
+ }
} else if (mode == PIE) {
if (fill) {
canvas.drawArc(rect, start, sweep, true, fillPaint);
@@ -769,7 +858,6 @@ protected void arcImpl(float x, float y, float w, float h,
if (stroke) {
canvas.drawArc(rect, start, sweep, true, strokePaint);
}
-
}
}
}
@@ -945,8 +1033,9 @@ public void curveDetail(int detail) {
@Override
- public void smooth() {
- smooth = true;
+ public void smooth(int quality) { // ignore
+ super.smooth(quality);
+// smooth = true;
// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_ON);
// canvas.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
@@ -958,7 +1047,8 @@ public void smooth() {
@Override
public void noSmooth() {
- smooth = false;
+ super.noSmooth();
+// smooth = false;
// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_OFF);
// canvas.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
@@ -999,20 +1089,23 @@ public void noSmooth() {
protected void imageImpl(PImage src,
float x1, float y1, float x2, float y2,
int u1, int v1, int u2, int v2) {
- if (src.bitmap != null && src.bitmap.isRecycled()) {
+ Bitmap bitmap = (Bitmap)src.getNative();
+
+ if (bitmap != null && bitmap.isRecycled()) {
// Let's make sure it is recreated
- src.bitmap = null;
+ bitmap = null;
}
- if (src.bitmap == null && src.format == ALPHA) {
+ if (bitmap == null && src.format == ALPHA) {
// create an alpha bitmap for this feller
- src.bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
int[] px = new int[src.pixels.length];
for (int i = 0; i < px.length; i++) {
px[i] = src.pixels[i] << 24 | 0xFFFFFF;
}
- src.bitmap.setPixels(px, 0, src.width, 0, 0, src.width, src.height);
- src.modified = false;
+ bitmap.setPixels(px, 0, src.width, 0, 0, src.width, src.height);
+ modified = false;
+ src.setNative(bitmap);
}
// this version's not usable because it doesn't allow you to set output w/h
@@ -1023,23 +1116,26 @@ protected void imageImpl(PImage src,
// src.format == ARGB, tint ? tintPaint : null);
// } else {
- if (src.bitmap == null ||
- src.width != src.bitmap.getWidth() ||
- src.height != src.bitmap.getHeight()) {
- if (src.bitmap != null) src.bitmap.recycle();
- src.bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
- src.modified = true;
+ if (bitmap == null ||
+ src.width != bitmap.getWidth() ||
+ src.height != bitmap.getHeight()) {
+ if (bitmap != null) bitmap.recycle();
+ bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ modified = true;
+ src.setNative(bitmap);
}
- if (src.modified) {
+
+ if (src.isModified()) {
//System.out.println("mutable, recycled = " + who.bitmap.isMutable() + ", " + who.bitmap.isRecycled());
- if (!src.bitmap.isMutable()) {
- src.bitmap.recycle();
- src.bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ if (!bitmap.isMutable()) {
+ bitmap.recycle();
+ bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ src.setNative(bitmap);
}
if (src.pixels != null) {
- src.bitmap.setPixels(src.pixels, 0, src.width, 0, 0, src.width, src.height);
+ bitmap.setPixels(src.pixels, 0, src.width, 0, 0, src.width, src.height);
}
- src.modified = false;
+ src.setModified(false);
}
if (imageImplSrcRect == null) {
@@ -1053,17 +1149,19 @@ protected void imageImpl(PImage src,
//System.out.println(PApplet.hex(fillPaint.getColor()));
//canvas.drawBitmap(who.bitmap, imageImplSrcRect, imageImplDstRect, fillPaint);
// System.out.println("drawing lower, tint = " + tint + " " + PApplet.hex(tintPaint.getColor()));
- canvas.drawBitmap(src.bitmap, imageImplSrcRect, imageImplDstRect, tint ? tintPaint : null);
+ canvas.drawBitmap(bitmap, imageImplSrcRect, imageImplDstRect, tint ? tintPaint : null);
// If the OS things the memory is low, then recycles bitmaps automatically...
// but I don't think it is particularly efficient, as the bitmaps are stored
// in native heap for Android 10 and older.
MemoryInfo mi = new MemoryInfo();
- ActivityManager activityManager = (ActivityManager) parent.getApplicationContext().getSystemService(android.content.Context.ACTIVITY_SERVICE);
+ Activity activity = parent.getSurface().getActivity();
+ if (activity == null) return;
+ ActivityManager activityManager = (ActivityManager) activity.getSystemService(android.content.Context.ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
if (mi.lowMemory) {
- src.bitmap.recycle();
- src.bitmap = null;
+ bitmap.recycle();
+ src.setNative(null);
}
}
@@ -1157,10 +1255,16 @@ public PShape loadShape(String filename) {
public void textFont(PFont which) {
super.textFont(which);
fillPaint.setTypeface((Typeface) which.getNative());
+ fillPaint.setTextSize(which.getDefaultSize());
}
- //public void textFont(PFont which, float size)
+ @Override
+ public void textFont(PFont which, float size) {
+ super.textFont(which, size);
+ fillPaint.setTypeface((Typeface) which.getNative());
+ fillPaint.setTextSize(size);
+ }
//public void textLeading(float leading)
@@ -1192,14 +1296,7 @@ public void textSize(float size) {
fillPaint.setTextSize(size);
}
- // take care of setting the textSize and textLeading vars
- // this has to happen second, because it calls textAscent()
- // (which requires the native font metrics to be set)
- textSize = size;
-// PApplet.println("P2D textSize textAscent -> " + textAscent());
-// PApplet.println("P2D textSize textDescent -> " + textDescent());
- textLeading = (textAscent() + textDescent()) * 1.275f;
-// PApplet.println("P2D textSize textLeading = " + textLeading);
+ handleTextSize(size);
}
@@ -1291,7 +1388,7 @@ protected void textLineImpl(char buffer[], int start, int stop,
// textFont.smooth ?
// RenderingHints.VALUE_ANTIALIAS_ON :
// RenderingHints.VALUE_ANTIALIAS_OFF);
- fillPaint.setAntiAlias(textFont.smooth);
+ fillPaint.setAntiAlias(textFont.isSmooth());
//System.out.println("setting frac metrics");
//g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
@@ -1304,7 +1401,7 @@ protected void textLineImpl(char buffer[], int start, int stop,
// return to previous smoothing state if it was changed
// canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialias);
- fillPaint.setAntiAlias(smooth);
+ fillPaint.setAntiAlias(0 < smooth);
// textX = x + textWidthImpl(buffer, start, stop);
// textY = y;
@@ -1320,29 +1417,38 @@ protected void textLineImpl(char buffer[], int start, int stop,
@Override
public void pushMatrix() {
-// if (transformCount == transformStack.length) {
-// throw new RuntimeException("pushMatrix() cannot use push more than " +
-// transformStack.length + " times");
-// }
-// transformStack[transformCount] = canvas.getMatrix();
-// transformCount++;
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ if (transformCount == transformStack.length) {
+ throw new RuntimeException("pushMatrix() cannot use push more than " +
+ transformStack.length + " times");
+ }
+ transform.get(transformStack[transformCount]);
+ transformCount++;
+
+// canvas.save();
}
@Override
public void popMatrix() {
-// if (transformCount == 0) {
-// throw new RuntimeException("missing a popMatrix() " +
-// "to go with that pushMatrix()");
-// }
-// transformCount--;
-// canvas.setMatrix(transformStack[transformCount]);
- canvas.restore();
+ if (transformCount == 0) {
+ throw new RuntimeException("missing a popMatrix() " +
+ "to go with that pushMatrix()");
+ }
+ transformCount--;
+ transform.set(transformStack[transformCount]);
+ updateTransformMatrix();
+
+ // Using canvas.restore() here and canvas.save() in popMatrix() and should achieve
+ // the same effect as setting copying transform into transformMatrix with updateTransformMatrix()
+ // and setting it below, although it has been reported that with the later approach, a push/pop
+ // would not result in the initial matrix state:
+ // https://github.com/processing/processing-android/issues/445
+ // However, cannot find
+ canvas.setMatrix(transformMatrix);
+// canvas.restore();
}
-
//////////////////////////////////////////////////////////////
// MATRIX TRANSFORMS
@@ -1350,15 +1456,14 @@ public void popMatrix() {
@Override
public void translate(float tx, float ty) {
+ transform.translate(tx, ty);
canvas.translate(tx, ty);
}
- //public void translate(float tx, float ty, float tz)
-
-
@Override
public void rotate(float angle) {
+ transform.rotate(angle);
canvas.rotate(angle * RAD_TO_DEG);
}
@@ -1389,12 +1494,14 @@ public void rotate(float angle, float vx, float vy, float vz) {
@Override
public void scale(float s) {
+ transform.scale(s, s);
canvas.scale(s, s);
}
@Override
public void scale(float sx, float sy) {
+ transform.scale(sx, sy);
canvas.scale(sx, sy);
}
@@ -1407,17 +1514,20 @@ public void scale(float sx, float sy, float sz) {
@Override
public void shearX(float angle) {
- canvas.skew((float) Math.tan(angle), 0);
+ float t = (float) Math.tan(angle);
+ transform.apply(1, t, 0, 0, 1, 0);
+ canvas.skew(t, 0);
}
@Override
public void shearY(float angle) {
- canvas.skew(0, (float) Math.tan(angle));
+ float t = (float) Math.tan(angle);
+ transform.apply(1, 0, 0, t, 1, 0);
+ canvas.skew(0, t);
}
-
//////////////////////////////////////////////////////////////
// MATRIX MORE
@@ -1425,8 +1535,8 @@ public void shearY(float angle) {
@Override
public void resetMatrix() {
-// canvas.setTransform(new AffineTransform());
- canvas.setMatrix(new Matrix());
+ transform.reset();
+ canvas.setMatrix(null);
}
@@ -1436,15 +1546,9 @@ public void resetMatrix() {
@Override
public void applyMatrix(float n00, float n01, float n02,
float n10, float n11, float n12) {
-// canvas.transform(new AffineTransform(n00, n10, n01, n11, n02, n12));
- // TODO optimize
- Matrix m = new Matrix();
- m.setValues(new float[] {
- n00, n01, n02,
- n10, n11, n12,
- 0, 0, 1
- });
- canvas.concat(m);
+ transform.apply(n00, n01, n02, n10, n11, n12);
+ updateTransformMatrix();
+ canvas.concat(transformMatrix);
}
@@ -1477,14 +1581,7 @@ public PMatrix2D getMatrix(PMatrix2D target) {
if (target == null) {
target = new PMatrix2D();
}
-// canvas.getTransform().getMatrix(transform);
- Matrix m = new Matrix();
- canvas.getMatrix(m);
- m.getValues(transform);
-// target.set((float) transform[0], (float) transform[2], (float) transform[4],
-// (float) transform[1], (float) transform[3], (float) transform[5]);
- target.set((float) transform[0], (float) transform[1], (float) transform[2],
- (float) transform[3], (float) transform[4], (float) transform[5]);
+ target.set(transform);
return target;
}
@@ -1501,16 +1598,9 @@ public PMatrix3D getMatrix(PMatrix3D target) {
@Override
public void setMatrix(PMatrix2D source) {
-// canvas.setTransform(new AffineTransform(source.m00, source.m10,
-// source.m01, source.m11,
-// source.m02, source.m12));
- Matrix matrix = new Matrix();
- matrix.setValues(new float[] {
- source.m00, source.m01, source.m02,
- source.m10, source.m11, source.m12,
- 0, 0, 1
- });
- canvas.setMatrix(matrix);
+ transform.set(source);
+ updateTransformMatrix();
+ canvas.setMatrix(transformMatrix);
}
@@ -1526,6 +1616,28 @@ public void printMatrix() {
}
+ protected Matrix getMatrixImp() {
+ Matrix m = new Matrix();
+ updateTransformMatrix();
+ m.set(transformMatrix);
+ return m;
+// return canvas.getMatrix();
+ }
+
+
+ public void updateTransformMatrix() {
+ transformArray[0] = transform.m00;
+ transformArray[1] = transform.m01;
+ transformArray[2] = transform.m02;
+ transformArray[3] = transform.m10;
+ transformArray[4] = transform.m11;
+ transformArray[5] = transform.m12;
+ transformArray[6] = 0;
+ transformArray[7] = 0;
+ transformArray[8] = 1;
+ transformMatrix.setValues(transformArray);
+ }
+
//////////////////////////////////////////////////////////////
@@ -1563,28 +1675,24 @@ public void printMatrix() {
@Override
public float screenX(float x, float y) {
-// canvas.getTransform().getMatrix(transform);
-// return (float)transform[0]*x + (float)transform[2]*y + (float)transform[4];
if (screenPoint == null) {
screenPoint = new float[2];
}
screenPoint[0] = x;
screenPoint[1] = y;
- canvas.getMatrix().mapPoints(screenPoint);
+ getMatrixImp().mapPoints(screenPoint);
return screenPoint[0];
}
@Override
public float screenY(float x, float y) {
-// canvas.getTransform().getMatrix(transform);
-// return (float)transform[1]*x + (float)transform[3]*y + (float)transform[5];
if (screenPoint == null) {
screenPoint = new float[2];
}
screenPoint[0] = x;
screenPoint[1] = y;
- canvas.getMatrix().mapPoints(screenPoint);
+ getMatrixImp().mapPoints(screenPoint);
return screenPoint[1];
}
@@ -1898,6 +2006,11 @@ public void endRaw() {
@Override
public void loadPixels() {
+ if (bitmap == null) {
+ throw new RuntimeException("The pixels array is not available in this " +
+ "renderer withouth a backing bitmap");
+ }
+
if ((pixels == null) || (pixels.length != width * height)) {
pixels = new int[width * height];
}
@@ -1915,6 +2028,11 @@ public void loadPixels() {
*/
@Override
public void updatePixels() {
+ if (bitmap == null) {
+ throw new RuntimeException("The pixels array is not available in this " +
+ "renderer withouth a backing bitmap");
+ }
+
// WritableRaster raster = ((BufferedImage) image).getRaster();
// raster.setDataElements(0, 0, width, height, pixels);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
@@ -1944,6 +2062,94 @@ public void resize(int wide, int high) {
}
+ @Override
+ protected void clearState() {
+ super.clearState();
+ if (restoreFilename != null) {
+ File cacheFile = new File(restoreFilename);
+ cacheFile.delete();
+ }
+ }
+
+
+ @Override
+ protected void saveState() {
+ super.saveState();
+
+ Context context = parent.getContext();
+ if (context == null || bitmap == null || parent.getSurface().getComponent().isService()) return;
+ try {
+ // Saving current width and height to avoid restoring the screen after a screen rotation
+ restoreWidth = pixelWidth;
+ restoreHeight = pixelHeight;
+
+ int size = bitmap.getHeight() * bitmap.getRowBytes();
+ ByteBuffer restoreBitmap = ByteBuffer.allocate(size);
+ bitmap.copyPixelsToBuffer(restoreBitmap);
+
+ // Tries to use external but if not mounted, falls back on internal storage, as shown in
+ // https://developer.android.com/topic/performance/graphics/cache-bitmap#java
+ File cacheDir = Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !isExternalStorageRemovable() ?
+ context.getExternalCacheDir() : context.getCacheDir();
+ File cacheFile = new File(cacheDir + File.separator + "restore_pixels");
+ restoreFilename = cacheFile.getAbsolutePath();
+
+ FileOutputStream stream = new FileOutputStream(cacheFile);
+ ObjectOutputStream dout = new ObjectOutputStream(stream);
+ byte[] array = new byte[size];
+ restoreBitmap.rewind();
+ restoreBitmap.get(array);
+ dout.writeObject(array);
+ dout.flush();
+ stream.getFD().sync();
+ stream.close();
+ } catch (Exception ex) {
+ PGraphics.showWarning("Could not save screen contents to cache");
+ ex.printStackTrace();
+ }
+ }
+
+
+ @Override
+ protected void restoreSurface() {
+ if (changed) {
+ changed = false;
+ if (restoreFilename != null && restoreWidth == pixelWidth && restoreHeight == pixelHeight) {
+ // Set the counter to 1 so the restore bitmap is drawn in the next frame.
+ restoreCount = 1;
+ }
+ } else if (restoreCount > 0) {
+ restoreCount--;
+ if (restoreCount == 0) {
+ Context context = parent.getContext();
+ if (context == null) return;
+ try {
+ // Load cached bitmap and draw
+ File cacheFile = new File(restoreFilename);
+ FileInputStream inStream = new FileInputStream(cacheFile);
+ ObjectInputStream din = new ObjectInputStream(inStream);
+ byte[] array = (byte[]) din.readObject();
+ ByteBuffer restoreBitmap = ByteBuffer.wrap(array);
+ if (restoreBitmap.capacity() == bitmap.getHeight() * bitmap.getRowBytes()) {
+ restoreBitmap.rewind();
+ bitmap.copyPixelsFromBuffer(restoreBitmap);
+ }
+ inStream.close();
+ cacheFile.delete();
+ } catch (Exception ex) {
+ PGraphics.showWarning("Could not restore screen contents from cache");
+ ex.printStackTrace();
+ } finally {
+ restoreFilename = null;
+ restoreWidth = -1;
+ restoreHeight = -1;
+ restoredSurface = true;
+ }
+ }
+ }
+ super.restoreSurface();
+ }
+
//////////////////////////////////////////////////////////////
@@ -1955,7 +2161,7 @@ public void resize(int wide, int high) {
@Override
public int get(int x, int y) {
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0;
+ if ((bitmap == null) || (x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0;
// WritableRaster raster = ((BufferedImage) image).getRaster();
// raster.getDataElements(x, y, getset);
// return getset[0];
@@ -1990,7 +2196,7 @@ public PImage get() {
@Override
public void set(int x, int y, int argb) {
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return;
+ if ((bitmap == null) || (x < 0) || (y < 0) || (x >= width) || (y >= height)) return;
// getset[0] = argb;
// WritableRaster raster = ((BufferedImage) image).getRaster();
// raster.setDataElements(x, y, getset);
@@ -2006,36 +2212,33 @@ public void set(int x, int y, PImage src) {
throw new RuntimeException("set() not available for ALPHA images");
}
- if (src.bitmap == null) {
- // hopefully this will do the work to figure out what's on/offscreen
- // in spite of the offset and stride that's been provided
- canvas.drawBitmap(src.pixels, 0, src.width,
- x, y, src.width, src.height, false, null);
- // hasAlpha is set to false since we don't want blending.
- // however that may be incorrect if it winds up copying only the RGB
- // (without the A) portion of the pixels.
-
- } else { // src.bitmap != null
- if (src.width != src.bitmap.getWidth() ||
- src.height != src.bitmap.getHeight()) {
- src.bitmap.recycle();
- src.bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
- src.modified = true;
- }
- if (src.modified) {
- if (!src.bitmap.isMutable()) {
- src.bitmap.recycle();
- src.bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
- }
- src.bitmap.setPixels(src.pixels, 0, src.width, 0, 0, src.width, src.height);
- src.modified = false;
+ Bitmap bitmap = (Bitmap)src.getNative();
+ if (bitmap == null) {
+ bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ src.setNative(bitmap);
+ src.setModified();
+ }
+ if (src.width != bitmap.getWidth() ||
+ src.height != bitmap.getHeight()) {
+ bitmap.recycle();
+ bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ src.setNative(bitmap);
+ src.setModified();
+ }
+ if (src.isModified()) {
+ if (!bitmap.isMutable()) {
+ bitmap.recycle();
+ bitmap = Bitmap.createBitmap(src.width, src.height, Config.ARGB_8888);
+ setNative(bitmap);
}
- // set() happens in screen coordinates, so need to clear the ctm
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.setMatrix(null); // set to identity
- canvas.drawBitmap(src.bitmap, x, y, null);
- canvas.restore();
+ bitmap.setPixels(src.pixels, 0, src.width, 0, 0, src.width, src.height);
+ src.setModified(false);
}
+ // set() happens in screen coordinates, so need to clear the ctm
+ pushMatrix();
+ canvas.setMatrix(null); // set to identity
+ canvas.drawBitmap(bitmap, x, y, null);
+ popMatrix();
}
@@ -2123,11 +2326,16 @@ public void mask(PImage alpha) {
@Override
public void copy(int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh) {
+ if (bitmap == null) {
+ throw new RuntimeException("The pixels array is not available in this " +
+ "renderer withouth a backing bitmap");
+ }
+
// Bitmap bitsy = Bitmap.createBitmap(image, sx, sy, sw, sh);
// rect.set(dx, dy, dx + dw, dy + dh);
// canvas.drawBitmap(bitsy,
- rect.set(sx, sy, sx+sw, sy+sh);
- Rect src = new Rect(dx, dy, dx+dw, dy+dh);
+ rect.set(dx, dy, dx+dw, dy+dh);
+ Rect src = new Rect(sx, sy, sx+sw, sy+sh);
canvas.drawBitmap(bitmap, src, rect, null);
// if ((sw != dw) || (sh != dh)) {
diff --git a/libs/processing-core/src/main/java/processing/a2d/PShapeAndroid2D.java b/libs/processing-core/src/main/java/processing/a2d/PShapeAndroid2D.java
new file mode 100644
index 000000000..b9bb2c019
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/a2d/PShapeAndroid2D.java
@@ -0,0 +1,132 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.a2d;
+
+import android.graphics.Shader;
+
+import processing.core.PGraphics;
+import processing.core.PShapeSVG;
+import processing.data.XML;
+
+public class PShapeAndroid2D extends PShapeSVG {
+ protected Shader strokeGradientPaint;
+ protected Shader fillGradientPaint;
+
+
+ public PShapeAndroid2D(XML svg) {
+ super(svg);
+ }
+
+
+ public PShapeAndroid2D(PShapeSVG parent, XML properties, boolean parseKids) {
+ super(parent, properties, parseKids);
+ }
+
+
+ @Override
+ protected void setParent(PShapeSVG parent) {
+ super.setParent(parent);
+
+ if (parent instanceof PShapeAndroid2D) {
+ PShapeAndroid2D pj = (PShapeAndroid2D) parent;
+ fillGradientPaint = pj.fillGradientPaint;
+ strokeGradientPaint = pj.strokeGradientPaint;
+
+ } else { // parent is null or not Android2D
+ fillGradientPaint = null;
+ strokeGradientPaint = null;
+ }
+ }
+
+
+ /** Factory method for subclasses. */
+ @Override
+ protected PShapeSVG createShape(PShapeSVG parent, XML properties, boolean parseKids) {
+ return new PShapeAndroid2D(parent, properties, parseKids);
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ protected Shader calcGradientPaint(Gradient gradient) {
+ // TODO just do this with the other parsing
+ int[] colors = new int[gradient.count];
+ int opacityMask = ((int) (opacity * 255)) << 24;
+ for (int i = 0; i < gradient.count; i++) {
+ colors[i] = opacityMask | (gradient.color[i] & 0xFFFFFF);
+ }
+
+ if (gradient instanceof LinearGradient) {
+ LinearGradient grad = (LinearGradient) gradient;
+// return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2,
+// grad.offset, grad.color, grad.count,
+// opacity);
+ return new android.graphics.LinearGradient(grad.x1, grad.y1,
+ grad.x2, grad.y2,
+ colors, grad.offset,
+ Shader.TileMode.CLAMP );
+
+ } else if (gradient instanceof RadialGradient) {
+ RadialGradient grad = (RadialGradient) gradient;
+// return new RadialGradientPaint(grad.cx, grad.cy, grad.r,
+// grad.offset, grad.color, grad.count,
+// opacity);
+ return new android.graphics.RadialGradient(grad.cx, grad.cy, grad.r,
+ colors, grad.offset,
+ Shader.TileMode.CLAMP);
+ }
+ return null;
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ @Override
+ protected void styles(PGraphics g) {
+ super.styles(g);
+
+ if (g instanceof PGraphicsAndroid2D) {
+ PGraphicsAndroid2D gg = (PGraphicsAndroid2D) g;
+
+ if (strokeGradient != null) {
+// gg.strokeGradient = true;
+ if (strokeGradientPaint == null) {
+ strokeGradientPaint = calcGradientPaint(strokeGradient);
+ }
+ gg.strokePaint.setShader(strokeGradientPaint);
+ }
+ if (fillGradient != null) {
+// gg.fillGradient = true;
+ if (fillGradientPaint == null) {
+ fillGradientPaint = calcGradientPaint(fillGradient);
+ }
+ gg.fillPaint.setShader(fillGradientPaint);
+ } else {
+ // need to shut off, in case parent object has a gradient applied
+ //gg.fillGradient = false;
+ }
+ }
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/a2d/PSurfaceAndroid2D.java b/libs/processing-core/src/main/java/processing/a2d/PSurfaceAndroid2D.java
new file mode 100644
index 000000000..eb7635b87
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/a2d/PSurfaceAndroid2D.java
@@ -0,0 +1,159 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.a2d;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.service.wallpaper.WallpaperService;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import processing.android.AppComponent;
+import processing.android.PFragment;
+import processing.core.PApplet;
+import processing.core.PGraphics;
+import processing.core.PSurfaceNone;
+
+public class PSurfaceAndroid2D extends PSurfaceNone {
+
+ public PSurfaceAndroid2D() { }
+
+ public PSurfaceAndroid2D(PGraphics graphics, AppComponent component, SurfaceHolder holder) {
+ this.sketch = graphics.parent;
+ this.graphics = graphics;
+ this.component = component;
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ PFragment frag = (PFragment)component;
+ activity = frag.getActivity();
+ surfaceView = new SurfaceViewAndroid2D(activity, null);
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ wallpaper = (WallpaperService)component;
+ surfaceView = new SurfaceViewAndroid2D(wallpaper, holder);
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ watchface = (CanvasWatchFaceService)component;
+ surfaceView = null;
+ // Set as ready here, as watch faces don't have a surface view with a
+ // surfaceCreate() event to do it.
+ surfaceReady = true;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ // SurfaceView
+
+ public class SurfaceViewAndroid2D extends SurfaceView implements SurfaceHolder.Callback {
+ SurfaceHolder holder;
+
+ public SurfaceViewAndroid2D(Context context, SurfaceHolder holder) {
+ super(context);
+ this.holder = holder;
+
+// println("surface holder");
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder h = getHolder();
+ h.addCallback(this);
+// surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU); // no longer needed.
+
+// println("setting focusable, requesting focus");
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+// println("done making surface view");
+
+ surfaceReady = false; // Will be ready when the surfaceCreated() event is called
+
+ // Solves screen flickering:
+ // https://github.com/processing/processing-android/issues/570
+ setBackgroundColor(Color.argb(0, 0, 0, 0));
+ getHolder().setFormat(PixelFormat.TRANSPARENT);
+ }
+
+ @Override
+ public SurfaceHolder getHolder() {
+ if (holder == null) {
+ return super.getHolder();
+ } else {
+ return holder;
+ }
+ }
+
+ // part of SurfaceHolder.Callback
+ public void surfaceCreated(SurfaceHolder holder) {
+ surfaceReady = true;
+ if (requestedThreadStart) {
+ // Only start the thread once the surface has been created, otherwise it will not be able to draw
+ startThread();
+ }
+ if (PApplet.DEBUG) {
+ System.out.println("surfaceCreated()");
+ }
+ }
+
+
+ // part of SurfaceHolder.Callback
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (PApplet.DEBUG) {
+ System.out.println("surfaceDestroyed()");
+ }
+ }
+
+
+ // part of SurfaceHolder.Callback
+ public void surfaceChanged(SurfaceHolder holder, int format, int iwidth, int iheight) {
+ if (PApplet.DEBUG) {
+ System.out.println("SketchSurfaceView.surfaceChanged() " + iwidth + " " + iheight);
+ }
+
+ sketch.surfaceChanged();
+ sketch.setSize(iwidth, iheight);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ sketch.surfaceWindowFocusChanged(hasFocus);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return sketch.surfaceTouchEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int code, android.view.KeyEvent event) {
+ sketch.surfaceKeyDown(code, event);
+ return super.onKeyDown(code, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int code, android.view.KeyEvent event) {
+ sketch.surfaceKeyUp(code, event);
+ return super.onKeyUp(code, event);
+ }
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/android/ActivityAPI.java b/libs/processing-core/src/main/java/processing/android/ActivityAPI.java
new file mode 100644
index 000000000..751ec2048
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/ActivityAPI.java
@@ -0,0 +1,65 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.app.FragmentManager;
+
+
+// Methods that should be implemented in PApplet to maintain backward
+// compatibility with (some) functionality available from Activity/Fragment
+public interface ActivityAPI {
+ // Lifecycle events
+ public void onCreate(Bundle savedInstanceState);
+ public void onDestroy();
+ public void onStart();
+ public void onStop();
+ public void onPause();
+ public void onResume();
+
+ // Activity and intent events
+ public void onActivityResult(int requestCode, int resultCode, Intent data);
+ public void onNewIntent(Intent intent);
+
+ // Menu API
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater);
+ public boolean onOptionsItemSelected(MenuItem item);
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo);
+ public boolean onContextItemSelected(MenuItem item);
+ public void setHasOptionsMenu(boolean hasMenu);
+
+ // IO events
+ public void onBackPressed();
+
+ // Activity management
+ public FragmentManager getFragmentManager();
+ public Window getWindow();
+}
diff --git a/libs/processing-core/src/main/java/processing/android/AppComponent.java b/libs/processing-core/src/main/java/processing/android/AppComponent.java
new file mode 100644
index 000000000..396e01126
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/AppComponent.java
@@ -0,0 +1,52 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.content.Intent;
+
+import processing.core.PApplet;
+import processing.core.PConstants;
+
+abstract public interface AppComponent extends PConstants {
+ static public final int FRAGMENT = 0;
+ static public final int WALLPAPER = 1;
+ static public final int WATCHFACE = 2;
+
+ public void initDimensions();
+ public int getDisplayWidth();
+ public int getDisplayHeight();
+ public float getDisplayDensity();
+ public int getKind();
+ public void setSketch(PApplet sketch);
+ public PApplet getSketch();
+
+ public boolean isService();
+ public ServiceEngine getEngine();
+
+ public void startActivity(Intent intent);
+
+ public void requestDraw();
+ public boolean canDraw();
+
+ public void dispose();
+}
diff --git a/libs/processing-core/src/main/java/processing/android/CompatUtils.java b/libs/processing-core/src/main/java/processing/android/CompatUtils.java
new file mode 100644
index 000000000..e218e860b
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/CompatUtils.java
@@ -0,0 +1,112 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2017-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.View;
+import android.graphics.Point;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Compatibility utilities that work across versions of Android. Even though
+ * the mode sets API level 17 (Android 4.2) as the minimum target, because the
+ * core library could be used from another IDE and lower targets, then this
+ * compatibility methods are still needed.
+ */
+public class CompatUtils {
+ // Start at 15,000,000, taking into account the comment from Singed
+ // http://stackoverflow.com/a/39307421
+ private static final AtomicInteger nextId = new AtomicInteger(15000000);
+
+
+ /**
+ * This method retrieves the "real" display metrics and size, without
+ * subtracting any window decor or applying any compatibility scale factors.
+ * @param display the Display object
+ * @param metrics the metrics to retrieve
+ * @param size the size to retrieve
+ */
+ static public void getDisplayParams(Display display,
+ DisplayMetrics metrics, Point size) {
+ if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT) {
+ display.getRealMetrics(metrics);
+ display.getRealSize(size);
+ } if (Build.VERSION_CODES.ICE_CREAM_SANDWICH <= Build.VERSION.SDK_INT) {
+ display.getMetrics(metrics);
+ // Use undocumented methods getRawWidth, getRawHeight
+ try {
+ size.x = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
+ size.y = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
+ } catch (Exception e) {
+ display.getSize(size);
+ }
+ } else {
+ display.getMetrics(metrics);
+ display.getSize(size);
+ }
+ }
+
+
+ /**
+ * This method generates a unique View ID's. Handles the lack of
+ * View.generateViewId() in Android versions lower than 17, using a technique
+ * based on fantouch's code at http://stackoverflow.com/a/21000252
+ * @return view ID
+ */
+ @SuppressLint("NewApi")
+ static public int getUniqueViewId() {
+ if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT) {
+ return View.generateViewId();
+ } else {
+ for (;;) {
+ final int result = nextId.get();
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+ if (nextId.compareAndSet(result, newValue)) {
+ return result;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This method returns the UTF-8 charset
+ * @return UTF-8 charset
+ */
+ @SuppressLint("NewApi")
+ static public Charset getCharsetUTF8() {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) {
+ return StandardCharsets.UTF_8;
+ } else {
+ return Charset.forName("UTF-8");
+ }
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/android/PFragment.java b/libs/processing-core/src/main/java/processing/android/PFragment.java
new file mode 100644
index 000000000..8fe80d72c
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/PFragment.java
@@ -0,0 +1,261 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.util.DisplayMetrics;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.ContextMenu.ContextMenuInfo;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import processing.core.PApplet;
+
+public class PFragment extends Fragment implements AppComponent {
+ private DisplayMetrics metrics;
+ private Point size;
+ private PApplet sketch;
+ private @LayoutRes int layout = -1;
+
+
+ public PFragment() {
+ super();
+ }
+
+
+ public PFragment(PApplet sketch) {
+ super();
+ setSketch(sketch);
+ }
+
+
+ public void initDimensions() {
+ metrics = new DisplayMetrics();
+ size = new Point();
+ WindowManager wm = getActivity().getWindowManager();
+ Display display = wm.getDefaultDisplay();
+ CompatUtils.getDisplayParams(display, metrics, size);
+ }
+
+
+ public int getDisplayWidth() {
+ return size.x;
+ }
+
+
+ public int getDisplayHeight() {
+ return size.y;
+ }
+
+
+ public float getDisplayDensity() {
+ return metrics.density;
+ }
+
+
+ public int getKind() {
+ return FRAGMENT;
+ }
+
+
+ public void setSketch(PApplet sketch) {
+ this.sketch = sketch;
+ if (layout != -1) {
+ sketch.parentLayout = layout;
+ }
+ }
+
+
+ public PApplet getSketch() {
+ return sketch;
+ }
+
+
+ public void setLayout(@LayoutRes int layout, @IdRes int id, FragmentActivity activity) {
+ this.layout = layout;
+ if (sketch != null) {
+ sketch.parentLayout = layout;
+ }
+ FragmentManager manager = activity.getSupportFragmentManager();
+ FragmentTransaction transaction = manager.beginTransaction();
+ transaction.add(id, this);
+ transaction.commit();
+ }
+
+
+ public void setView(View view, FragmentActivity activity) {
+ FragmentManager manager = activity.getSupportFragmentManager();
+ FragmentTransaction transaction = manager.beginTransaction();
+ transaction.add(view.getId(), this);
+ transaction.commit();
+ }
+
+
+ public boolean isService() {
+ return false;
+ }
+
+
+ public ServiceEngine getEngine() {
+ return null;
+ }
+
+
+ public void dispose() {
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ if (sketch != null) {
+ sketch.initSurface(inflater, container, savedInstanceState, this, null);
+
+ // For compatibility with older sketches that run some hardware initialization
+ // inside onCreate(), don't call from Fragment.onCreate() because the surface
+ // will not be yet ready, and so the reference to the activity and other
+ // system variables will be null. In any case, onCreateView() is called
+ // immediately after onCreate():
+ // https://developer.android.com/reference/android/app/Fragment.html#Lifecycle
+ sketch.onCreate(savedInstanceState);
+
+ return sketch.getSurface().getRootView();
+ } else {
+ return null;
+ }
+ }
+
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (sketch != null) {
+ sketch.onStart();
+ }
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (sketch != null) {
+ sketch.onResume();
+ }
+ }
+
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (sketch != null) {
+ sketch.onPause();
+ }
+ }
+
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (sketch != null) {
+ sketch.onStop();
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (sketch != null) {
+ sketch.onDestroy();
+ }
+ }
+
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (sketch != null) sketch.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (sketch != null) sketch.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (sketch != null) return sketch.onOptionsItemSelected(item);
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ if (sketch != null) sketch.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ if (sketch != null) return sketch.onContextItemSelected(item);
+ return super.onContextItemSelected(item);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (PApplet.DEBUG) System.out.println("configuration changed: " + newConfig);
+ super.onConfigurationChanged(newConfig);
+ }
+
+
+ public void setOrientation(int which) {
+ if (which == PORTRAIT) {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ } else if (which == LANDSCAPE) {
+ getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+ }
+
+
+ public void requestDraw() {
+ }
+
+
+ public boolean canDraw() {
+ return sketch != null && sketch.isLooping();
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/android/PWallpaper.java b/libs/processing-core/src/main/java/processing/android/PWallpaper.java
new file mode 100644
index 000000000..850339100
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/PWallpaper.java
@@ -0,0 +1,302 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.service.wallpaper.WallpaperService;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.WindowManager;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import processing.core.PApplet;
+
+public class PWallpaper extends WallpaperService implements AppComponent {
+ private Point size;
+ private DisplayMetrics metrics;
+ private WallpaperEngine engine;
+
+
+ public void initDimensions() {
+ metrics = new DisplayMetrics();
+ size = new Point();
+ WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ CompatUtils.getDisplayParams(display, metrics, size);
+ }
+
+
+ public int getDisplayWidth() {
+ return size.x;
+ }
+
+
+ public int getDisplayHeight() {
+ return size.y;
+ }
+
+
+ public float getDisplayDensity() {
+ return metrics.density;
+ }
+
+
+ public int getKind() {
+ return WALLPAPER;
+ }
+
+
+ public PApplet createSketch() {
+ return new PApplet();
+ }
+
+
+ public void setSketch(PApplet sketch) {
+ engine.sketch = sketch;
+ }
+
+
+ public PApplet getSketch() {
+ return engine.sketch;
+ }
+
+
+ public boolean isService() {
+ return true;
+ }
+
+
+ public ServiceEngine getEngine() {
+ return engine;
+ }
+
+
+ public void requestDraw() {
+ }
+
+
+ public boolean canDraw() {
+ return true;
+ }
+
+
+ public void dispose() {
+ }
+
+
+ public void requestPermissions() {
+ }
+
+
+ @Override
+ public Engine onCreateEngine() {
+ engine = new WallpaperEngine();
+ return engine;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ if (engine != null){
+ //engine.sketch = null;
+ engine.onDestroy();
+ }
+ }
+
+
+ public class WallpaperEngine extends Engine implements ServiceEngine {
+ PApplet sketch;
+ private float xOffset, xOffsetStep;
+ private float yOffset, yOffsetStep;
+ private int xPixelOffset, yPixelOffset;
+
+
+ @Override
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ super.onCreate(surfaceHolder);
+ sketch = createSketch();
+ sketch.initSurface(PWallpaper.this, getSurfaceHolder());
+ if (isPreview()) requestPermissions();
+ setTouchEventsEnabled(true);
+ }
+
+
+ @Override
+ public void onSurfaceCreated(SurfaceHolder surfaceHolder) {
+ super.onSurfaceCreated(surfaceHolder);
+ }
+
+
+ @Override
+ public void onSurfaceChanged(final SurfaceHolder holder, final int format,
+ final int width, final int height) {
+ // When the surface of a live wallpaper changes (eg: after a screen rotation) the same sketch
+ // continues to run (unlike the case of regular apps, where its re-created) so we need to
+ // force a reset of the renderer so the backing FBOs (in the case of the OpenGL renderers)
+ // get reinitalized with the correct size.
+ sketch.g.reset();
+ super.onSurfaceChanged(holder, format, width, height);
+ }
+
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ if (sketch != null) {
+ if (visible) {
+ sketch.onResume();
+ } else {
+ sketch.onPause();
+ }
+ }
+ super.onVisibilityChanged(visible);
+ }
+
+
+ /*
+ * Store the position of the touch event so we can use it for drawing
+ * later
+ */
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ if (sketch != null) {
+ sketch.surfaceTouchEvent(event);
+ }
+ }
+
+
+ @Override
+ public void onOffsetsChanged(float xOffset, float yOffset,
+ float xOffsetStep, float yOffsetStep,
+ int xPixelOffset, int yPixelOffset) {
+
+ if (sketch != null) {
+ this.xOffset = xOffset;
+ this.yOffset = yOffset;
+ this.xOffsetStep = xOffsetStep;
+ this.yOffsetStep = yOffsetStep;
+ this.xPixelOffset = xPixelOffset;
+ this.yPixelOffset = yPixelOffset;
+ }
+ }
+
+
+ @Override
+ public void onSurfaceDestroyed(SurfaceHolder holder) {
+ // This is called immediately before a surface is being destroyed.
+ // After returning from this call, you should no longer try to access this
+ // surface. If you have a rendering thread that directly accesses the
+ // surface, you must ensure that thread is no longer touching the Surface
+ // before returning from this function.
+ super.onSurfaceDestroyed(holder);
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (sketch != null) {
+ sketch.onDestroy();
+ }
+ }
+
+
+ @Override
+ public float getXOffset() {
+ return xOffset;
+ }
+
+
+ @Override
+ public float getYOffset() {
+ return yOffset;
+ }
+
+
+ @Override
+ public float getXOffsetStep() {
+ return xOffsetStep;
+ }
+
+
+ @Override
+ public float getYOffsetStep() {
+ return yOffsetStep;
+ }
+
+
+ @Override
+ public int getXPixelOffset() {
+ return xPixelOffset;
+ }
+
+
+ @Override
+ public int getYPixelOffset() {
+ return yPixelOffset;
+ }
+
+
+ @Override
+ public boolean isInAmbientMode() {
+ return false;
+ }
+
+
+ @Override
+ public boolean isRound() {
+ return false;
+ }
+
+
+ @Override
+ public Rect getInsets() {
+ return null;
+ }
+
+
+ @Override
+ public boolean useLowBitAmbient() {
+ return false;
+ }
+
+
+ @Override
+ public boolean requireBurnInProtection() {
+ return false;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[],
+ int[] grantResults) {
+ if (sketch != null) {
+ sketch.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/android/PWatchFaceCanvas.java b/libs/processing-core/src/main/java/processing/android/PWatchFaceCanvas.java
new file mode 100644
index 000000000..ee4222dfc
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/PWatchFaceCanvas.java
@@ -0,0 +1,368 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import java.lang.reflect.Method;
+
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.graphics.Rect;
+import android.support.wearable.complications.ComplicationData;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import processing.a2d.PGraphicsAndroid2D;
+import processing.core.PApplet;
+
+@TargetApi(21)
+public class PWatchFaceCanvas extends CanvasWatchFaceService implements AppComponent {
+ private Point size;
+ private DisplayMetrics metrics;
+ private CanvasEngine engine;
+
+ public void initDimensions() {
+ metrics = new DisplayMetrics();
+ size = new Point();
+ WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ CompatUtils.getDisplayParams(display, metrics, size);
+ }
+
+
+ public int getDisplayWidth() {
+ return size.x;
+ }
+
+
+ public int getDisplayHeight() {
+ return size.y;
+ }
+
+
+ public float getDisplayDensity() {
+ return metrics.density;
+ }
+
+
+ public int getKind() {
+ return WATCHFACE;
+ }
+
+
+ public PApplet createSketch() {
+ return new PApplet();
+ }
+
+
+ public void setSketch(PApplet sketch) {
+ engine.sketch = sketch;
+ }
+
+
+ public PApplet getSketch() {
+ return engine.sketch;
+ }
+
+
+ public boolean isService() {
+ return true;
+ }
+
+
+ public ServiceEngine getEngine() {
+ return engine;
+ }
+
+
+ public void requestDraw() {
+ if (engine != null) engine.invalidateIfNecessary();
+ }
+
+
+ public boolean canDraw() {
+ // The rendering loop should never call handleDraw() directly,
+ // it only needs to invalidate the screen
+ return false;
+ }
+
+
+ public void dispose() {
+ }
+
+
+ public void requestPermissions() {
+ }
+
+
+ @Override
+ public Engine onCreateEngine() {
+ engine = new CanvasEngine();
+ return engine;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (engine != null) engine.onDestroy();
+ }
+
+
+ private class CanvasEngine extends CanvasWatchFaceService.Engine implements ServiceEngine {
+ private PApplet sketch;
+ private Method compUpdatedMethod;
+ private Method tapCommandMethod;
+ private boolean isRound = false;
+ private Rect insets = new Rect();
+ private boolean lowBitAmbient = false;
+ private boolean burnInProtection = false;
+
+ @Override
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ super.onCreate(surfaceHolder);
+ setWatchFaceStyle(new WatchFaceStyle.Builder(PWatchFaceCanvas.this)
+ .setAcceptsTapEvents(true)
+ .build());
+ sketch = createSketch();
+ PGraphicsAndroid2D.useBitmap = false;
+ sketch.initSurface(PWatchFaceCanvas.this, null);
+ initTapEvents();
+ initComplications();
+ requestPermissions();
+ }
+
+
+ private void initTapEvents() {
+ try {
+ tapCommandMethod = sketch.getClass().getMethod("onTapCommand",
+ new Class[] {int.class, int.class, int.class, long.class});
+ } catch (Exception e) {
+ tapCommandMethod = null;
+ }
+ }
+
+
+ private void initComplications() {
+ try {
+ compUpdatedMethod = sketch.getClass().getMethod("onComplicationDataUpdate",
+ new Class[] {int.class, ComplicationData.class});
+ } catch (Exception e) {
+ compUpdatedMethod = null;
+ }
+ }
+
+
+ private void invalidateIfNecessary() {
+ if (isVisible() && !isInAmbientMode()) {
+ invalidate();
+ }
+ }
+
+
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode) {
+ super.onAmbientModeChanged(inAmbientMode);
+ invalidateIfNecessary();
+ // call new event handlers in sketch (?)
+ }
+
+
+ @Override
+ public void onPropertiesChanged(Bundle properties) {
+ super.onPropertiesChanged(properties);
+ lowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
+ burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
+ }
+
+
+ @Override
+ public void onApplyWindowInsets(WindowInsets insets) {
+ super.onApplyWindowInsets(insets);
+ isRound = insets.isRound();
+ this.insets.set(insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom());
+ }
+
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ super.onVisibilityChanged(visible);
+ if (sketch != null) {
+ if (visible) {
+ sketch.onResume();
+ } else {
+ sketch.onPause();
+ }
+ }
+ }
+
+
+ @Override
+ public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ super.onSurfaceChanged(holder, format, width, height);
+ if (sketch != null) {
+ sketch.surfaceChanged();
+ sketch.setSize(width, height);
+ }
+ }
+
+
+ @Override
+ public void onPeekCardPositionUpdate(Rect rect) { }
+
+
+ @Override
+ public void onTimeTick() {
+ invalidate();
+ }
+
+
+ @Override
+ public void onDraw(Canvas canvas, Rect bounds) {
+ super.onDraw(canvas, bounds);
+ if (sketch != null) {
+ PGraphicsAndroid2D g2 = (PGraphicsAndroid2D)sketch.g;
+ g2.canvas = canvas;
+ sketch.handleDraw();
+ }
+ }
+
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ if (sketch != null) sketch.surfaceTouchEvent(event);
+ }
+
+
+ @Override
+ public void onTapCommand(@TapType int tapType, int x, int y, long eventTime) {
+ if (tapCommandMethod != null) {
+ try {
+ tapCommandMethod.invoke(tapType, x, y, eventTime);
+ } catch (Exception e) { }
+ invalidate();
+ }
+ }
+
+
+ @Override
+ public void onComplicationDataUpdate(int complicationId,
+ ComplicationData complicationData) {
+ if (compUpdatedMethod != null) {
+ try {
+ compUpdatedMethod.invoke(complicationId, complicationData);
+ } catch (Exception e) { }
+ invalidate();
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (sketch != null) {
+ sketch.onDestroy();
+ }
+ }
+
+
+ @Override
+ public float getXOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public float getYOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public float getXOffsetStep() {
+ return 0;
+ }
+
+
+ @Override
+ public float getYOffsetStep() {
+ return 0;
+ }
+
+
+ @Override
+ public int getXPixelOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public int getYPixelOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public boolean isRound() {
+ return isRound;
+ }
+
+
+ @Override
+ public Rect getInsets() {
+ return insets;
+ }
+
+
+ @Override
+ public boolean useLowBitAmbient() {
+ return lowBitAmbient;
+ }
+
+
+ @Override
+ public boolean requireBurnInProtection() {
+ return burnInProtection;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[],
+ int[] grantResults) {
+ if (sketch != null) {
+ sketch.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/android/PWatchFaceGLES.java b/libs/processing-core/src/main/java/processing/android/PWatchFaceGLES.java
new file mode 100644
index 000000000..e62ab7eb4
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/PWatchFaceGLES.java
@@ -0,0 +1,393 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.annotation.TargetApi;
+import android.graphics.Point;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLDisplay;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.WindowInsets;
+import android.support.wearable.complications.ComplicationData;
+import android.support.wearable.watchface.Gles2WatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.WindowManager;
+import android.graphics.Rect;
+
+import java.lang.reflect.Method;
+
+import processing.core.PApplet;
+
+@TargetApi(21)
+public class PWatchFaceGLES extends Gles2WatchFaceService implements AppComponent {
+ private static final int[] CONFIG_ATTRIB_LIST = new int[]{
+ EGL14.EGL_RENDERABLE_TYPE, 4,
+ EGL14.EGL_RED_SIZE, 8,
+ EGL14.EGL_GREEN_SIZE, 8,
+ EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_ALPHA_SIZE, 8,
+ EGL14.EGL_DEPTH_SIZE, 16, // this was missing
+ EGL14.EGL_NONE};
+
+ private Point size;
+ private DisplayMetrics metrics;
+ private GLES2Engine engine;
+
+
+ public void initDimensions() {
+ metrics = new DisplayMetrics();
+ size = new Point();
+ WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ CompatUtils.getDisplayParams(display, metrics, size);
+ }
+
+
+ public int getDisplayWidth() {
+ return size.x;
+ }
+
+
+ public int getDisplayHeight() {
+ return size.y;
+ }
+
+
+ public float getDisplayDensity() {
+ return metrics.density;
+ }
+
+
+ public int getKind() {
+ return WATCHFACE;
+ }
+
+
+ public PApplet createSketch() {
+ return new PApplet();
+ }
+
+
+ public void setSketch(PApplet sketch) {
+ engine.sketch = sketch;
+ }
+
+
+ public PApplet getSketch() {
+ return engine.sketch;
+ }
+
+
+ public boolean isService() {
+ return true;
+ }
+
+
+ public ServiceEngine getEngine() {
+ return engine;
+ }
+
+
+ public void requestDraw() {
+ if (engine != null) engine.invalidateIfNecessary();
+ }
+
+
+ public boolean canDraw() {
+ // The rendering loop should never call handleDraw() directly, it only needs to invalidate the
+ // screen
+ return false;
+ }
+
+
+ public void dispose() {
+ }
+
+
+ public void requestPermissions() {
+ }
+
+
+ @Override
+ public Engine onCreateEngine() {
+ engine = new GLES2Engine();
+ return engine;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (engine != null) engine.onDestroy();
+ }
+
+
+ private class GLES2Engine extends Gles2WatchFaceService.Engine implements ServiceEngine {
+ private PApplet sketch;
+ private Method compUpdatedMethod;
+ private Method tapCommandMethod;
+ private boolean isRound = false;
+ private Rect insets = new Rect();
+ private boolean lowBitAmbient = false;
+ private boolean burnInProtection = false;
+
+ @Override
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ super.onCreate(surfaceHolder);
+ setWatchFaceStyle(new WatchFaceStyle.Builder(PWatchFaceGLES.this)
+ .setAcceptsTapEvents(true)
+ .build());
+ sketch = createSketch();
+ sketch.initSurface(PWatchFaceGLES.this, null);
+ initTapEvents();
+ initComplications();
+ requestPermissions();
+ }
+
+
+ public EGLConfig chooseEglConfig(EGLDisplay eglDisplay) {
+ int[] numEglConfigs = new int[1];
+ EGLConfig[] eglConfigs = new EGLConfig[1];
+ if (!EGL14.eglChooseConfig(eglDisplay, CONFIG_ATTRIB_LIST, 0, eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
+ throw new RuntimeException("eglChooseConfig failed");
+ } else if (numEglConfigs[0] == 0) {
+ throw new RuntimeException("no matching EGL configs");
+ } else {
+ return eglConfigs[0];
+ }
+ }
+
+ @Override
+ public void onGlContextCreated() {
+ super.onGlContextCreated();
+ }
+
+
+ @Override
+ public void onGlSurfaceCreated(int width, int height) {
+ super.onGlSurfaceCreated(width, height);
+ if (sketch != null) {
+ sketch.surfaceChanged();
+ sketch.setSize(width, height);
+ }
+ }
+
+
+ private void initTapEvents() {
+ try {
+ tapCommandMethod = sketch.getClass().getMethod("onTapCommand",
+ new Class[] {int.class, int.class, int.class, long.class});
+ } catch (Exception e) {
+ tapCommandMethod = null;
+ }
+ }
+
+
+ private void initComplications() {
+ try {
+ compUpdatedMethod = sketch.getClass().getMethod("onComplicationDataUpdate",
+ new Class[] {int.class, ComplicationData.class});
+ } catch (Exception e) {
+ compUpdatedMethod = null;
+ }
+ }
+
+
+ private void invalidateIfNecessary() {
+ if (isVisible() && !isInAmbientMode()) {
+ invalidate();
+ }
+ }
+
+
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode) {
+ super.onAmbientModeChanged(inAmbientMode);
+ invalidateIfNecessary();
+ // call new event handlers in sketch (?)
+ }
+
+
+ @Override
+ public void onPropertiesChanged(Bundle properties) {
+ super.onPropertiesChanged(properties);
+ lowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
+ burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
+ }
+
+
+ @Override
+ public void onApplyWindowInsets(WindowInsets insets) {
+ super.onApplyWindowInsets(insets);
+ this.insets.set(insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom());
+ }
+
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ super.onVisibilityChanged(visible);
+ if (sketch != null) {
+ if (visible) {
+ sketch.onResume();
+ } else {
+ sketch.onPause();
+ }
+ }
+ }
+
+
+ @Override
+ public void onPeekCardPositionUpdate(Rect rect) { }
+
+
+ @Override
+ public void onTimeTick() {
+ invalidate();
+ }
+
+
+ @Override
+ public void onDraw() {
+ super.onDraw();
+ if (sketch != null) sketch.handleDraw();
+ }
+
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ if (sketch != null) sketch.surfaceTouchEvent(event);
+ }
+
+
+ @Override
+ public void onTapCommand(@TapType int tapType, int x, int y, long eventTime) {
+ if (tapCommandMethod != null) {
+ try {
+ tapCommandMethod.invoke(tapType, x, y, eventTime);
+ } catch (Exception e) { }
+ invalidate();
+ }
+ }
+
+
+ @Override
+ public void onComplicationDataUpdate(int complicationId,
+ ComplicationData complicationData) {
+ if (compUpdatedMethod != null) {
+ try {
+ compUpdatedMethod.invoke(complicationId, complicationData);
+ } catch (Exception e) {
+ }
+ invalidate();
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (sketch != null) {
+ sketch.onDestroy();
+ }
+ }
+
+
+ @Override
+ public float getXOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public float getYOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public float getXOffsetStep() {
+ return 0;
+ }
+
+
+ @Override
+ public float getYOffsetStep() {
+ return 0;
+ }
+
+
+ @Override
+ public int getXPixelOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public int getYPixelOffset() {
+ return 0;
+ }
+
+
+ @Override
+ public boolean isRound() {
+ return isRound;
+ }
+
+
+ @Override
+ public Rect getInsets() {
+ return insets;
+ }
+
+
+ @Override
+ public boolean useLowBitAmbient() {
+ return lowBitAmbient;
+ }
+
+
+ @Override
+ public boolean requireBurnInProtection() {
+ return burnInProtection;
+ }
+
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[],
+ int[] grantResults) {
+ if (sketch != null) {
+ sketch.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/android/PermissionRequestor.java b/libs/processing-core/src/main/java/processing/android/PermissionRequestor.java
new file mode 100644
index 000000000..deac0d852
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/PermissionRequestor.java
@@ -0,0 +1,61 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2017-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import android.support.v4.os.ResultReceiver;
+import androidx.core.app.ActivityCompat;
+import androidx.annotation.RestrictTo;
+
+// A simple utility activity to request permissions in a service.
+public class PermissionRequestor extends Activity {
+ public static final String KEY_RESULT_RECEIVER = "resultReceiver";
+ public static final String KEY_PERMISSIONS = "permissions";
+ public static final String KEY_GRANT_RESULTS = "grantResults";
+ public static final String KEY_REQUEST_CODE = "requestCode";
+
+ ResultReceiver resultReceiver;
+ String[] permissions;
+ int requestCode;
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ resultReceiver = this.getIntent().getParcelableExtra(KEY_RESULT_RECEIVER);
+ permissions = this.getIntent().getStringArrayExtra(KEY_PERMISSIONS);
+ requestCode = this.getIntent().getIntExtra(KEY_REQUEST_CODE, 0);
+ ActivityCompat.requestPermissions(this, permissions, requestCode);
+ }
+
+ @Override
+ @SuppressWarnings("RestrictedApi")
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ Bundle resultData = new Bundle();
+ resultData.putStringArray(KEY_PERMISSIONS, permissions);
+ resultData.putIntArray(KEY_GRANT_RESULTS, grantResults);
+ resultReceiver.send(requestCode, resultData);
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/libs/processing-core/src/main/java/processing/android/ServiceEngine.java b/libs/processing-core/src/main/java/processing/android/ServiceEngine.java
new file mode 100644
index 000000000..11b1164f0
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/android/ServiceEngine.java
@@ -0,0 +1,50 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2017-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.android;
+
+import android.graphics.Rect;
+
+import processing.core.PConstants;
+
+public interface ServiceEngine extends PConstants {
+ // wallpapers
+ public boolean isPreview();
+ public float getXOffset();
+ public float getYOffset();
+ public float getXOffsetStep();
+ public float getYOffsetStep();
+ public int getXPixelOffset();
+ public int getYPixelOffset();
+
+ // wear
+ public boolean isInAmbientMode();
+ public boolean isRound();
+ public Rect getInsets();
+ public boolean useLowBitAmbient();
+ public boolean requireBurnInProtection();
+
+ // Service permissions
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[],
+ int[] grantResults);
+}
diff --git a/core/src/processing/core/PApplet.java b/libs/processing-core/src/main/java/processing/core/PApplet.java
similarity index 53%
rename from core/src/processing/core/PApplet.java
rename to libs/processing-core/src/main/java/processing/core/PApplet.java
index f3e1cfa54..2b55cf67c 100644
--- a/core/src/processing/core/PApplet.java
+++ b/libs/processing-core/src/main/java/processing/core/PApplet.java
@@ -3,6 +3,7 @@
/*
Part of the Processing project - http://processing.org
+ Copyright (c) 2012-21 The Processing Foundation
Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
@@ -23,55 +24,111 @@
package processing.core;
-import java.io.*;
-import java.lang.reflect.*;
-import java.net.*;
-import java.text.NumberFormat;
-import java.util.*;
-import java.util.regex.*;
-import java.util.zip.*;
-
-import android.app.*;
-import android.content.*;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ConfigurationInfo;
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.graphics.*;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.Typeface;
import android.net.Uri;
-import android.opengl.GLSurfaceView;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.text.format.Time;
-import android.util.*;
+import android.os.Looper;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewGroup.LayoutParams;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
-import android.view.WindowManager;
-import android.widget.*;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.annotation.LayoutRes;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import processing.a2d.PGraphicsAndroid2D;
+import processing.android.ActivityAPI;
+import processing.android.AppComponent;
+import processing.android.CompatUtils;
+import processing.data.JSONArray;
+import processing.data.JSONObject;
+import processing.data.StringList;
+import processing.data.Table;
+import processing.data.XML;
+import processing.event.Event;
+import processing.event.KeyEvent;
+import processing.event.MouseEvent;
+import processing.event.TouchEvent;
+import processing.opengl.PGL;
+import processing.opengl.PGraphics2D;
+import processing.opengl.PGraphics3D;
+import processing.opengl.PShader;
+
+public class PApplet extends Object implements ActivityAPI, PConstants {
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpEntity;
+ static final public boolean DEBUG = false;
+// static final public boolean DEBUG = true;
-import processing.data.*;
-import processing.event.*;
-import processing.opengl.*;
+ // Convenience public constant holding the SDK version, akin to platform in Java mode
+ static final public int SDK = Build.VERSION.SDK_INT;
+ //static final public int SDK = Build.VERSION_CODES.ICE_CREAM_SANDWICH; // Forcing older SDK for testing
-public class PApplet extends Activity implements PConstants, Runnable {
- /** The PGraphics renderer associated with this PApplet */
- public PGraphics g;
+ /**
+ * The surface this sketch draws to.
+ */
+ protected PSurface surface;
-// static final public boolean DEBUG = true;
- static final public boolean DEBUG = false;
+ /**
+ * The view group containing the surface view of the PApplet.
+ */
+ public @LayoutRes int parentLayout = -1;
- /** The frame containing this applet (if any) */
-// public Frame frame;
+ /** The PGraphics renderer associated with this PApplet */
+ public PGraphics g;
/**
* The screen size when the sketch was started. This is initialized inside
@@ -105,29 +162,8 @@ public class PApplet extends Activity implements PConstants, Runnable {
// static final boolean THREAD_DEBUG = false;
/** Default width and height for applet when not specified */
-// static public final int DEFAULT_WIDTH = 100;
-// static public final int DEFAULT_HEIGHT = 100;
-
- /**
- * Minimum dimensions for the window holding an applet.
- * This varies between platforms, Mac OS X 10.3 can do any height
- * but requires at least 128 pixels width. Windows XP has another
- * set of limitations. And for all I know, Linux probably lets you
- * make windows with negative sizes.
- */
-// static public final int MIN_WINDOW_WIDTH = 128;
-// static public final int MIN_WINDOW_HEIGHT = 128;
-
- /**
- * Exception thrown when size() is called the first time.
- *
- * This is used internally so that setup() is forced to run twice
- * when the renderer is changed. This is the only way for us to handle
- * invoking the new renderer while also in the midst of rendering.
- */
- static public class RendererChangeException extends RuntimeException { }
-
- protected boolean surfaceReady;
+ static public final int DEFAULT_WIDTH = -1;
+ static public final int DEFAULT_HEIGHT = -1;
/**
* Set true when the surface dimensions have changed, so that the PGraphics
@@ -135,16 +171,6 @@ static public class RendererChangeException extends RuntimeException { }
*/
protected boolean surfaceChanged;
- /**
- * true if no size() command has been executed. This is used to wait until
- * a size has been set before placing in the window and showing it.
- */
-// public boolean defaultSize;
-
-// volatile boolean resizeRequest;
-// volatile int resizeWidth;
-// volatile int resizeHeight;
-
/**
* Pixel buffer from this applet's PGraphics.
*
@@ -154,13 +180,28 @@ static public class RendererChangeException extends RuntimeException { }
public int[] pixels;
/** width of this applet's associated PGraphics */
- public int width;
+ public int width = DEFAULT_WIDTH;
/** height of this applet's associated PGraphics */
- public int height;
+ public int height = DEFAULT_HEIGHT;
- // can't call this because causes an ex, but could set elsewhere
- //final float screenDensity = getResources().getDisplayMetrics().density;
+ /** The logical density of the display from getDisplayMetrics().density
+ * According to Android's documentation:
+ * This is a scaling factor for the Density Independent Pixel unit,
+ * where one DIP is one pixel on an approximately 160 dpi screen
+ * (for example a 240x320, 1.5"x2" screen), providing the baseline of the
+ * system's display. Thus on a 160dpi screen this density value will be 1;
+ * on a 120 dpi screen it would be .75; etc.
+ */
+ public float displayDensity = 1;
+
+ // For future use
+ public int pixelDensity = 1;
+ public int pixelWidth;
+ public int pixelHeight;
+
+ ///////////////////////////////////////////////////////////////
+ // Mouse events
/** absolute x position of input on screen */
public int mouseX;
@@ -168,37 +209,6 @@ static public class RendererChangeException extends RuntimeException { }
/** absolute x position of input on screen */
public int mouseY;
-// /** current x position of motion (relative to start of motion) */
-// public float motionX;
-//
-// /** current y position of the mouse (relative to start of motion) */
-// public float motionY;
-//
-// /** Last reported pressure of the current motion event */
-// public float motionPressure;
-//
-// /** Last reported positions and pressures for all pointers */
-// protected int numPointers;
-// protected int pnumPointers;
-//
-// protected float[] ppointersX = {0};
-// protected float[] ppointersY = {0};
-// protected float[] ppointersPressure = {0};
-//
-// protected float[] pointersX = {0};
-// protected float[] pointersY = {0};
-// protected float[] pointersPressure = {0};
-//
-// protected int downMillis;
-// protected float downX, downY;
-// protected boolean onePointerGesture = false;
-// protected boolean twoPointerGesture = true;
-//
-// protected final int MIN_SWIPE_LENGTH = 150; // Minimum length (in pixels) of a swipe event
-// protected final int MAX_SWIPE_DURATION = 2000; // Maximum duration (in millis) of a swipe event
-// protected final int MAX_TAP_DISP = 20; // Maximum displacement (in pixels) during a tap event
-// protected final int MAX_TAP_DURATION = 1000; // Maximum duration (in millis) of a tap event
-
/**
* Previous x/y position of the mouse. This will be a different value
@@ -210,14 +220,24 @@ static public class RendererChangeException extends RuntimeException { }
* you're gonna run into trouble.
*/
public int pmouseX, pmouseY;
-// public float pmotionX, pmotionY;
+
+ public int mouseButton;
+
+ public boolean mousePressed;
+
+
+ public boolean touchIsStarted;
+
+
+ public TouchEvent.Pointer[] touches = new TouchEvent.Pointer[0];
+
/**
* previous mouseX/Y for the draw loop, separated out because this is
* separate from the pmouseX/Y when inside the mouse event handlers.
*/
protected int dmouseX, dmouseY;
-// protected float dmotionX, dmotionY;
+
/**
* pmotionX/Y for the event handlers (motionPressed(), motionDragged() etc)
@@ -227,27 +247,21 @@ static public class RendererChangeException extends RuntimeException { }
* to be updated once per trip through draw().
*/
protected int emouseX, emouseY;
-// protected float emotionX, emotionY;
-// /**
-// * Used to set pmotionX/Y to motionX/Y the first time motionX/Y are used,
-// * otherwise pmotionX/Y are always zero, causing a nasty jump.
-// *
-// * Just using (frameCount == 0) won't work since motionXxxxx()
-// * may not be called until a couple frames into things.
-// */
-// public boolean firstMotion;
-// public int mouseButton;
-
- public boolean mousePressed;
+ /**
+ * ID of the pointer tracked for mouse events.
+ */
+ protected int mousePointerId;
-// public MouseEvent mouseEvent;
-// public MotionEvent motionEvent;
+ /**
+ * ID of the most recently touch pointer gone up or down.
+ */
+ protected int touchPointerId;
- /** Post events to the main thread that created the Activity */
- Handler handler;
+ ///////////////////////////////////////////////////////////////
+ // Key events
/**
* Last key pressed.
@@ -281,16 +295,44 @@ static public class RendererChangeException extends RuntimeException { }
*/
public boolean focused = false;
- protected boolean windowFocused = false;
- protected boolean viewFocused = false;
+ /**
+ * Keeps track of ENABLE_KEY_REPEAT hint
+ */
+ protected boolean keyRepeatEnabled = false;
/**
- * true if the applet is online.
- *
- * This can be used to test how the applet should behave
- * since online situations are different (no file writing, etc).
+ * Set to open when openKeyboard() is called, and used to close the keyboard when the sketch is
+ * paused, otherwise it remains visible.
+ */
+ boolean keyboardIsOpen = false;
+
+ /**
+ * Flag to determine if the back key was pressed.
+ */
+ private boolean requestedBackPress = false;
+
+ /**
+ * Flag to determine if the user handled the back press.
+ */
+ public boolean handledBackPressed = true;
+
+ ///////////////////////////////////////////////////////////////
+ // Permission handling
+
+ /**
+ * Callback methods to handle permission requests
+ */
+ protected HashMap permissionMethods = new HashMap();
+
+
+ /**
+ * Permissions requested during one frame
*/
-// public boolean online = false;
+ protected ArrayList reqPermissions = new ArrayList();
+
+
+ ///////////////////////////////////////////////////////////////
+ // Rendering/timing
/**
* Time in milliseconds when the applet was started.
@@ -299,6 +341,11 @@ static public class RendererChangeException extends RuntimeException { }
*/
long millisOffset = System.currentTimeMillis();
+ protected boolean insideDraw;
+
+ /** Last time in nanoseconds that frameRate was checked */
+ protected long frameRateLastNanos = 0;
+
/**
* The current value of frames per second.
*
@@ -309,12 +356,6 @@ static public class RendererChangeException extends RuntimeException { }
* As such, this value won't be valid until after 5-10 frames.
*/
public float frameRate = 10;
- /** Last time in nanoseconds that frameRate was checked */
- protected long frameRateLastNanos = 0;
-
- /** As of release 0116, frameRate(60) is called as a default */
- protected float frameRateTarget = 60;
- protected long frameRatePeriod = 1000000000L / 60L;
protected boolean looping;
@@ -337,27 +378,35 @@ static public class RendererChangeException extends RuntimeException { }
*/
public boolean finished;
- /**
- * For Android, true if the activity has been paused.
- */
- protected boolean paused;
-
- protected SurfaceView surfaceView;
-
- /**
- * The Window object for Android.
- */
-// protected Window window;
-
/**
* true if exit() has been called so that things shut down
* once the main thread kicks off.
*/
protected boolean exitCalled;
- Thread thread;
+ boolean insideSettings;
+
+ String renderer = JAVA2D;
+
+ int smooth = 1; // default smoothing (whatever that means for the renderer)
+
+ boolean fullScreen = false;
+
+ int display = -1; // use default
+
+ // Background default needs to be different from the default value in
+ // PGraphics.backgroundColor, otherwise size(100, 100) bg spills over.
+ // https://github.com/processing/processing/issues/2297
+ int windowColor = 0xffDDDDDD;
+
+ ///////////////////////////////////////////////////////////////
+ // Error messages
+
+ static final String ERROR_MIN_MAX =
+ "Cannot use min() or max() on an empty array.";
- // messages to send if attached as an external vm
+ ///////////////////////////////////////////////////////////////
+ // Command line options
/**
* Position of the upper-lefthand corner of the editor window
@@ -415,579 +464,441 @@ static public class RendererChangeException extends RuntimeException { }
/** true if this sketch is being run by the PDE */
boolean external = false;
- static final String ERROR_MIN_MAX =
- "Cannot use min() or max() on an empty array.";
-
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
+ /**
+ * Required empty constructor.
+ */
+ public PApplet() {
- /** Called with the activity is first created. */
- @SuppressWarnings("unchecked")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-// println("PApplet.onCreate()");
-
- if (DEBUG) println("onCreate() happening here: " + Thread.currentThread().getName());
-
- Window window = getWindow();
-
- // Take up as much area as possible
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
-
- // This does the actual full screen work
- window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
-
- DisplayMetrics dm = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(dm);
- displayWidth = dm.widthPixels;
- displayHeight = dm.heightPixels;
-// println("density is " + dm.density);
-// println("densityDpi is " + dm.densityDpi);
- if (DEBUG) println("display metrics: " + dm);
-
- //println("screen size is " + screenWidth + "x" + screenHeight);
-
-// LinearLayout layout = new LinearLayout(this);
-// layout.setOrientation(LinearLayout.VERTICAL | LinearLayout.HORIZONTAL);
-// viewGroup = new ViewGroup();
-// surfaceView.setLayoutParams();
-// viewGroup.setLayoutParams(LayoutParams.)
-// RelativeLayout layout = new RelativeLayout(this);
-// RelativeLayout overallLayout = new RelativeLayout(this);
-// RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.FILL_PARENT);
-//lp.addRule(RelativeLayout.RIGHT_OF, tv1.getId());
-// layout.setGravity(RelativeLayout.CENTER_IN_PARENT);
-
- int sw = sketchWidth();
- int sh = sketchHeight();
-
- // Get renderer name and class
- String rendererName = sketchRenderer();
- Class> rendererClass = null;
- try {
- rendererClass = Class.forName(rendererName);
- } catch (ClassNotFoundException exception) {
- String message = String.format(
- "Error: Could not resolve renderer class name: %s", rendererName);
- throw new RuntimeException(message, exception);
- }
-
- if (rendererName.equals(JAVA2D)) {
- // JAVA2D renderer
- surfaceView = new SketchSurfaceView(this, sw, sh,
- (Class extends PGraphicsAndroid2D>) rendererClass);
- } else if (PGraphicsOpenGL.class.isAssignableFrom(rendererClass)) {
- // P2D, P3D, and any other PGraphicsOpenGL-based renderer
- surfaceView = new SketchSurfaceViewGL(this, sw, sh,
- (Class extends PGraphicsOpenGL>) rendererClass);
- } else {
- // Anything else
- String message = String.format(
- "Error: Unsupported renderer class: %s", rendererName);
- throw new RuntimeException(message);
- }
+ }
-// g = ((SketchSurfaceView) surfaceView).getGraphics();
-// surfaceView.setLayoutParams(new LayoutParams(sketchWidth(), sketchHeight()));
+ public PSurface getSurface() {
+ return surface;
+ }
-// layout.addView(surfaceView);
-// surfaceView.setVisibility(1);
-// println("visibility " + surfaceView.getVisibility() + " " + SurfaceView.VISIBLE);
-// layout.addView(surfaceView);
-// AttributeSet as = new AttributeSet();
-// RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(layout, as);
-// lp.addRule(android.R.styleable.ViewGroup_Layout_layout_height,
-// layout.add
- //lp.addRule(, arg1)
- //layout.addView(surfaceView, sketchWidth(), sketchHeight());
+ public Context getContext() {
+ return surface.getContext();
+ }
-// new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
-// RelativeLayout.LayoutParams.FILL_PARENT);
- if (sw == displayWidth && sh == displayHeight) {
- // If using the full screen, don't embed inside other layouts
- window.setContentView(surfaceView);
- } else {
- // If not using full screen, setup awkward view-inside-a-view so that
- // the sketch can be centered on screen. (If anyone has a more efficient
- // way to do this, please file an issue on Google Code, otherwise you
- // can keep your "talentless hack" comments to yourself. Ahem.)
- RelativeLayout overallLayout = new RelativeLayout(this);
- RelativeLayout.LayoutParams lp =
- new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.addRule(RelativeLayout.CENTER_IN_PARENT);
-
- LinearLayout layout = new LinearLayout(this);
- layout.addView(surfaceView, sketchWidth(), sketchHeight());
- overallLayout.addView(layout, lp);
- window.setContentView(overallLayout);
- }
+ public Activity getActivity() {
+ return surface.getActivity();
+ }
- /*
- // Here we use Honeycomb API (11+) to hide (in reality, just make the status icons into small dots)
- // the status bar. Since the core is still built against API 7 (2.1), we use introspection to get
- // the setSystemUiVisibility() method from the view class.
- Method visibilityMethod = null;
- try {
- visibilityMethod = surfaceView.getClass().getMethod("setSystemUiVisibility", new Class[] { int.class});
- } catch (NoSuchMethodException e) {
- // Nothing to do. This means that we are running with a version of Android previous to Honeycomb.
- }
- if (visibilityMethod != null) {
- try {
- // This is equivalent to calling:
- //surfaceView.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
- // The value of View.STATUS_BAR_HIDDEN is 1.
- visibilityMethod.invoke(surfaceView, new Object[] { 1 });
- } catch (InvocationTargetException e) {
- } catch (IllegalAccessException e) {
- }
- }
- window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- */
+ public void initSurface(AppComponent component, SurfaceHolder holder) {
+ parentLayout = -1;
+ initSurface(null, null, null, component, holder);
+ }
-// layout.addView(surfaceView, lp);
-// surfaceView.setLayoutParams(new LayoutParams(sketchWidth(), sketchHeight()));
-// RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams()
-// layout.addView(surfaceView, new LayoutParams(arg0)
+ public void initSurface(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState,
+ AppComponent component, SurfaceHolder holder) {
+ if (DEBUG) println("initSurface() happening here: " + Thread.currentThread().getName());
- // TODO probably don't want to set these here, can't we wait for surfaceChanged()?
- // removing this in 0187
-// width = screenWidth;
-// height = screenHeight;
+ component.initDimensions();
+ displayWidth = component.getDisplayWidth();
+ displayHeight = component.getDisplayHeight();
+ displayDensity = component.getDisplayDensity();
-// int left = (screenWidth - iwidth) / 2;
-// int right = screenWidth - (left + iwidth);
-// int top = (screenHeight - iheight) / 2;
-// int bottom = screenHeight - (top + iheight);
-// surfaceView.setPadding(left, top, right, bottom);
- // android:layout_width
+ handleSettings();
-// window.setContentView(surfaceView); // set full screen
+ boolean parentSize = false;
+ if (parentLayout == -1) {
+ if (fullScreen || width == -1 || height == -1) {
+ // Either sketch explicitly set to full-screen mode, or not
+ // size/fullScreen provided, so sketch uses the entire display
+ width = displayWidth;
+ height = displayHeight;
+ }
+ } else {
+ if (fullScreen || width == -1 || height == -1) {
+ // Dummy weight and height to initialize the PGraphics, will be resized
+ // when the view associated to the parent layout is created
+ width = 100;
+ height = 100;
+ parentSize = true;
+ }
+ }
- // code below here formerly from init()
+ pixelWidth = width * pixelDensity;
+ pixelHeight = height * pixelDensity;
- //millisOffset = System.currentTimeMillis(); // moved to the variable declaration
+ String rendererName = sketchRenderer();
+ if (DEBUG) println("Renderer " + rendererName);
+ g = makeGraphics(width, height, rendererName, true);
+ if (DEBUG) println("Created renderer");
+ surface = g.createSurface(component, holder, false);
+ if (DEBUG) println("Created surface");
+
+ if (parentLayout == -1) {
+ setFullScreenVisibility();
+ surface.initView(width, height);
+ } else {
+ surface.initView(width, height, parentSize,
+ inflater, container, savedInstanceState);
+ }
finished = false; // just for clarity
-
// this will be cleared by draw() if it is not overridden
looping = true;
redraw = true; // draw this guy once
-// firstMotion = true;
- Context context = getApplicationContext();
- sketchPath = context.getFilesDir().getAbsolutePath();
+ sketchPath = surface.getFilesDir().getAbsolutePath();
-// Looper.prepare();
- handler = new Handler();
-// println("calling loop()");
-// Looper.loop();
-// println("done with loop() call, will continue...");
+ surface.startThread();
- start();
+ if (DEBUG) println("Done with init surface");
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- if (DEBUG) System.out.println("configuration changed: " + newConfig);
- super.onConfigurationChanged(newConfig);
+ private void setFullScreenVisibility() {
+ if (fullScreen) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ int visibility;
+ if (SDK < 19) {
+ // Pre-4.4
+ visibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ } else {
+ // 4.4 and higher. Integer instead of constants defined in View so it can
+ // build with SDK < 4.4
+ visibility = 256 | // View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ 512 | // View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ 1024 | // View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ 4 | // View.SYSTEM_UI_FLAG_FULLSCREEN
+ 4096; // View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ // However, this visibility does not fix a bug where the navigation area
+ // turns black after resuming the app:
+ // https://code.google.com/p/android/issues/detail?id=170752
+ }
+ surface.setSystemUiVisibility(visibility);
+ }
+ });
+ }
}
- @Override
- protected void onResume() {
- super.onResume();
-
- // TODO need to bring back app state here!
-// surfaceView.onResume();
+ public void onResume() {
if (DEBUG) System.out.println("PApplet.onResume() called");
- paused = false;
+ if (parentLayout == -1) {
+ setFullScreenVisibility();
+ }
+
handleMethods("resume");
- //start(); // kick the thread back on
- resume();
-// surfaceView.onResume();
+
+ // Don't call resume() when the app is starting and setup() has not been called yet:
+ // https://github.com/processing/processing-android/issues/274
+ // Also, there is no need to call resume() from anywhere else (for example, from
+ // onStart) since onResume() is always called in the activity lifecyle:
+ // https://developer.android.com/guide/components/activities/activity-lifecycle.html
+ if (0 < frameCount) {
+ resume();
+ }
+
+ // Set the handledBackPressed to true to handle the situation where a fragment is popping
+ // right back after pressing the back button (the sketch does not exit).
+ handledBackPressed = true;
+
+ if (g != null) {
+ g.restoreState();
+ }
+
+ surface.resumeThread();
}
- @Override
- protected void onPause() {
- super.onPause();
+ public void onPause() {
+ surface.pauseThread();
+
+ // Make sure that the keyboard is not left open after leaving the app
+ closeKeyboard();
+
+ if (g != null) {
+ g.saveState();
+ }
- // TODO need to save all application state here!
-// System.out.println("PApplet.onPause() called");
- paused = true;
handleMethods("pause");
+
pause(); // handler for others to write
-// synchronized (this) {
-// paused = true;
-//}
-// surfaceView.onPause();
}
- /**
- * Developers can override here to save state. The 'paused' variable will be
- * set before this function is called.
- */
- public void pause() {
+ public void onStart() {
+ start();
}
- /**
- * Developers can override here to restore state. The 'paused' variable
- * will be cleared before this function is called.
- */
- public void resume() {
+ public void onStop() {
+ stop();
+ }
+
+
+ public void onCreate(Bundle savedInstanceState) {
+ create();
}
- @Override
public void onDestroy() {
-// stop();
+ handleMethods("onDestroy");
+
+ surface.stopThread();
+
dispose();
- if (PApplet.DEBUG) {
- System.out.println("PApplet.onDestroy() called");
- }
- super.onDestroy();
- //finish();
}
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ handleMethods("onActivityResult", new Object[] { requestCode, resultCode, data });
+ }
- //////////////////////////////////////////////////////////////
- // ANDROID SURFACE VIEW
+ public void onNewIntent(Intent intent) {
+ handleMethods("onNewIntent", new Object[] { intent });
+ }
+
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
- // TODO this is only used by A2D, when finishing up a draw. but if the
- // surfaceview has changed, then it might belong to an a3d surfaceview. hrm.
- public SurfaceHolder getSurfaceHolder() {
- return surfaceView.getHolder();
-// return surfaceHolder;
}
- /** Not official API, not guaranteed to work in the future. */
- public SurfaceView getSurfaceView() {
- return surfaceView;
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return false;
}
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ }
-// public interface SketchSurfaceView {
-// public PGraphics getGraphics();
-// }
+ public boolean onContextItemSelected(MenuItem item) {
+ return false;
+ }
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ public void setHasOptionsMenu(boolean hasMenu) {
+ surface.setHasOptionsMenu(hasMenu);
+ }
- public class SketchSurfaceView extends SurfaceView implements
- SurfaceHolder.Callback {
- PGraphicsAndroid2D g2;
- SurfaceHolder surfaceHolder;
+ synchronized public void onBackPressed() {
+ requestedBackPress = true;
+ }
- public SketchSurfaceView(Context context, int wide, int high,
- Class extends PGraphicsAndroid2D> clazz) {
- super(context);
+ public FragmentManager getFragmentManager() {
+ if (getActivity() != null) {
+ return getActivity().getFragmentManager();
+ }
+ return null;
+ }
-// println("surface holder");
- // Install a SurfaceHolder.Callback so we get notified when the
- // underlying surface is created and destroyed
- surfaceHolder = getHolder();
- surfaceHolder.addCallback(this);
- surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
-// println("creating graphics");
- if (clazz.equals(PGraphicsAndroid2D.class)) {
- g2 = new PGraphicsAndroid2D();
- } else {
- try {
- Constructor extends PGraphicsAndroid2D> constructor =
- clazz.getConstructor();
- g2 = constructor.newInstance();
- } catch (Exception exception) {
- throw new RuntimeException(
- "Error: Failed to initialize custom Android2D renderer",
- exception);
- }
- }
+ public Window getWindow(){
+ if (getActivity() != null) {
+ return getActivity().getWindow();
+ }
+ return null;
+ }
- // Set semi-arbitrary size; will be set properly when surfaceChanged() called
- g2.setSize(wide, high);
-// newGraphics.setSize(getWidth(), getHeight());
- g2.setParent(PApplet.this);
- g2.setPrimary(true);
- // Set the value for 'g' once everything is ready (otherwise rendering
- // may attempt before setSize(), setParent() etc)
-// g = newGraphics;
- g = g2; // assign the g object for the PApplet
-// println("setting focusable, requesting focus");
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
-// println("done making surface view");
- }
+ public void startActivity(Intent intent) {
+ surface.startActivity(intent);
+ }
-// public PGraphics getGraphics() {
-// return g2;
-// }
+ public void runOnUiThread(Runnable action) {
+ surface.runOnUiThread(action);
+ }
- // part of SurfaceHolder.Callback
- public void surfaceCreated(SurfaceHolder holder) {
- }
+ public boolean hasPermission(String permission) {
+ return surface.hasPermission(permission);
+ }
- // part of SurfaceHolder.Callback
- public void surfaceDestroyed(SurfaceHolder holder) {
- //g2.dispose();
+ public void requestPermission(String permission) {
+ if (!hasPermission(permission)) {
+ reqPermissions.add(permission);
}
+ }
- // part of SurfaceHolder.Callback
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- if (DEBUG) {
- System.out.println("SketchSurfaceView2D.surfaceChanged() " + w + " " + h);
- }
- surfaceChanged = true;
+ public void requestPermission(String permission, String callback) {
+ requestPermission(permission, callback, this);
+ }
-// width = w;
-// height = h;
-//
-// g.setSize(w, h);
+
+ public void requestPermission(String permission, String callback, Object target) {
+ registerWithArgs(callback, target, new Class[] { boolean.class });
+ if (hasPermission(permission)) {
+ // If the app already has permission, still call the handle method as it
+ // may be doing some initialization
+ handleMethods(callback, new Object[] { true });
+ } else {
+ permissionMethods.put(permission, callback);
+ // Accumulating permissions so they requested all at once at the end
+ // of draw.
+ reqPermissions.add(permission);
}
+ }
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- surfaceWindowFocusChanged(hasFocus);
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[],
+ int[] grantResults) {
+ if (requestCode == PSurface.REQUEST_PERMISSIONS) {
+ for (int i = 0; i < grantResults.length; i++) {
+ boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
+ handlePermissionsResult(permissions[i], granted);
+ }
}
+ }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return surfaceTouchEvent(event);
+ private void handlePermissionsResult(String permission, final boolean granted) {
+ String methodName = permissionMethods.get(permission);
+ final RegisteredMethods meth = registerMap.get(methodName);
+ if (meth != null) {
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ meth.handle(new Object[] { granted });
+ }
+ });
}
+ }
- @Override
- public boolean onKeyDown(int code, android.view.KeyEvent event) {
- return surfaceKeyDown(code, event);
+ private void handlePermissions() {
+ if (0 < reqPermissions.size()) {
+ String[] req = reqPermissions.toArray(new String[reqPermissions.size()]);
+ surface.requestPermissions(req);
+ reqPermissions.clear();
}
+ }
+ synchronized private void handleBackPressed() {
+ if (requestedBackPress) {
+ requestedBackPress = false;
+ backPressed();
+ if (!handledBackPressed) {
+ if (getActivity() != null) {
+ // Services don't have an activity associated to them, but back press could not be triggered for those anyways
+ getActivity().finish();
+ }
+ handledBackPressed = false;
+ }
+ }
+ }
- @Override
- public boolean onKeyUp(int code, android.view.KeyEvent event) {
- return surfaceKeyUp(code, event);
+ /**
+ * @param method "size" or "fullScreen"
+ * @param args parameters passed to the function so we can show the user
+ * @return true if safely inside the settings() method
+ */
+ boolean insideSettings(String method, Object... args) {
+ if (insideSettings) {
+ return true;
+ }
+ final String url = "https://processing.org/reference/" + method + "_.html";
+ if (!external) { // post a warning for users of Eclipse and other IDEs
+ StringList argList = new StringList(args);
+ System.err.println("When not using the PDE, " + method + "() can only be used inside settings().");
+ System.err.println("Remove the " + method + "() method from setup(), and add the following:");
+ System.err.println("public void settings() {");
+ System.err.println(" " + method + "(" + argList.join(", ") + ");");
+ System.err.println("}");
}
+ throw new IllegalStateException(method + "() cannot be used here, see " + url);
+ }
- // don't think i want to call stop() from here, since it might be swapping renderers
-// @Override
-// protected void onDetachedFromWindow() {
-// super.onDetachedFromWindow();
-// stop();
-// }
+ void handleSettings() {
+ insideSettings = true;
+ //Do stuff
+ settings();
+ insideSettings = false;
}
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ public void settings() {
+ //It'll be empty. Will be overridden by user's sketch class.
+ }
- public class SketchSurfaceViewGL extends GLSurfaceView {
- PGraphicsOpenGL g3;
- SurfaceHolder surfaceHolder;
+ final public int sketchWidth() {
+ return width;
+ }
- public SketchSurfaceViewGL(Context context, int wide, int high,
- Class extends PGraphicsOpenGL> clazz) {
- super(context);
+ final public int sketchHeight() {
+ return height;
+ }
- // Check if the system supports OpenGL ES 2.0.
- final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
- final boolean supportsGLES2 = configurationInfo.reqGlEsVersion >= 0x20000;
- if (!supportsGLES2) {
- throw new RuntimeException("OpenGL ES 2.0 is not supported by this device.");
- }
+ final public String sketchRenderer() {
+ return renderer;
+ }
- surfaceHolder = getHolder();
- // are these two needed?
- surfaceHolder.addCallback(this);
- //surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
-
- // The PGraphics object needs to be created here so the renderer is not
- // null. This is required because PApplet.onResume events (which call
- // this.onResume() and thus require a valid renderer) are triggered
- // before surfaceChanged() is ever called.
-
- if (clazz.equals(PGraphics2D.class)) { // P2D
- g3 = new PGraphics2D();
- } else if (clazz.equals(PGraphics3D.class)) { // P3D
- g3 = new PGraphics3D();
- } else { // something that extends P2D, P3D, or PGraphicsOpenGL
- try {
- Constructor extends PGraphicsOpenGL> constructor =
- clazz.getConstructor();
- g3 = constructor.newInstance();
- } catch (Exception exception) {
- throw new RuntimeException(
- "Error: Failed to initialize custom OpenGL renderer",
- exception);
- }
- }
- //set it up
- g3.setParent(PApplet.this);
- g3.setPrimary(true);
- // Set semi-arbitrary size; will be set properly when surfaceChanged() called
- g3.setSize(wide, high);
+ public int sketchSmooth() {
+ return smooth;
+ }
- // Tells the default EGLContextFactory and EGLConfigChooser to create an GLES2 context.
- setEGLContextClientVersion(2);
- int quality = sketchQuality();
- if (1 < quality) {
- setEGLConfigChooser(((PGLES)g3.pgl).getConfigChooser(quality));
- }
+ final public boolean sketchFullScreen() {
+ return fullScreen;
+ }
- // The renderer can be set only once.
- setRenderer(((PGLES)g3.pgl).getRenderer());
- setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
- // assign this g to the PApplet
- g = g3;
+ final public int sketchDisplay() {
+ return display;
+ }
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
- }
+ final public String sketchOutputPath() {
+ return null;
+ }
- public PGraphics getGraphics() {
- return g3;
- }
+ final public OutputStream sketchOutputStream() {
+ return null;
+ }
- // part of SurfaceHolder.Callback
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- super.surfaceCreated(holder);
- if (DEBUG) {
- System.out.println("surfaceCreated()");
- }
- }
+ final public int sketchWindowColor() {
+ return windowColor;
+ }
- // part of SurfaceHolder.Callback
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- super.surfaceDestroyed(holder);
- if (DEBUG) {
- System.out.println("surfaceDestroyed()");
- }
- /*
- // TODO: Check how to make sure of calling g3.dispose() when this call to
- // surfaceDestoryed corresponds to the sketch being shut down instead of just
- // taken to the background.
+ final public int sketchPixelDensity() {
+ return pixelDensity;
+ }
- // For instance, something like this would be ok?
- // The sketch is being stopped, so we dispose the resources.
- if (!paused) {
- g3.dispose();
- }
- */
- }
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- super.surfaceChanged(holder, format, w, h);
- if (DEBUG) {
- System.out.println("SketchSurfaceView3D.surfaceChanged() " + w + " " + h);
- }
- surfaceChanged = true;
-// width = w;
-// height = h;
-// g.setSize(w, h);
+ public void surfaceChanged() {
+ surfaceChanged = true;
+ g.surfaceChanged();
+ }
- // No need to call g.setSize(width, height) b/c super.surfaceChanged()
- // will trigger onSurfaceChanged in the renderer, which calls setSize().
- // -- apparently not true? (100110)
- }
-
- /**
- * Inform the view that the window focus has changed.
- */
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- surfaceWindowFocusChanged(hasFocus);
-// super.onWindowFocusChanged(hasFocus);
-// focused = hasFocus;
-// if (focused) {
-//// println("got focus");
-// focusGained();
-// } else {
-//// println("lost focus");
-// focusLost();
-// }
- }
-
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return surfaceTouchEvent(event);
- }
-
-
- @Override
- public boolean onKeyDown(int code, android.view.KeyEvent event) {
- return surfaceKeyDown(code, event);
- }
-
-
- @Override
- public boolean onKeyUp(int code, android.view.KeyEvent event) {
- return surfaceKeyUp(code, event);
- }
-
-
- // don't think i want to call stop() from here, since it might be swapping renderers
-// @Override
-// protected void onDetachedFromWindow() {
-// super.onDetachedFromWindow();
-// stop();
-// }
- }
-
-
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
@@ -995,7 +906,6 @@ public boolean onKeyUp(int code, android.view.KeyEvent event) {
* by Android as well.
*/
public void surfaceWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
focused = hasFocus;
if (focused) {
focusGained();
@@ -1006,69 +916,25 @@ public void surfaceWindowFocusChanged(boolean hasFocus) {
/**
- * If you override this function without calling super.onTouchEvent(),
+ * If you override this function without calling super.surfaceTouchEvent(),
* then motionX, motionY, motionPressed, and motionEvent will not be set.
*/
public boolean surfaceTouchEvent(MotionEvent event) {
-// println(event);
nativeMotionEvent(event);
-// return super.onTouchEvent(event);
return true;
}
- public boolean surfaceKeyDown(int code, android.view.KeyEvent event) {
- // System.out.println("got onKeyDown for " + code + " " + event);
+ public void surfaceKeyDown(int code, android.view.KeyEvent event) {
nativeKeyEvent(event);
- return super.onKeyDown(code, event);
}
- public boolean surfaceKeyUp(int code, android.view.KeyEvent event) {
- // System.out.println("got onKeyUp for " + code + " " + event);
+ public void surfaceKeyUp(int code, android.view.KeyEvent event) {
nativeKeyEvent(event);
- return super.onKeyUp(code, event);
- }
-
-
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
-
- public int sketchQuality() {
- return 1;
}
- public int sketchWidth() {
- return displayWidth;
- }
-
-
- public int sketchHeight() {
- return displayHeight;
- }
-
-
- public String sketchRenderer() {
- return JAVA2D;
- }
-
-
- public void orientation(int which) {
- if (which == PORTRAIT) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- } else if (which == LANDSCAPE) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- }
- }
-
-
-// public int sketchOrientation() {
-// return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-// //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-// }
-
-
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -1081,13 +947,6 @@ public void orientation(int which) {
* PAppletGL needs to have a usable screen before getting things rolling.
*/
public void start() {
- finished = false;
- paused = false; // unpause the thread
-
- if (thread == null) {
- thread = new Thread(this, "Animation Thread");
- thread.start();
- }
}
@@ -1100,48 +959,28 @@ public void start() {
* or when moving between web pages), and it's not always called.
*/
public void stop() {
- // this used to shut down the sketch, but that code has
- // been moved to dispose()
-
- paused = true; // sleep the animation thread
-
- //TODO listeners
}
/**
- * Called by the browser or applet viewer to inform this applet
- * that it is being reclaimed and that it should destroy
- * any resources that it has allocated.
- *
- * This also attempts to call PApplet.stop(), in case there
- * was an inadvertent override of the stop() function by a user.
- *
- * destroy() supposedly gets called as the applet viewer
- * is shutting down the applet. stop() is called
- * first, and then destroy() to really get rid of things.
- * no guarantees on when they're run (on browser quit, or
- * when moving between pages), though.
+ * Developers can override here to save state. The 'paused' variable will be
+ * set before this function is called.
*/
- public void destroy() {
- ((PApplet)this).exit();
+ public void pause() {
}
/**
- * This returns the last width and height specified by the user
- * via the size() command.
+ * Developers can override here to restore state. The 'paused' variable
+ * will be cleared before this function is called.
*/
-// public Dimension getPreferredSize() {
-// return new Dimension(width, height);
-// }
-
+ public void resume() {
+ }
-// public void addNotify() {
-// super.addNotify();
-// println("addNotify()");
-// }
+ public void backPressed() {
+ handledBackPressed = false;
+ }
//////////////////////////////////////////////////////////////
@@ -1258,8 +1097,8 @@ protected int findIndex(Object object) {
* pre – at the very top of the draw() method (safe to draw)
* draw – at the end of the draw() method (safe to draw)
* post – after draw() has exited (not safe to draw)
- * pause – called when the sketch is paused
- * resume – called when the sketch is resumed
+ * pause – called when the sketch is paused
+ * resume – called when the sketch is resumed
* dispose – when the sketch is shutting down (definitely not safe to draw)
*
* In addition, the new (for 2.0) processing.event classes are passed to
@@ -1284,6 +1123,14 @@ public void registerMethod(String methodName, Object target) {
} else if (methodName.equals("touchEvent")) {
registerWithArgs("touchEvent", target, new Class[] { processing.event.TouchEvent.class });
+ // Android-lifecycle event handlers
+ } else if (methodName.equals("onDestroy")) {
+ registerNoArgs(methodName, target);
+ } else if (methodName.equals("onActivityResult")) {
+ registerWithArgs("onActivityResult", target, new Class[] { int.class, int.class, Intent.class });
+ } else if (methodName.equals("onNewIntent")) {
+ registerWithArgs("onNewIntent", target, new Class[] { Intent.class });
+
} else {
registerNoArgs(methodName, target);
}
@@ -1360,10 +1207,16 @@ protected void handleMethods(String methodName) {
}
- protected void handleMethods(String methodName, Object[] args) {
- RegisteredMethods meth = registerMap.get(methodName);
+ protected void handleMethods(String methodName, final Object[] args) {
+ final RegisteredMethods meth = registerMap.get(methodName);
if (meth != null) {
- meth.handle(args);
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ meth.handle(args);
+ }
+ });
}
}
@@ -1467,6 +1320,54 @@ public void draw() {
// }
+ /**
+ * Create a full-screen sketch using the default renderer.
+ */
+ public void fullScreen() {
+ if (!fullScreen) {
+ if (insideSettings("fullScreen")) {
+ this.fullScreen = true;
+ }
+ }
+ }
+
+
+ public void fullScreen(int display) {
+ //Display index doesn't make sense in Android.
+ //Should we throw some error in log ?
+ if (!fullScreen /*|| display != this.display*/) {
+ if (insideSettings("fullScreen", display)) {
+ this.fullScreen = true;
+// this.display = display;
+ }
+ }
+ }
+
+
+ public void fullScreen(String renderer) {
+ if (!fullScreen ||
+ !renderer.equals(this.renderer)) {
+ if (insideSettings("fullScreen", renderer)) {
+ this.fullScreen = true;
+ this.renderer = renderer;
+ }
+ }
+ }
+
+
+ public void fullScreen(String renderer, int display) {
+ if (!fullScreen ||
+ !renderer.equals(this.renderer) /*||
+ display != this.display*/) {
+ if (insideSettings("fullScreen", renderer, display)) {
+ this.fullScreen = true;
+ this.renderer = renderer;
+// this.display = display;
+ }
+ }
+ }
+
+
/**
* Starts up and creates a two-dimensional drawing surface, or resizes the
* current drawing surface.
@@ -1477,15 +1378,92 @@ public void draw() {
* previous renderer and simply resize it.
*/
public void size(int iwidth, int iheight) {
- size(iwidth, iheight, P2D, null);
+ if (iwidth != this.width || iheight != this.height) {
+ if (insideSettings("size", iwidth, iheight)) {
+ this.width = iwidth;
+ this.height = iheight;
+ }
+ }
}
public void size(int iwidth, int iheight, String irenderer) {
- size(iwidth, iheight, irenderer, null);
+ if (iwidth != this.width || iheight != this.height ||
+ !this.renderer.equals(irenderer)) {
+ if (insideSettings("size", iwidth, iheight, irenderer)) {
+ this.width = iwidth;
+ this.height = iheight;
+ this.renderer = irenderer;
+ }
+ }
}
+ public void setSize(int width, int height) {
+ if (fullScreen) {
+ this.displayWidth = width;
+ this.displayHeight = height;
+ }
+ this.width = width;
+ this.height = height;
+ pixelWidth = width * pixelDensity;
+ pixelHeight = height * pixelDensity;
+ g.setSize(sketchWidth(), sketchHeight());
+ }
+
+
+ public void setExternal(boolean external) {
+ this.external = external;
+ }
+
+
+//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public void smooth() {
+ smooth(1);
+ }
+
+
+ public void smooth(int level) {
+ if (insideSettings) {
+ this.smooth = level;
+ } else if (this.smooth != level) {
+ smoothWarning("smooth");
+ }
+ }
+
+
+ public void noSmooth() {
+ if (insideSettings) {
+ this.smooth = 0;
+ } else if (this.smooth != 0) {
+ smoothWarning("noSmooth");
+ }
+ }
+
+ private void smoothWarning(String method) {
+ // When running from the PDE, say setup(), otherwise say settings()
+ final String where = external ? "setup" : "settings";
+ PGraphics.showWarning("%s() can only be used inside %s()", method, where);
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public PGraphics getGraphics() {
+ return g;
+ }
+
+ public void orientation(int which) {
+ surface.setOrientation(which);
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
// not finished yet--will swap the renderer at a bad time
/*
public void renderer(String name) {
@@ -1519,82 +1497,15 @@ public void renderer(String name) {
*/
public void size(final int iwidth, final int iheight,
final String irenderer, final String ipath) {
- System.out.println("This size() method is ignored on Android.");
- System.out.println("See http://wiki.processing.org/w/Android for more information.");
-
- /*
-// Looper.prepare();
- // Run this from the EDT, just cuz it's AWT stuff (or maybe later Swing)
-// new Handler().post(new Runnable() {
- handler.post(new Runnable() {
- public void run() {
- println("Handler is on thread " + Thread.currentThread().getName());
-// // Set the preferred size so that the layout managers can handle it
-//// setPreferredSize(new Dimension(iwidth, iheight));
-//// setSize(iwidth, iheight);
-// g.setSize(iwidth, iheight);
-// }
-// });
-
- // ensure that this is an absolute path
-// if (ipath != null) ipath = savePath(ipath);
- // no path renderers supported yet
-
- println("I'm the ole thread " + Thread.currentThread().getName());
- String currentRenderer = g.getClass().getName();
- if (currentRenderer.equals(irenderer)) {
- println("resizing renderer inside size(....)");
- // Avoid infinite loop of throwing exception to reset renderer
- resizeRenderer(iwidth, iheight);
- //redraw(); // will only be called insize draw()
-
- } else { // renderer is being changed
- println("changing renderer inside size(....)");
-
- // otherwise ok to fall through and create renderer below
- // the renderer is changing, so need to create a new object
-// g = makeGraphics(iwidth, iheight, irenderer, ipath, true);
-// width = iwidth;
-// height = iheight;
-// if ()
-
- // Remove the old view from the layout
- layout.removeView(surfaceView);
-
- if (irenderer.equals(A2D)) {
- surfaceView = new SketchSurfaceView2D(PApplet.this, iwidth, iheight);
- } else if (irenderer.equals(A3D)) {
- surfaceView = new SketchSurfaceView3D(PApplet.this, iwidth, iheight);
+ if (iwidth != this.width || iheight != this.height ||
+ !this.renderer.equals(irenderer)) {
+ if (insideSettings("size", iwidth, iheight, irenderer,
+ ipath)) {
+ this.width = iwidth;
+ this.height = iheight;
+ this.renderer = irenderer;
}
- g = ((SketchSurfaceView) surfaceView).getGraphics();
-
- // these don't seem like a good idea
-// width = screenWidth;
-// height = screenHeight;
-
-// println("getting window");
-// Window window = getWindow();
-// window.setContentView(surfaceView); // set full screen
-// layout.removeAllViews();
- layout.addView(surfaceView);
-
- // fire resize event to make sure the applet is the proper size
-// setSize(iwidth, iheight);
- // this is the function that will run if the user does their own
- // size() command inside setup, so set defaultSize to false.
-// defaultSize = false;
-
- // throw an exception so that setup() is called again
- // but with a properly sized render
- // this is for opengl, which needs a valid, properly sized
- // display before calling anything inside setup().
-// throw new RendererChangeException();
- println("interrupting animation thread");
- thread.interrupt();
}
- }
- });
-*/
}
@@ -1654,16 +1565,23 @@ public PGraphics createGraphics(int iwidth, int iheight) {
*
*/
public PGraphics createGraphics(int iwidth, int iheight, String irenderer) {
+ return makeGraphics(iwidth, iheight, irenderer, false);
+ }
+
+
+ protected PGraphics makeGraphics(int w, int h,
+ String renderer, boolean primary) {
PGraphics pg = null;
- if (irenderer.equals(JAVA2D)) {
+ if (renderer.equals(JAVA2D)) {
pg = new PGraphicsAndroid2D();
- } else if (irenderer.equals(P2D)) {
- if (!g.isGL()) {
+ } else if (renderer.equals(P2D)) {
+ if (!primary && !g.isGL()) {
throw new RuntimeException("createGraphics() with P2D requires size() to use P2D or P3D");
}
pg = new PGraphics2D();
- } else if (irenderer.equals(P3D)) {
- if (!g.isGL()) {
+
+ } else if (renderer.equals(P3D)) {
+ if (!primary && !g.isGL()) {
throw new RuntimeException("createGraphics() with P3D or OPENGL requires size() to use P2D or P3D");
}
pg = new PGraphics3D();
@@ -1671,12 +1589,8 @@ public PGraphics createGraphics(int iwidth, int iheight, String irenderer) {
Class> rendererClass = null;
Constructor> constructor = null;
try {
- // The context class loader doesn't work:
- //rendererClass = Thread.currentThread().getContextClassLoader().loadClass(irenderer);
- // even though it should, according to this discussion:
// http://code.google.com/p/android/issues/detail?id=11101
- // While the method that is not supposed to work, using the class loader, does:
- rendererClass = this.getClass().getClassLoader().loadClass(irenderer);
+ rendererClass = Thread.currentThread().getContextClassLoader().loadClass(renderer);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Missing renderer class");
}
@@ -1692,27 +1606,28 @@ public PGraphics createGraphics(int iwidth, int iheight, String irenderer) {
try {
pg = (PGraphics) constructor.newInstance();
} catch (InvocationTargetException e) {
- e.printStackTrace();
+ printStackTrace(e);
throw new RuntimeException(e.getMessage());
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ printStackTrace(e);
throw new RuntimeException(e.getMessage());
} catch (InstantiationException e) {
- e.printStackTrace();
+ printStackTrace(e);
throw new RuntimeException(e.getMessage());
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ printStackTrace(e);
}
}
}
}
pg.setParent(this);
- pg.setPrimary(false);
- pg.setSize(iwidth, iheight);
-
+ pg.setPrimary(primary);
+ pg.setSize(w, h);
return pg;
}
-
/**
* Create an offscreen graphics surface for drawing, in this case
* for a renderer that writes to a file (such as PDF or DXF).
@@ -1860,9 +1775,7 @@ protected void paint() {
//////////////////////////////////////////////////////////////
- /**
- * Main method for the primary animation thread.
- */
+/*
public void run() { // not good to make this synchronized, locks things up
long beforeTime = System.nanoTime();
long overSleepTime = 0L;
@@ -1872,7 +1785,7 @@ public void run() { // not good to make this synchronized, locks things up
// animation thread yields to other running threads.
final int NO_DELAYS_PER_YIELD = 15;
- while ((Thread.currentThread() == thread) && !finished) {
+ while (!finished) {
while (paused) {
try{
@@ -1882,27 +1795,8 @@ public void run() { // not good to make this synchronized, locks things up
}
}
- // Don't resize the renderer from the EDT (i.e. from a ComponentEvent),
- // otherwise it may attempt a resize mid-render.
-// if (resizeRequest) {
-// resizeRenderer(resizeWidth, resizeHeight);
-// resizeRequest = false;
-// }
-
// render a single frame
if (g != null) g.requestDraw();
-// g.requestDraw();
-// surfaceView.requestDraw();
-
- // removed in android
-// if (frameCount == 1) {
-// // Call the request focus event once the image is sure to be on
-// // screen and the component is valid. The OpenGL renderer will
-// // request focus for its canvas inside beginDraw().
-// // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/FocusSpec.html
-// //println("requesting focus");
-// requestFocus();
-// }
// wait for update & paint to happen before drawing next frame
// this is necessary since the drawing is sometimes in a
@@ -1944,132 +1838,134 @@ public void run() { // not good to make this synchronized, locks things up
// If the user called the exit() function, the window should close,
// rather than the sketch just halting.
if (exitCalled) {
- exit2();
+ exitActual();
}
}
}
-
+*/
public void handleDraw() {
- if (DEBUG) {
- println("inside handleDraw() " + millis() +
- " changed=" + surfaceChanged +
- " ready=" + surfaceReady +
- " paused=" + paused +
- " looping=" + looping +
- " redraw=" + redraw);
- }
- if (surfaceChanged) {
- int newWidth = surfaceView.getWidth();
- int newHeight = surfaceView.getHeight();
- if (newWidth != width || newHeight != height) {
- width = newWidth;
- height = newHeight;
- g.setSize(width, height);
- }
- surfaceChanged = false;
- surfaceReady = true;
- if (DEBUG) {
- println("surfaceChanged true, resized to " + width + "x" + height);
- }
+ //debug("handleDraw() " + g + " " + looping + " " + redraw + " valid:" + this.isValid() + " visible:" + this.isVisible());
+
+ if (g == null) return;
+
+ if (!surfaceChanged && parentLayout != -1) {
+ // When using a parent layout, don't start drawing until the sketch
+ // has been properly sized.
+ return;
}
-// if (surfaceView.isShown()) {
-// println("surface view not visible, getting out");
-// return;
-// } else {
-// println("surface set to go.");
-// }
+ if (!looping && !redraw) return;
- // don't start drawing (e.g. don't call setup) until there's a legitimate
- // width and height that have been set by surfaceChanged().
-// boolean validSize = width != 0 && height != 0;
-// println("valid size = " + validSize + " (" + width + "x" + height + ")");
- if (canDraw()) {
-// if (!g.canDraw()) {
-// // Don't draw if the renderer is not yet ready.
-// // (e.g. OpenGL has to wait for a peer to be on screen)
-// return;
-// }
+ if (insideDraw) {
+ System.err.println("handleDraw() called before finishing");
+ System.exit(1);
+ }
- g.beginDraw();
+ insideDraw = true;
- long now = System.nanoTime();
+// if (recorder != null) {
+// recorder.beginDraw();
+// }
- if (frameCount == 0) {
- try {
- //println("Calling setup()");
- setup();
- //println("Done with setup()");
+ if (handleSpecialDraw()) return;
- } catch (RendererChangeException e) {
- // Give up, instead set the new renderer and re-attempt setup()
- return;
- }
-// this.defaultSize = false;
+ g.beginDraw();
- } else { // frameCount > 0, meaning an actual draw()
- // update the current frameRate
- double rate = 1000000.0 / ((now - frameRateLastNanos) / 1000000.0);
- float instantaneousRate = (float) rate / 1000.0f;
- frameRate = (frameRate * 0.9f) + (instantaneousRate * 0.1f);
+ long now = System.nanoTime();
- if (frameCount != 0) {
- handleMethods("pre");
- }
+ if (frameCount == 0) {
+ setup();
- // use dmouseX/Y as previous mouse pos, since this is the
- // last position the mouse was in during the previous draw.
- pmouseX = dmouseX;
- pmouseY = dmouseY;
-// pmotionX = dmotionX;
-// pmotionY = dmotionY;
-
- //println("Calling draw()");
- draw();
- //println("Done calling draw()");
-
- // dmouseX/Y is updated only once per frame (unlike emouseX/Y)
- dmouseX = mouseX;
- dmouseY = mouseY;
-// dmotionX = motionX;
-// dmotionY = motionY;
-
- // these are called *after* loop so that valid
- // drawing commands can be run inside them. it can't
- // be before, since a call to background() would wipe
- // out anything that had been drawn so far.
-// dequeueMotionEvents();
-// dequeueKeyEvents();
- dequeueEvents();
-
- handleMethods("draw");
-
- redraw = false; // unset 'redraw' flag in case it was set
- // (only do this once draw() has run, not just setup())
- }
- g.endDraw();
+ } else { // frameCount > 0, meaning an actual draw()
+ // update the current frameRate
+ double rate = 1000000.0 / ((now - frameRateLastNanos) / 1000000.0);
+ float instantaneousRate = (float) (rate / 1000.0);
+ frameRate = (frameRate * 0.9f) + (instantaneousRate * 0.1f);
if (frameCount != 0) {
- handleMethods("post");
+ handleMethods("pre");
}
- frameRateLastNanos = now;
- frameCount++;
+ // use dmouseX/Y as previous mouse pos, since this is the
+ // last position the mouse was in during the previous draw.
+ pmouseX = dmouseX;
+ pmouseY = dmouseY;
+
+ draw();
+
+ // dmouseX/Y is updated only once per frame (unlike emouseX/Y)
+ dmouseX = mouseX;
+ dmouseY = mouseY;
+
+ // these are called *after* loop so that valid
+ // drawing commands can be run inside them. it can't
+ // be before, since a call to background() would wipe
+ // out anything that had been drawn so far.
+ dequeueEvents();
+
+ handleMethods("draw");
+ handlePermissions();
+ handleBackPressed();
+
+ redraw = false; // unset 'redraw' flag in case it was set
+ // (only do this once draw() has run, not just setup())
+ }
+ g.endDraw();
+
+// if (recorder != null) {
+// recorder.endDraw();
+// }
+ insideDraw = false;
+
+ if (frameCount != 0) {
+ handleMethods("post");
}
+
+ frameRateLastNanos = now;
+ frameCount++;
}
- /** Not official API, not guaranteed to work in the future. */
- public boolean canDraw() {
- return g != null && surfaceReady && !paused && (looping || redraw);
+ // This method handles some special situations on Android where beginDraw/endDraw are needed,
+ // but not to render the actual contents of draw(). In general, these situations arise from
+ // having to refresh/restore the screen after requesting no loop, or resuming the sketch in
+ // no-loop state.
+ protected boolean handleSpecialDraw() {
+ boolean handled = false;
+
+ if (g.restoringState()) {
+ // The sketch is restoring, so begin/end the frame properly and quit drawing.
+ g.beginDraw();
+ g.endDraw();
+
+ handled = true;
+ } else if (g.requestedNoLoop) {
+ // noLoop() was called sometime in the previous frame with a GL renderer, but only now
+ // we are sure that the frame is properly displayed.
+ looping = false;
+
+ // Perform a full frame draw, to ensure that the previous frame is properly displayed (see
+ // comment in the declaration of requestedNoLoop).
+ g.beginDraw();
+ g.endDraw();
+
+ g.requestedNoLoop = false;
+ handled = true;
+ }
+
+ if (handled) {
+ insideDraw = false;
+ return true;
+ } else {
+ return false;
+ }
}
//////////////////////////////////////////////////////////////
-
synchronized public void redraw() {
if (!looping) {
redraw = true;
@@ -2097,11 +1993,20 @@ synchronized public void loop() {
synchronized public void noLoop() {
if (looping) {
- looping = false;
+ if (g.requestNoLoop()) {
+ g.requestedNoLoop = true;
+ } else {
+ looping = false;
+ }
}
}
+ public boolean isLooping() {
+ return looping;
+ }
+
+
//////////////////////////////////////////////////////////////
@@ -2179,9 +2084,9 @@ protected void dequeueEvents() {
Event e = eventQueue.remove();
switch (e.getFlavor()) {
-// case Event.TOUCH:
-// handleTouchEvent((TouchEvent) e);
-// break;
+ case Event.TOUCH:
+ handleTouchEvent((TouchEvent) e);
+ break;
case Event.MOUSE:
handleMouseEvent((MouseEvent) e);
break;
@@ -2228,7 +2133,7 @@ protected void handleMouseEvent(MouseEvent event) {
// }
// Get the (already processed) button code
-// mouseButton = event.getButton();
+ mouseButton = event.getButton();
// Added in 0215 (2.0b7) so that pmouseX/Y behave more like one would
// expect from the desktop. This makes the ContinousLines example behave.
@@ -2302,377 +2207,192 @@ protected void handleMouseEvent(MouseEvent event) {
}
- /*
- // ******************** NOT CURRENTLY IN USE ********************
- // this class is hiding inside PApplet for now,
- // until I have a chance to get the API right inside TouchEvent.
- class AndroidTouchEvent extends TouchEvent {
- int action;
- int numPointers;
- float[] motionX, motionY;
- float[] motionPressure;
- int[] mouseX, mouseY;
-
- AndroidTouchEvent(Object nativeObject, long millis, int action, int modifiers) {
- super(nativeObject, millis, action, modifiers);
- }
-
-// void setAction(int action) {
-// this.action = action;
-// }
+ protected void handleTouchEvent(TouchEvent event) {
+ touches = event.getTouches(touches);
- void setNumPointers(int n) {
- numPointers = n;
- motionX = new float[n];
- motionY = new float[n];
- motionPressure = new float[n];
- mouseX = new int[n];
- mouseY = new int[n];
- }
-
- void setPointers(MotionEvent event) {
- for (int ptIdx = 0; ptIdx < numPointers; ptIdx++) {
- motionX[ptIdx] = event.getX(ptIdx);
- motionY[ptIdx] = event.getY(ptIdx);
- motionPressure[ptIdx] = event.getPressure(ptIdx); // should this be constrained?
- mouseX[ptIdx] = (int) motionX[ptIdx]; //event.getRawX();
- mouseY[ptIdx] = (int) motionY[ptIdx]; //event.getRawY();
- }
+ switch (event.getAction()) {
+ case TouchEvent.START:
+ touchIsStarted = true;
+ break;
+ case TouchEvent.END:
+ touchIsStarted = false;
+ break;
}
- // Sets the pointers for the historical event histIdx
- void setPointers(MotionEvent event, int hisIdx) {
- for (int ptIdx = 0; ptIdx < numPointers; ptIdx++) {
- motionX[ptIdx] = event.getHistoricalX(ptIdx, hisIdx);
- motionY[ptIdx] = event.getHistoricalY(ptIdx, hisIdx);
- motionPressure[ptIdx] = event.getHistoricalPressure(ptIdx, hisIdx); // should this be constrained?
- mouseX[ptIdx] = (int) motionX[ptIdx]; //event.getRawX();
- mouseY[ptIdx] = (int) motionY[ptIdx]; //event.getRawY();
- }
+ handleMethods("touchEvent", new Object[] { event });
+
+ switch (event.getAction()) {
+ case TouchEvent.START:
+ touchStarted(event);
+ break;
+ case TouchEvent.END:
+ touchEnded(event);
+ break;
+ case TouchEvent.MOVE:
+ touchMoved(event);
+ break;
+ case TouchEvent.CANCEL:
+ touchCancelled(event);
+ break;
}
}
-// Object motionLock = new Object();
-// AndroidTouchEvent[] motionEventQueue;
-// int motionEventCount;
-//
-// protected void enqueueMotionEvent(MotionEvent event) {
-// synchronized (motionLock) {
-// // on first run, allocate array for motion events
-// if (motionEventQueue == null) {
-// motionEventQueue = new AndroidTouchEvent[20];
-// for (int i = 0; i < motionEventQueue.length; i++) {
-// motionEventQueue[i] = new AndroidTouchEvent();
-// }
-// }
-// // allocate more PMotionEvent objects if we're out
-// int historyCount = event.getHistorySize();
-//// println("motion: " + motionEventCount + " " + historyCount + " " + motionEventQueue.length);
-// if (motionEventCount + historyCount >= motionEventQueue.length) {
-// int atLeast = motionEventCount + historyCount + 1;
-// AndroidTouchEvent[] temp = new AndroidTouchEvent[max(atLeast, motionEventCount << 1)];
-// if (PApplet.DEBUG) {
-// println("motion: " + motionEventCount + " " + historyCount + " " + motionEventQueue.length);
-// println("allocating " + temp.length + " entries for motion events");
-// }
-// System.arraycopy(motionEventQueue, 0, temp, 0, motionEventCount);
-// motionEventQueue = temp;
-// for (int i = motionEventCount; i < motionEventQueue.length; i++) {
-// motionEventQueue[i] = new AndroidTouchEvent();
-// }
-// }
-//
-// // this will be the last event in the list
-// AndroidTouchEvent pme = motionEventQueue[motionEventCount + historyCount];
-// pme.setAction(event.getAction());
-// pme.setNumPointers(event.getPointerCount());
-// pme.setPointers(event);
-//
-// // historical events happen before the 'current' values
-// if (pme.action == MotionEvent.ACTION_MOVE && historyCount > 0) {
-// for (int i = 0; i < historyCount; i++) {
-// AndroidTouchEvent hist = motionEventQueue[motionEventCount++];
-// hist.setAction(event.getAction());
-// hist.setNumPointers(event.getPointerCount());
-// hist.setPointers(event, i);
-// }
-// }
-//
-// // now step over the last one that we used to assign 'pme'
-// // if historyCount is 0, this just steps over the last
-// motionEventCount++;
-// }
-// }
-//
-//
-// protected void dequeueMotionEvents() {
-// synchronized (motionLock) {
-// for (int i = 0; i < motionEventCount; i++) {
-// handleMotionEvent(motionEventQueue[i]);
-// }
-// motionEventCount = 0;
-// }
-// }
-
-
- // ******************** NOT CURRENTLY IN USE ********************
- // Take action based on a motion event.
- // Internally updates mouseX, mouseY, mousePressed, and mouseEvent.
- // Then it calls the event type with no params,
- // i.e. mousePressed() or mouseReleased() that the user may have
- // overloaded to do something more useful.
- protected void handleMotionEvent(AndroidTouchEvent pme) {
- pmotionX = emotionX;
- pmotionY = emotionY;
- motionX = pme.motionX[0];
- motionY = pme.motionY[0];
- motionPressure = pme.motionPressure[0];
-
- // replace previous mouseX/Y with the last from the event handlers
- pmouseX = emouseX;
- pmouseY = emouseY;
- mouseX = pme.mouseX[0];
- mouseY = pme.mouseY[0];
-
- // *** because removed from PApplet
- boolean firstMotion = false;
- // this used to only be called on mouseMoved and mouseDragged
- // change it back if people run into trouble
- if (firstMotion) {
- pmouseX = mouseX;
- pmouseY = mouseY;
- dmouseX = mouseX; // set it as the first value to be used inside draw() too
- dmouseY = mouseY;
-
- pmotionX = motionX;
- pmotionY = motionY;
- dmotionX = motionX;
- dmotionY = motionY;
- firstMotion = false;
+ /**
+ * Figure out how to process a mouse event. When loop() has been
+ * called, the events will be queued up until drawing is complete.
+ * If noLoop() has been called, then events will happen immediately.
+ */
+ protected void nativeMotionEvent(MotionEvent motionEvent) {
+ int metaState = motionEvent.getMetaState();
+ int modifiers = 0;
+ if ((metaState & android.view.KeyEvent.META_SHIFT_ON) != 0) {
+ modifiers |= Event.SHIFT;
}
-
- // TODO implement method handling for registry of motion/mouse events
-// MouseEvent me = new MouseEvent(nativeObject, millis, action, modifiers, x, y, button, clickCount);
-// handleMethods("mouseEvent", new Object[] { mouseEvent });
-// handleMethods("motionEvent", new Object[] { motionEvent });
-
- if (ppointersX.length < numPointers) {
- ppointersX = new float[numPointers];
- ppointersY = new float[numPointers];
- ppointersPressure = new float[numPointers];
+ if ((metaState & android.view.KeyEvent.META_CTRL_ON) != 0) {
+ modifiers |= Event.CTRL;
+ }
+ if ((metaState & android.view.KeyEvent.META_META_ON) != 0) {
+ modifiers |= Event.META;
+ }
+ if ((metaState & android.view.KeyEvent.META_ALT_ON) != 0) {
+ modifiers |= Event.ALT;
}
- arrayCopy(pointersX, ppointersX);
- arrayCopy(pointersY, ppointersY);
- arrayCopy(pointersPressure, ppointersPressure);
- numPointers = pme.numPointers;
- if (pointersX.length < numPointers) {
- pointersX = new float[numPointers];
- pointersY = new float[numPointers];
- pointersPressure = new float[numPointers];
+ int button;
+ int state = motionEvent.getButtonState();
+ switch (state) {
+ case MotionEvent.BUTTON_PRIMARY:
+ button = LEFT;
+ break;
+ case MotionEvent.BUTTON_SECONDARY:
+ button = RIGHT;
+ break;
+ case MotionEvent.BUTTON_TERTIARY:
+ button = CENTER;
+ break;
+ default:
+ // Covers the BUTTON_FORWARD, BUTTON_BACK,
+ // BUTTON_STYLUS_PRIMARY, and BUTTON_STYLUS_SECONDARY
+ button = state;
}
- arrayCopy(pme.motionX, pointersX);
- arrayCopy(pme.motionY, pointersY);
- arrayCopy(pme.motionPressure, pointersPressure);
- // Triggering the appropriate event methods
- if (pme.action == MotionEvent.ACTION_DOWN || (!mousePressed && numPointers == 1)) {
- // First pointer is down
- mousePressed = true;
- onePointerGesture = true;
- twoPointerGesture = false;
- downMillis = millis();
- downX = pointersX[0];
- downY = pointersY[0];
-
- mousePressed();
- pressEvent();
-
- } else if ((pme.action == MotionEvent.ACTION_POINTER_DOWN && numPointers == 2) ||
- (pme.action == MotionEvent.ACTION_POINTER_2_DOWN) || // 2.3 seems to use this action constant (supposedly deprecated) instead of ACTION_POINTER_DOWN
- (pnumPointers == 1 && numPointers == 2)) { // 2.1 just uses MOVE as the action constant, so the only way to know we have a new pointer is to compare the counters.
-
- // An additional pointer is down (we keep track of multitouch only for 2 pointers)
- onePointerGesture = false;
- twoPointerGesture = true;
-
- } else if ((pme.action == MotionEvent.ACTION_POINTER_UP && numPointers == 2) ||
- (pme.action == MotionEvent.ACTION_POINTER_2_UP) || // 2.1 doesn't use the ACTION_POINTER_UP constant, but this one, apparently deprecated in newer versions of the SDK.
- (twoPointerGesture && numPointers < 2)) { // Sometimes it seems that it doesn't generate the up event.
- // A previously detected pointer is up
-
- twoPointerGesture = false; // Not doing a 2-pointer gesture anymore, but neither a 1-pointer.
-
- } else if (pme.action == MotionEvent.ACTION_MOVE) {
- // Pointer motion
-
- if (onePointerGesture) {
- if (mousePressed) {
- mouseDragged();
- dragEvent();
- } else { // TODO is this physically possible? (perhaps with alt input devices...)
- mouseMoved();
- moveEvent();
- }
- } else if (twoPointerGesture) {
- float d0 = PApplet.dist(ppointersX[0], ppointersY[0], ppointersX[1], ppointersY[1]);
- float d1 = PApplet.dist(pointersX[0], pointersY[0], pointersX[1], pointersY[1]);
+ enqueueMouseEvents(motionEvent, button, modifiers);
+ enqueueTouchEvents(motionEvent, button, modifiers);
+ }
- if (0 < d0 && 0 < d1) {
- float centerX = 0.5f * (pointersX[0] + pointersX[1]);
- float centerY = 0.5f * (pointersY[0] + pointersY[1]);
- zoomEvent(centerX, centerY, d0, d1);
- }
- }
+ protected void enqueueTouchEvents(MotionEvent event, int button, int modifiers) {
+ int actionMasked = event.getActionMasked();
+ int pAction = 0;
+ int pointerUp = 0;
+ int pointerUpIdx = -1;
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ pAction = TouchEvent.START;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ pAction = TouchEvent.START;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ pAction = TouchEvent.MOVE;
+ break;
+ case MotionEvent.ACTION_UP:
+ pAction = TouchEvent.END;
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ pAction = TouchEvent.END;
+ pointerUp = 1;
+ // We get the index of the pointer that is being released:
+ // https://developer.android.com/reference/android/view/MotionEvent#getActionIndex()
+ pointerUpIdx = event.getActionIndex();
+ break;
+ default:
+ // Covers any other action value, including ACTION_CANCEL
+ pAction = TouchEvent.CANCEL;
+ break;
+ }
- } else if (pme.action == MotionEvent.ACTION_UP) {
- // Final pointer is up
- mousePressed = false;
+ if (pAction == TouchEvent.START || pAction == TouchEvent.END || pAction == TouchEvent.CANCEL) {
+ touchPointerId = event.getPointerId(0);
+ }
- float upX = pointersX[0];
- float upY = pointersY[0];
- float gestureLength = PApplet.dist(downX, downY, upX, upY);
-
- int upMillis = millis();
- int gestureTime = upMillis - downMillis;
-
- if (onePointerGesture) {
- // First, lets determine if this 1-pointer event is a tap
- boolean tap = gestureLength <= MAX_TAP_DISP && gestureTime <= MAX_TAP_DURATION;
- if (tap) {
- mouseClicked();
- tapEvent(downX, downY);
- } else if (MIN_SWIPE_LENGTH <= gestureLength && gestureTime <= MAX_SWIPE_DURATION) {
- mouseReleased();
- swipeEvent(downX, downY, upX, upY);
- } else {
- mouseReleased();
- releaseEvent();
- }
+ // getPointerCount() will return the count including the pointer that is being released, so
+ // we substract 1 if if this current event is a pointer up
+ int activePointerCount = event.getPointerCount() - pointerUp;
- } else {
- mouseReleased();
- releaseEvent();
+ if (actionMasked == MotionEvent.ACTION_MOVE) {
+ // Post historical movement events, if any.
+ int historySize = event.getHistorySize();
+ for (int h = 0; h < historySize; h++) {
+ TouchEvent touchEvent = new TouchEvent(event, event.getHistoricalEventTime(h),
+ pAction, modifiers, button);
+ touchEvent.setNumPointers(activePointerCount);
+ int p = 0;
+ for (int idx = 0; idx < event.getPointerCount(); idx++) {
+ if (idx == pointerUpIdx) continue; // Skip the released pointer
+ touchEvent.setPointer(p++, event.getPointerId(idx), event.getHistoricalX(idx, h), event.getHistoricalY(idx, h),
+ event.getHistoricalSize(idx, h), event.getHistoricalPressure(idx, h));
+ }
+ postEvent(touchEvent);
}
- onePointerGesture = twoPointerGesture = false;
-
- } else if (pme.action == MotionEvent.ACTION_CANCEL) {
- // Current gesture is canceled.
- onePointerGesture = twoPointerGesture = false;
- mousePressed = false;
- mouseReleased();
- releaseEvent();
-
- } else {
- //System.out.println("Unknown MotionEvent action: " + action);
}
- pnumPointers = numPointers;
-
- if (pme.action == MotionEvent.ACTION_MOVE) {
- emotionX = motionX;
- emotionY = motionY;
- emouseX = mouseX;
- emouseY = mouseY;
+ // Current event
+ TouchEvent touchEvent = new TouchEvent(event, event.getEventTime(),
+ pAction, modifiers, button);
+ if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL) {
+ // Last pointer up
+ touchEvent.setNumPointers(0);
+ } else {
+ // We still have some pointers left
+ touchEvent.setNumPointers(activePointerCount);
+ int p = 0;
+ for (int idx = 0; idx < event.getPointerCount(); idx++) {
+ if (idx == pointerUpIdx) continue; // Skip the released pointer
+ touchEvent.setPointer(p++, event.getPointerId(idx), event.getX(idx), event.getY(idx),
+ event.getSize(idx), event.getPressure(idx));
+ }
}
+ postEvent(touchEvent);
}
- */
-
-
- // Added in API 11, so defined here: Constant Value: 4096 (0x00001000)
- static final int META_CTRL_ON = 4096;
- // Added in API 11, so defined here: 65536 (0x00010000)
- static final int META_META_ON = 65536;
-
- int motionPointerId;
-
-
- /**
- * Figure out how to process a mouse event. When loop() has been
- * called, the events will be queued up until drawing is complete.
- * If noLoop() has been called, then events will happen immediately.
- */
- protected void nativeMotionEvent(android.view.MotionEvent motionEvent) {
-// enqueueMotionEvent(event);
-//
-// // this will be the last event in the list
-// AndroidTouchEvent pme = motionEventQueue[motionEventCount + historyCount];
-// pme.setAction(event.getAction());
-// pme.setNumPointers(event.getPointerCount());
-// pme.setPointers(event);
-//
-// // historical events happen before the 'current' values
-// if (pme.action == MotionEvent.ACTION_MOVE && historyCount > 0) {
-// for (int i = 0; i < historyCount; i++) {
-// AndroidTouchEvent hist = motionEventQueue[motionEventCount++];
-// hist.setAction(event.getAction());
-// hist.setNumPointers(event.getPointerCount());
-// hist.setPointers(event, i);
-// }
-// }
-
- // ACTION_HOVER_ENTER and ACTION_HOVER_EXIT are passed into
- // onGenericMotionEvent(android.view.MotionEvent)
- // if we want to implement mouseEntered/Exited
- // http://developer.android.com/reference/android/view/MotionEvent.html
- // http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
- // http://www.techrepublic.com/blog/app-builder/use-androids-gesture-detector-to-translate-a-swipe-into-an-event/1577
- int metaState = motionEvent.getMetaState();
- int modifiers = 0;
- if ((metaState & android.view.KeyEvent.META_SHIFT_ON) != 0) {
- modifiers |= Event.SHIFT;
- }
- if ((metaState & META_CTRL_ON) != 0) {
- modifiers |= Event.CTRL;
- }
- if ((metaState & META_META_ON) != 0) {
- modifiers |= Event.META;
- }
- if ((metaState & android.view.KeyEvent.META_ALT_ON) != 0) {
- modifiers |= Event.ALT;
- }
+ protected void enqueueMouseEvents(MotionEvent event, int button, int modifiers) {
+ int actionMasked = event.getActionMasked();
int clickCount = 1; // not really set... (i.e. not catching double taps)
int index;
- // MotionEvent.html -> getButtonState() does BUTTON_PRIMARY, SECONDARY, TERTIARY
- // use this for left/right/etc
- switch (motionEvent.getAction()) {
+ switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
- motionPointerId = motionEvent.getPointerId(0);
- postEvent(new MouseEvent(motionEvent, motionEvent.getEventTime(),
+ mousePointerId = event.getPointerId(0);
+ postEvent(new MouseEvent(event, event.getEventTime(),
MouseEvent.PRESS, modifiers,
- (int) motionEvent.getX(), (int) motionEvent.getY(),
- LEFT, clickCount));
+ (int) event.getX(), (int) event.getY(),
+ button, clickCount));
break;
case MotionEvent.ACTION_MOVE:
-// int historySize = motionEvent.getHistorySize();
- index = motionEvent.findPointerIndex(motionPointerId);
+ index = event.findPointerIndex(mousePointerId);
if (index != -1) {
- postEvent(new MouseEvent(motionEvent, motionEvent.getEventTime(),
+ postEvent(new MouseEvent(event, event.getEventTime(),
MouseEvent.DRAG, modifiers,
- (int) motionEvent.getX(index), (int) motionEvent.getY(index),
- LEFT, clickCount));
+ (int) event.getX(index), (int) event.getY(index),
+ button, clickCount));
}
break;
case MotionEvent.ACTION_UP:
- index = motionEvent.findPointerIndex(motionPointerId);
+ index = event.findPointerIndex(mousePointerId);
if (index != -1) {
- postEvent(new MouseEvent(motionEvent, motionEvent.getEventTime(),
+ postEvent(new MouseEvent(event, event.getEventTime(),
MouseEvent.RELEASE, modifiers,
- (int) motionEvent.getX(index), (int) motionEvent.getY(index),
- LEFT, clickCount));
+ (int) event.getX(index), (int) event.getY(index),
+ button, clickCount));
}
break;
}
- //postEvent(pme);
}
-
public void mousePressed() { }
@@ -2733,25 +2453,36 @@ public void mouseExited(MouseEvent event) {
}
+ public void touchStarted() { }
- //////////////////////////////////////////////////////////////
- // unfinished API, do not use
+ public void touchStarted(TouchEvent event) {
+ touchStarted();
+ }
-// protected void pressEvent() { }
-//
-// protected void dragEvent() { }
-//
-// protected void moveEvent() { }
-//
-// protected void releaseEvent() { }
-//
-// protected void zoomEvent(float x, float y, float d0, float d1) { }
-//
-// protected void tapEvent(float x, float y) { }
-//
-// protected void swipeEvent(float x0, float y0, float x1, float y1) { }
+ public void touchMoved() { }
+
+
+ public void touchMoved(TouchEvent event) {
+ touchMoved();
+ }
+
+
+ public void touchEnded() { }
+
+
+ public void touchEnded(TouchEvent event) {
+ touchEnded();
+ }
+
+
+ public void touchCancelled() { }
+
+
+ public void touchCancelled(TouchEvent event) {
+ touchCancelled();
+ }
//////////////////////////////////////////////////////////////
@@ -2780,6 +2511,10 @@ public void mouseExited(MouseEvent event) {
protected void handleKeyEvent(KeyEvent event) {
+
+ // Get rid of auto-repeating keys if desired and supported
+ if (!keyRepeatEnabled && event.isAutoRepeat()) return;
+
// keyEvent = event;
key = event.getKey();
keyCode = event.getKeyCode();
@@ -2799,12 +2534,6 @@ protected void handleKeyEvent(KeyEvent event) {
}
- @Override
- public void onBackPressed() {
- exit();
- }
-
-
protected void nativeKeyEvent(android.view.KeyEvent event) {
// event.isPrintingKey() returns false for whitespace and others,
// which is a problem if the space bar or tab key are used.
@@ -2828,12 +2557,33 @@ protected void nativeKeyEvent(android.view.KeyEvent event) {
int keModifiers = 0;
KeyEvent ke = new KeyEvent(event, event.getEventTime(),
- keAction, keModifiers, key, keyCode);
+ keAction, keModifiers, key, keyCode, 0 < event.getRepeatCount());
postEvent(ke);
}
+ public void openKeyboard() {
+ Context context = surface.getContext();
+ InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,0);
+ keyboardIsOpen = true;
+ }
+
+
+ public void closeKeyboard() {
+ if (keyboardIsOpen) {
+ Context context = surface.getContext();
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
+ keyboardIsOpen = false;
+ if (parentLayout == -1) {
+ setFullScreenVisibility();
+ }
+ }
+ }
+
+
public void keyPressed() { }
@@ -2853,14 +2603,10 @@ public void keyReleased(KeyEvent event) {
}
- /**
- * Never currently called (does not exist) on Android.
- * http://code.google.com/p/processing/issues/detail?id=1489
- */
public void keyTyped() { }
- public void keyTyped(KeyEvent event ) {
+ public void keyTyped(KeyEvent event) {
keyTyped();
}
@@ -2889,8 +2635,6 @@ public void focusLost() { }
// getting the time
- static protected Time time = new Time();
-
/**
* Get the number of milliseconds since the applet started.
*
@@ -2903,16 +2647,12 @@ public int millis() {
/** Seconds position of the current time. */
static public int second() {
- //return Calendar.getInstance().get(Calendar.SECOND);
- time.setToNow();
- return time.second;
+ return Calendar.getInstance().get(Calendar.SECOND);
}
/** Minutes position of the current time. */
static public int minute() {
- //return Calendar.getInstance().get(Calendar.MINUTE);
- time.setToNow();
- return time.minute;
+ return Calendar.getInstance().get(Calendar.MINUTE);
}
/**
@@ -2923,9 +2663,7 @@ static public int minute() {
* if (yankeeHour == 0) yankeeHour = 12;
*/
static public int hour() {
- //return Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
- time.setToNow();
- return time.hour;
+ return Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
}
/**
@@ -2935,9 +2673,7 @@ static public int hour() {
* or day of the year (1..365) then use java's Calendar.get()
*/
static public int day() {
- //return Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
- time.setToNow();
- return time.monthDay;
+ return Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
}
/**
@@ -2945,18 +2681,14 @@ static public int day() {
*/
static public int month() {
// months are number 0..11 so change to colloquial 1..12
- //return Calendar.getInstance().get(Calendar.MONTH) + 1;
- time.setToNow();
- return time.month + 1;
+ return Calendar.getInstance().get(Calendar.MONTH) + 1;
}
/**
* Get the current year.
*/
static public int year() {
- //return Calendar.getInstance().get(Calendar.YEAR);
- time.setToNow();
- return time.year;
+ return Calendar.getInstance().get(Calendar.YEAR);
}
@@ -3002,10 +2734,12 @@ public void delay(int napTime) {
*
* ( end auto-generated )
*/
- public void frameRate(float newRateTarget) {
- frameRateTarget = newRateTarget;
- frameRatePeriod = (long) (1000000000.0 / frameRateTarget);
- g.setFrameRate(newRateTarget);
+ public void frameRate(float fps) {
+//
+// frameRateTarget = newRateTarget;
+// frameRatePeriod = (long) (1000000000.0 / frameRateTarget);
+// g.setFrameRate(newRateTarget);
+ surface.setFrameRate(fps);
}
@@ -3060,7 +2794,7 @@ public void link(String here) {
*/
public void link(String url, String frameTitle) {
Intent viewIntent = new Intent("android.intent.action.VIEW", Uri.parse(url));
- startActivity(viewIntent);
+ surface.startActivity(viewIntent);
}
@@ -3087,7 +2821,6 @@ static public Process exec(String[] argv) {
try {
return Runtime.getRuntime().exec(argv);
} catch (Exception e) {
- e.printStackTrace();
throw new RuntimeException("Could not open " + join(argv, ' '));
}
}
@@ -3096,6 +2829,15 @@ static public Process exec(String[] argv) {
//////////////////////////////////////////////////////////////
+ /**
+ * Better way of handling e.printStackTrace() calls so that they can be
+ * handled by subclasses as necessary.
+ */
+ protected void printStackTrace(Throwable t) {
+ t.printStackTrace();
+ }
+
+
/**
* Function for an applet/application to kill itself and
* display an error. Mostly this is here to be improved later.
@@ -3116,42 +2858,22 @@ public void die(String what, Exception e) {
/**
- * Call to safely exit the sketch when finished. For instance,
- * to render a single frame, save it, and quit.
+ * Conveniency method so perform initialization tasks when the activity is
+ * created, while avoiding the ackward call to onCreate() with the bundle
+ * and super.onCreate().
*/
- public void exit() {
-// println("exit() called");
- if (thread == null) {
- // exit immediately, stop() has already been called,
- // meaning that the main thread has long since exited
- exit2();
-
- } else if (looping) {
- // stop() will be called as the thread exits
- finished = true;
- // tell the code to call exit2() to do a System.exit()
- // once the next draw() has completed
- exitCalled = true;
-
- } else if (!looping) {
- // if not looping, shut down things explicitly,
- // because the main thread will be sleeping
- dispose();
+ public void create() {
- // now get out
- exit2();
- }
}
-
- void exit2() {
- try {
- System.exit(0);
- } catch (SecurityException e) {
- // don't care about applet security exceptions
- }
+ /**
+ * Should trigger a graceful activity/service shutdown (calling onPause/onStop, etc).
+ */
+ public void exit() {
+ surface.finish();
}
+
/**
* Called to dispose of resources and shut down the sketch.
* Destroys the thread, dispose the renderer, and notify listeners.
@@ -3163,12 +2885,15 @@ final public void dispose() {
// moved here from stop()
finished = true; // let the sketch know it is shut down time
- // don't run stop and disposers twice
- if (thread == null) return;
- thread = null;
-
// call to shut down renderer, in case it needs it (pdf does)
- if (g != null) g.dispose();
+ if (surface != null) {
+ surface.stopThread();
+ surface.dispose();
+ }
+ if (g != null) {
+ g.clearState(); // This should probably go in dispose, but for the time being...
+ g.dispose();
+ }
handleMethods("dispose");
}
@@ -3191,16 +2916,16 @@ public void method(String name) {
method.invoke(this, new Object[] { });
} catch (IllegalArgumentException e) {
- e.printStackTrace();
+ printStackTrace(e);
} catch (IllegalAccessException e) {
- e.printStackTrace();
+ printStackTrace(e);
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
} catch (NoSuchMethodException nsme) {
System.err.println("There is no public " + name + "() method " +
"in the class " + getClass().getName());
} catch (Exception e) {
- e.printStackTrace();
+ printStackTrace(e);
}
}
@@ -3422,6 +3147,25 @@ static public void print(String what) {
System.out.flush();
}
+ /**
+ * @param variables list of data, separated by commas
+ */
+ static public void print(Object... variables) {
+ StringBuilder sb = new StringBuilder();
+ for (Object o : variables) {
+ if (sb.length() != 0) {
+ sb.append(" ");
+ }
+ if (o == null) {
+ sb.append("null");
+ } else {
+ sb.append(o.toString());
+ }
+ }
+ System.out.print(sb.toString());
+ }
+
+ /*
static public void print(Object what) {
if (what == null) {
// special case since this does fuggly things on > 1.1
@@ -3430,6 +3174,7 @@ static public void print(Object what) {
System.out.println(what.toString());
}
}
+ */
//
@@ -3463,6 +3208,15 @@ static public void println(String what) {
print(what); System.out.println();
}
+ /**
+ * @param variables list of data, separated by commas
+ */
+ static public void println(Object... variables) {
+// System.out.println("got " + variables.length + " variables");
+ print(variables);
+ println();
+ }
+
static public void println(Object what) {
if (what == null) {
// special case since this does fuggly things on > 1.1
@@ -3543,6 +3297,100 @@ static public void println(Object what) {
}
}
+
+/**
+ * @webref output:text_area
+ * @param what one-dimensional array
+ * @usage IDE
+ * @see PApplet#print(byte)
+ * @see PApplet#println()
+ */
+ static public void printArray(Object what) {
+ if (what == null) {
+ // special case since this does fuggly things on > 1.1
+ System.out.println("null");
+
+ } else {
+ String name = what.getClass().getName();
+ if (name.charAt(0) == '[') {
+ switch (name.charAt(1)) {
+ case '[':
+ // don't even mess with multi-dimensional arrays (case '[')
+ // or anything else that's not int, float, boolean, char
+ System.out.println(what);
+ break;
+
+ case 'L':
+ // print a 1D array of objects as individual elements
+ Object poo[] = (Object[]) what;
+ for (int i = 0; i < poo.length; i++) {
+ if (poo[i] instanceof String) {
+ System.out.println("[" + i + "] \"" + poo[i] + "\"");
+ } else {
+ System.out.println("[" + i + "] " + poo[i]);
+ }
+ }
+ break;
+
+ case 'Z': // boolean
+ boolean zz[] = (boolean[]) what;
+ for (int i = 0; i < zz.length; i++) {
+ System.out.println("[" + i + "] " + zz[i]);
+ }
+ break;
+
+ case 'B': // byte
+ byte bb[] = (byte[]) what;
+ for (int i = 0; i < bb.length; i++) {
+ System.out.println("[" + i + "] " + bb[i]);
+ }
+ break;
+
+ case 'C': // char
+ char cc[] = (char[]) what;
+ for (int i = 0; i < cc.length; i++) {
+ System.out.println("[" + i + "] '" + cc[i] + "'");
+ }
+ break;
+
+ case 'I': // int
+ int ii[] = (int[]) what;
+ for (int i = 0; i < ii.length; i++) {
+ System.out.println("[" + i + "] " + ii[i]);
+ }
+ break;
+
+ case 'J': // int
+ long jj[] = (long[]) what;
+ for (int i = 0; i < jj.length; i++) {
+ System.out.println("[" + i + "] " + jj[i]);
+ }
+ break;
+
+ case 'F': // float
+ float ff[] = (float[]) what;
+ for (int i = 0; i < ff.length; i++) {
+ System.out.println("[" + i + "] " + ff[i]);
+ }
+ break;
+
+ case 'D': // double
+ double dd[] = (double[]) what;
+ for (int i = 0; i < dd.length; i++) {
+ System.out.println("[" + i + "] " + dd[i]);
+ }
+ break;
+
+ default:
+ System.out.println(what);
+ }
+ } else { // not an array
+ System.out.println(what);
+ }
+ }
+ System.out.flush();
+ }
+
//
/*
@@ -3815,54 +3663,110 @@ static public final float map(float value,
Random internalRandom;
/**
- * Return a random number in the range [0, howbig).
- *
- * The number returned will range from zero up to
- * (but not including) 'howbig'.
+ *
*/
- public final float random(float howbig) {
+ public final float random(float high) {
+ // avoid an infinite loop when 0 or NaN are passed in
+ if (high == 0 || high != high) {
+ return 0;
+ }
+
+ if (internalRandom == null) {
+ internalRandom = new Random();
+ }
+
// for some reason (rounding error?) Math.random() * 3
// can sometimes return '3' (once in ~30 million tries)
// so a check was added to avoid the inclusion of 'howbig'
+ float value = 0;
+ do {
+ value = internalRandom.nextFloat() * high;
+ } while (value == high);
+ return value;
+ }
- // avoid an infinite loop
- if (howbig == 0) return 0;
+ /**
+ * ( begin auto-generated from randomGaussian.xml )
+ *
+ * Returns a float from a random series of numbers having a mean of 0
+ * and standard deviation of 1. Each time the randomGaussian()
+ * function is called, it returns a number fitting a Gaussian, or
+ * normal, distribution. There is theoretically no minimum or maximum
+ * value that randomGaussian() might return. Rather, there is
+ * just a very low probability that values far from the mean will be
+ * returned; and a higher probability that numbers near the mean will
+ * be returned.
+ *
+ * ( end auto-generated )
+ * @webref math:random
+ * @see PApplet#random(float,float)
+ * @see PApplet#noise(float, float, float)
+ */
+ public final float randomGaussian() {
+ if (internalRandom == null) {
+ internalRandom = new Random();
+ }
+ return (float) internalRandom.nextGaussian();
+ }
- // internal random number object
- if (internalRandom == null) internalRandom = new Random();
+ /**
+ * ( begin auto-generated from random.xml )
+ *
+ * Generates random numbers. Each time the random() function is
+ * called, it returns an unexpected value within the specified range. If
+ * one parameter is passed to the function it will return a float
+ * between zero and the value of the high parameter. The function
+ * call random(5) returns values between 0 and 5 (starting at zero,
+ * up to but not including 5). If two parameters are passed, it will return
+ * a float with a value between the the parameters. The function
+ * call random(-5, 10.2) returns values starting at -5 up to (but
+ * not including) 10.2. To convert a floating-point random number to an
+ * integer, use the int() function.
+ *
+ * ( end auto-generated )
+ * @webref math:random
+ * @param low lower limit
+ * @param high upper limit
+ * @see PApplet#randomSeed(long)
+ * @see PApplet#noise(float, float, float)
+ */
+ public final float random(float low, float high) {
+ if (low >= high) return low;
+ float diff = high - low;
float value = 0;
+ // because of rounding error, can't just add low, otherwise it may hit high
+ // https://github.com/processing/processing/issues/4551
do {
- //value = (float)Math.random() * howbig;
- value = internalRandom.nextFloat() * howbig;
- } while (value == howbig);
+ value = random(diff) + low;
+ } while (value == high);
return value;
}
/**
- * Return a random number in the range [howsmall, howbig).
- *
- * The number returned will range from 'howsmall' up to
- * (but not including 'howbig'.
- *
- * If howsmall is >= howbig, howsmall will be returned,
- * meaning that random(5, 5) will return 5 (useful)
- * and random(7, 4) will return 7 (not useful.. better idea?)
+ * ( begin auto-generated from randomSeed.xml )
+ *
+ * Sets the seed value for random() . By default, random()
+ * produces different results each time the program is run. Set the
+ * value parameter to a constant to return the same pseudo-random
+ * numbers each time the software is run.
+ *
+ * ( end auto-generated )
+ * @webref math:random
+ * @param seed seed value
+ * @see PApplet#random(float,float)
+ * @see PApplet#noise(float, float, float)
+ * @see PApplet#noiseSeed(long)
*/
- public final float random(float howsmall, float howbig) {
- if (howsmall >= howbig) return howsmall;
- float diff = howbig - howsmall;
- return random(diff) + howsmall;
+ public final void randomSeed(long seed) {
+ if (internalRandom == null) {
+ internalRandom = new Random();
+ }
+ internalRandom.setSeed(seed);
}
- public final void randomSeed(long what) {
- // internal random number object
- if (internalRandom == null) internalRandom = new Random();
- internalRandom.setSeed(what);
- }
-
//////////////////////////////////////////////////////////////
@@ -3899,7 +3803,6 @@ public final void randomSeed(long what) {
/**
- * Computes the Perlin noise function value at point x.
*/
public float noise(float x) {
// is this legit? it's a dumb way to do it (but repair it later)
@@ -3907,15 +3810,50 @@ public float noise(float x) {
}
/**
- * Computes the Perlin noise function value at the point x, y.
*/
public float noise(float x, float y) {
return noise(x, y, 0f);
}
/**
- * Computes the Perlin noise function value at x, y, z.
- */
+ * ( begin auto-generated from noise.xml )
+ *
+ * Returns the Perlin noise value at specified coordinates. Perlin noise is
+ * a random sequence generator producing a more natural ordered, harmonic
+ * succession of numbers compared to the standard random() function.
+ * It was invented by Ken Perlin in the 1980s and been used since in
+ * graphical applications to produce procedural textures, natural motion,
+ * shapes, terrains etc. The main difference to the
+ * random() function is that Perlin noise is defined in an infinite
+ * n-dimensional space where each pair of coordinates corresponds to a
+ * fixed semi-random value (fixed only for the lifespan of the program).
+ * The resulting value will always be between 0.0 and 1.0. Processing can
+ * compute 1D, 2D and 3D noise, depending on the number of coordinates
+ * given. The noise value can be animated by moving through the noise space
+ * as demonstrated in the example above. The 2nd and 3rd dimension can also
+ * be interpreted as time. The actual noise is structured
+ * similar to an audio signal, in respect to the function's use of
+ * frequencies. Similar to the concept of harmonics in physics, perlin
+ * noise is computed over several octaves which are added together for the
+ * final result. Another way to adjust the character of the
+ * resulting sequence is the scale of the input coordinates. As the
+ * function works within an infinite space the value of the coordinates
+ * doesn't matter as such, only the distance between successive coordinates
+ * does (eg. when using noise() within a loop). As a general rule
+ * the smaller the difference between coordinates, the smoother the
+ * resulting noise sequence will be. Steps of 0.005-0.03 work best for most
+ * applications, but this will differ depending on use.
+ *
+ * ( end auto-generated )
+ *
+ * @webref math:random
+ * @param x x-coordinate in noise space
+ * @param y y-coordinate in noise space
+ * @param z z-coordinate in noise space
+ * @see PApplet#noiseSeed(long)
+ * @see PApplet#noiseDetail(int, float)
+ * @see PApplet#random(float,float)
+ */
public float noise(float x, float y, float z) {
if (perlin == null) {
if (perlinRandom == null) {
@@ -3938,9 +3876,9 @@ public float noise(float x, float y, float z) {
if (z<0) z=-z;
int xi=(int)x, yi=(int)y, zi=(int)z;
- float xf = (float)(x-xi);
- float yf = (float)(y-yi);
- float zf = (float)(z-zi);
+ float xf = x - xi;
+ float yf = y - yi;
+ float zf = z - zi;
float rxf, ryf;
float r=0;
@@ -3995,18 +3933,61 @@ private float noise_fsc(float i) {
// for different levels of detail. lower values will produce
// smoother results as higher octaves are surpressed
+ /**
+ * ( begin auto-generated from noiseDetail.xml )
+ *
+ * Adjusts the character and level of detail produced by the Perlin noise
+ * function. Similar to harmonics in physics, noise is computed over
+ * several octaves. Lower octaves contribute more to the output signal and
+ * as such define the overal intensity of the noise, whereas higher octaves
+ * create finer grained details in the noise sequence. By default, noise is
+ * computed over 4 octaves with each octave contributing exactly half than
+ * its predecessor, starting at 50% strength for the 1st octave. This
+ * falloff amount can be changed by adding an additional function
+ * parameter. Eg. a falloff factor of 0.75 means each octave will now have
+ * 75% impact (25% less) of the previous lower octave. Any value between
+ * 0.0 and 1.0 is valid, however note that values greater than 0.5 might
+ * result in greater than 1.0 values returned by noise() . By changing these parameters, the signal created by the noise()
+ * function can be adapted to fit very specific needs and characteristics.
+ *
+ * ( end auto-generated )
+ * @webref math:random
+ * @param lod number of octaves to be used by the noise
+ * @see PApplet#noise(float, float, float)
+ */
public void noiseDetail(int lod) {
if (lod>0) perlin_octaves=lod;
}
+ /**
+ * @see #noiseDetail(int)
+ * @param falloff falloff factor for each octave
+ */
public void noiseDetail(int lod, float falloff) {
if (lod>0) perlin_octaves=lod;
if (falloff>0) perlin_amp_falloff=falloff;
}
- public void noiseSeed(long what) {
+ /**
+ * ( begin auto-generated from noiseSeed.xml )
+ *
+ * Sets the seed value for noise() . By default, noise()
+ * produces different results each time the program is run. Set the
+ * value parameter to a constant to return the same pseudo-random
+ * numbers each time the software is run.
+ *
+ * ( end auto-generated )
+ * @webref math:random
+ * @param seed seed value
+ * @see PApplet#noise(float, float, float)
+ * @see PApplet#noiseDetail(int, float)
+ * @see PApplet#random(float,float)
+ * @see PApplet#randomSeed(long)
+ */
+ public void noiseSeed(long seed) {
if (perlinRandom == null) perlinRandom = new Random();
- perlinRandom.setSeed(what);
+ perlinRandom.setSeed(seed);
// force table reset after changing the random number seed [0122]
perlin = null;
}
@@ -4016,13 +3997,6 @@ public void noiseSeed(long what) {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-// protected String[] loadImageFormats;
-
-
-// public PImage loadImage(String filename) {
-// return loadImage(filename, null);
-// }
-
public PImage loadImage(String filename) { //, Object params) {
// return loadImage(filename, null);
@@ -4043,95 +4017,20 @@ public PImage loadImage(String filename) { //, Object params) {
}
// int much = (int) (System.currentTimeMillis() - t);
// println("loadImage(" + filename + ") was " + nfc(much));
- PImage image = new PImage(bitmap);
- image.parent = this;
-// if (params != null) {
-// image.setParams(g, params);
-// }
- return image;
+ if (bitmap == null) {
+ System.err.println("Could not load the image because the bitmap was empty.");
+ return null;
+ } else {
+ PImage image = new PImage(bitmap);
+ image.parent = this;
+ return image;
+ }
}
- /*
public PImage loadImage(String filename, String extension) {
- if (extension == null) {
- String lower = filename.toLowerCase();
- int dot = filename.lastIndexOf('.');
- if (dot == -1) {
- extension = "unknown"; // no extension found
- }
- extension = lower.substring(dot + 1);
-
- // check for, and strip any parameters on the url, i.e.
- // filename.jpg?blah=blah&something=that
- int question = extension.indexOf('?');
- if (question != -1) {
- extension = extension.substring(0, question);
- }
- }
-
- // just in case. them users will try anything!
- extension = extension.toLowerCase();
-
- if (extension.equals("tga")) {
- try {
- return loadImageTGA(filename);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- if (extension.equals("tif") || extension.equals("tiff")) {
- byte bytes[] = loadBytes(filename);
- return (bytes == null) ? null : PImage.loadTIFF(bytes);
- }
-
- // For jpeg, gif, and png, load them using createImage(),
- // because the javax.imageio code was found to be much slower, see
- // Bug 392 .
- try {
- if (extension.equals("jpg") || extension.equals("jpeg") ||
- extension.equals("gif") || extension.equals("png") ||
- extension.equals("unknown")) {
- byte bytes[] = loadBytes(filename);
- if (bytes == null) {
- return null;
- } else {
- Image awtImage = Toolkit.getDefaultToolkit().createImage(bytes);
- PImage image = loadImageMT(awtImage);
- if (image.width == -1) {
- System.err.println("The file " + filename +
- " contains bad image data, or may not be an image.");
- }
- // if it's a .gif image, test to see if it has transparency
- if (extension.equals("gif") || extension.equals("png")) {
- image.checkAlpha();
- }
- return image;
- }
- }
- } catch (Exception e) {
- // show error, but move on to the stuff below, see if it'll work
- e.printStackTrace();
- }
-
- if (loadImageFormats == null) {
- loadImageFormats = ImageIO.getReaderFormatNames();
- }
- if (loadImageFormats != null) {
- for (int i = 0; i < loadImageFormats.length; i++) {
- if (extension.equals(loadImageFormats[i])) {
- return loadImageIO(filename);
- }
- }
- }
-
- // failed, could not load image after all those attempts
- System.err.println("Could not find a method to load " + filename);
- return null;
+ return loadImage(filename);
}
- */
public PImage requestImage(String filename) {
@@ -4188,6 +4087,10 @@ public void run() {
vessel.pixels = actual.pixels;
// an android, pixels[] will probably be null, we want this one
vessel.bitmap = actual.bitmap;
+
+ vessel.pixelWidth = actual.width;
+ vessel.pixelHeight = actual.height;
+ vessel.pixelDensity = 1;
}
requestImageCount--;
}
@@ -4195,31 +4098,6 @@ public void run() {
- //////////////////////////////////////////////////////////////
-
- // EXTENSIONS
-
-
- /**
- * Get the compression-free extension for this filename.
- * @param filename The filename to check
- * @return an extension, skipping past .gz if it's present
- */
- static public String checkExtension(String filename) {
- // Don't consider the .gz as part of the name, createInput()
- // and createOuput() will take care of fixing that up.
- if (filename.toLowerCase().endsWith(".gz")) {
- filename = filename.substring(0, filename.length() - 3);
- }
- int dotIndex = filename.lastIndexOf('.');
- if (dotIndex != -1) {
- return filename.substring(dotIndex + 1).toLowerCase();
- }
- return null;
- }
-
-
-
//////////////////////////////////////////////////////////////
// DATA I/O
@@ -4229,7 +4107,7 @@ public XML createXML(String name) {
try {
return new XML(name);
} catch (Exception e) {
- e.printStackTrace();
+ printStackTrace(e);
return null;
}
}
@@ -4253,7 +4131,7 @@ public XML loadXML(String filename, String options) {
try {
return new XML(createInput(filename), options);
} catch (Exception e) {
- e.printStackTrace();
+ printStackTrace(e);
return null;
}
}
@@ -4268,7 +4146,7 @@ public XML parseXML(String xmlString, String options) {
try {
return XML.parse(xmlString, options);
} catch (Exception e) {
- e.printStackTrace();
+ printStackTrace(e);
return null;
}
}
@@ -4284,6 +4162,137 @@ public boolean saveXML(XML xml, String filename, String options) {
}
+ /**
+ * @webref input:files
+ * @param input String to parse as a JSONObject
+ * @see PApplet#loadJSONObject(String)
+ * @see PApplet#saveJSONObject(JSONObject, String)
+ */
+ public JSONObject parseJSONObject(String input) {
+ return new JSONObject(new StringReader(input));
+ }
+
+
+ /**
+ * @webref input:files
+ * @param filename name of a file in the data folder or a URL
+ * @see JSONObject
+ * @see JSONArray
+ * @see PApplet#loadJSONArray(String)
+ * @see PApplet#saveJSONObject(JSONObject, String)
+ * @see PApplet#saveJSONArray(JSONArray, String)
+ */
+ public JSONObject loadJSONObject(String filename) {
+ // can't pass of createReader() to the constructor b/c of resource leak
+ BufferedReader reader = createReader(filename);
+ JSONObject outgoing = new JSONObject(reader);
+ try {
+ reader.close();
+ } catch (IOException e) { // not sure what would cause this
+ e.printStackTrace();
+ }
+ return outgoing;
+ }
+
+
+ static public JSONObject loadJSONObject(File file) {
+ // can't pass of createReader() to the constructor b/c of resource leak
+ BufferedReader reader = createReader(file);
+ JSONObject outgoing = new JSONObject(reader);
+ try {
+ reader.close();
+ } catch (IOException e) { // not sure what would cause this
+ e.printStackTrace();
+ }
+ return outgoing;
+ }
+
+
+ /**
+ * @webref output:files
+ * @see JSONObject
+ * @see JSONArray
+ * @see PApplet#loadJSONObject(String)
+ * @see PApplet#loadJSONArray(String)
+ * @see PApplet#saveJSONArray(JSONArray, String)
+ */
+ public boolean saveJSONObject(JSONObject json, String filename) {
+ return saveJSONObject(json, filename, null);
+ }
+
+
+ /**
+ * @nowebref
+ */
+ public boolean saveJSONObject(JSONObject json, String filename, String options) {
+ return json.save(saveFile(filename), options);
+ }
+
+
+ /**
+ * @webref input:files
+ * @param input String to parse as a JSONArray
+ * @see JSONObject
+ * @see PApplet#loadJSONObject(String)
+ * @see PApplet#saveJSONObject(JSONObject, String)
+ */
+ public JSONArray parseJSONArray(String input) {
+ return new JSONArray(new StringReader(input));
+ }
+
+
+ /**
+ * @webref input:files
+ * @param filename name of a file in the data folder or a URL
+ * @see JSONArray
+ * @see PApplet#loadJSONObject(String)
+ * @see PApplet#saveJSONObject(JSONObject, String)
+ * @see PApplet#saveJSONArray(JSONArray, String)
+ */
+ public JSONArray loadJSONArray(String filename) {
+ // can't pass of createReader() to the constructor b/c of resource leak
+ BufferedReader reader = createReader(filename);
+ JSONArray outgoing = new JSONArray(reader);
+ try {
+ reader.close();
+ } catch (IOException e) { // not sure what would cause this
+ e.printStackTrace();
+ }
+ return outgoing;
+ }
+
+
+ static public JSONArray loadJSONArray(File file) {
+ // can't pass of createReader() to the constructor b/c of resource leak
+ BufferedReader reader = createReader(file);
+ JSONArray outgoing = new JSONArray(reader);
+ try {
+ reader.close();
+ } catch (IOException e) { // not sure what would cause this
+ e.printStackTrace();
+ }
+ return outgoing;
+ }
+
+
+ /**
+ * @webref output:files
+ * @see JSONObject
+ * @see JSONArray
+ * @see PApplet#loadJSONObject(String)
+ * @see PApplet#loadJSONArray(String)
+ * @see PApplet#saveJSONObject(JSONObject, String)
+ */
+ public boolean saveJSONArray(JSONArray json, String filename) {
+ return saveJSONArray(json, filename, null);
+ }
+
+
+ public boolean saveJSONArray(JSONArray json, String filename, String options) {
+ return json.save(saveFile(filename), options);
+ }
+
+
public Table createTable() {
return new Table();
}
@@ -4316,7 +4325,7 @@ public Table loadTable(String filename, String options) {
return new Table(createInput(filename), options);
} catch (IOException e) {
- e.printStackTrace();
+ printStackTrace(e);
return null;
}
}
@@ -4332,7 +4341,7 @@ public boolean saveTable(Table table, String filename, String options) {
table.save(saveFile(filename), options);
return true;
} catch (IOException e) {
- e.printStackTrace();
+ printStackTrace(e);
}
return false;
}
@@ -4388,7 +4397,7 @@ public PFont createFont(String name, float size,
Typeface baseFont = null;
if (lowerName.endsWith(".otf") || lowerName.endsWith(".ttf")) {
- AssetManager assets = getBaseContext().getAssets();
+ AssetManager assets = surface.getAssets();
baseFont = Typeface.createFromAsset(assets, name);
} else {
baseFont = (Typeface) PFont.findNative(name);
@@ -4541,6 +4550,156 @@ public PFont createFont(String name, float size,
// }
+ //////////////////////////////////////////////////////////////
+
+ // LISTING DIRECTORIES
+
+
+ public String[] listPaths(String path, String... options) {
+ File[] list = listFiles(path, options);
+
+ int offset = 0;
+ for (String opt : options) {
+ if (opt.equals("relative")) {
+ if (!path.endsWith(File.pathSeparator)) {
+ path += File.pathSeparator;
+ }
+ offset = path.length();
+ break;
+ }
+ }
+ String[] outgoing = new String[list.length];
+ for (int i = 0; i < list.length; i++) {
+ // as of Java 1.8, substring(0) returns the original object
+ outgoing[i] = list[i].getAbsolutePath().substring(offset);
+ }
+ return outgoing;
+ }
+
+
+ public File[] listFiles(String path, String... options) {
+ File file = new File(path);
+ // if not an absolute path, make it relative to the sketch folder
+ if (!file.isAbsolute()) {
+ file = sketchFile(path);
+ }
+ return listFiles(file, options);
+ }
+
+
+ // "relative" -> no effect with the Files version, but important for listPaths
+ // "recursive"
+ // "extension=js" or "extensions=js|csv|txt" (no dot)
+ // "directories" -> only directories
+ // "files" -> only files
+ // "hidden" -> include hidden files (prefixed with .) disabled by default
+ static public File[] listFiles(File base, String... options) {
+ boolean recursive = false;
+ String[] extensions = null;
+ boolean directories = true;
+ boolean files = true;
+ boolean hidden = false;
+
+ for (String opt : options) {
+ if (opt.equals("recursive")) {
+ recursive = true;
+ } else if (opt.startsWith("extension=")) {
+ extensions = new String[] { opt.substring(10) };
+ } else if (opt.startsWith("extensions=")) {
+ extensions = split(opt.substring(10), ',');
+ } else if (opt.equals("files")) {
+ directories = false;
+ } else if (opt.equals("directories")) {
+ files = false;
+ } else if (opt.equals("hidden")) {
+ hidden = true;
+ } else if (opt.equals("relative")) {
+ // ignored
+ } else {
+ throw new RuntimeException(opt + " is not a listFiles() option");
+ }
+ }
+
+ if (extensions != null) {
+ for (int i = 0; i < extensions.length; i++) {
+ extensions[i] = "." + extensions[i];
+ }
+ }
+
+ if (!files && !directories) {
+ // just make "only files" and "only directories" mean... both
+ files = true;
+ directories = true;
+ }
+
+ if (!base.canRead()) {
+ return null;
+ }
+
+ List outgoing = new ArrayList<>();
+ listFilesImpl(base, recursive, extensions, hidden, directories, files, outgoing);
+ return outgoing.toArray(new File[0]);
+ }
+
+
+ static void listFilesImpl(File folder, boolean recursive,
+ String[] extensions, boolean hidden,
+ boolean directories, boolean files,
+ List list) {
+ File[] items = folder.listFiles();
+ if (items != null) {
+ for (File item : items) {
+ String name = item.getName();
+ if (!hidden && name.charAt(0) == '.') {
+ continue;
+ }
+ if (item.isDirectory()) {
+ if (recursive) {
+ listFilesImpl(item, recursive, extensions, hidden, directories, files, list);
+ }
+ if (directories) {
+ list.add(item);
+ }
+ } else if (files) {
+ if (extensions == null) {
+ list.add(item);
+ } else {
+ for (String ext : extensions) {
+ if (item.getName().toLowerCase().endsWith(ext)) {
+ list.add(item);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // EXTENSIONS
+
+
+ /**
+ * Get the compression-free extension for this filename.
+ * @param filename The filename to check
+ * @return an extension, skipping past .gz if it's present
+ */
+ static public String checkExtension(String filename) {
+ // Don't consider the .gz as part of the name, createInput()
+ // and createOuput() will take care of fixing that up.
+ if (filename.toLowerCase().endsWith(".gz")) {
+ filename = filename.substring(0, filename.length() - 3);
+ }
+ int dotIndex = filename.lastIndexOf('.');
+ if (dotIndex != -1) {
+ return filename.substring(dotIndex + 1).toLowerCase();
+ }
+ return null;
+ }
+
//////////////////////////////////////////////////////////////
@@ -4600,11 +4759,22 @@ static public BufferedReader createReader(File file) {
* following lines any more I'm gonna send Sun my medical bills.
*/
static public BufferedReader createReader(InputStream input) {
- InputStreamReader isr = null;
+ InputStreamReader isr =
+ new InputStreamReader(input, CompatUtils.getCharsetUTF8());
+
+ BufferedReader reader = new BufferedReader(isr);
+ // consume the Unicode BOM (byte order marker) if present
try {
- isr = new InputStreamReader(input, "UTF-8");
- } catch (UnsupportedEncodingException e) { } // not gonna happen
- return new BufferedReader(isr);
+ reader.mark(1);
+ int c = reader.read();
+ // if not the BOM, back up to the beginning again
+ if (c != '\uFEFF') {
+ reader.reset();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return reader;
}
@@ -4646,12 +4816,10 @@ static public PrintWriter createWriter(File file) {
* It's the JavaSoft API engineers who need to explain themselves.
*/
static public PrintWriter createWriter(OutputStream output) {
- try {
- BufferedOutputStream bos = new BufferedOutputStream(output, 8192);
- OutputStreamWriter osw = new OutputStreamWriter(bos, "UTF-8");
- return new PrintWriter(osw);
- } catch (UnsupportedEncodingException e) { } // not gonna happen
- return null;
+ BufferedOutputStream bos = new BufferedOutputStream(output, 8192);
+ OutputStreamWriter osw =
+ new OutputStreamWriter(bos, CompatUtils.getCharsetUTF8());
+ return new PrintWriter(osw);
}
@@ -4693,15 +4861,18 @@ static public PrintWriter createWriter(OutputStream output) {
*/
public InputStream createInput(String filename) {
InputStream input = createInputRaw(filename);
- if ((input != null) && filename.toLowerCase().endsWith(".gz")) {
+ final String lower = filename.toLowerCase();
+ if ((input != null) &&
+ (lower.endsWith(".gz") || lower.endsWith(".svgz"))) {
try {
- return new GZIPInputStream(input);
+ // buffered has to go *around* the GZ, otherwise 25x slower
+ return new BufferedInputStream(new GZIPInputStream(input));
} catch (IOException e) {
- e.printStackTrace();
+ printStackTrace(e);
return null;
}
}
- return input;
+ return new BufferedInputStream(input);
}
@@ -4731,12 +4902,19 @@ public InputStream createInputRaw(String filename) {
// URL url = new URL(filename);
// stream = url.openStream();
// return stream;
- HttpGet httpRequest = null;
- httpRequest = new HttpGet(URI.create(filename));
- HttpClient httpclient = new DefaultHttpClient();
- HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);
- HttpEntity entity = response.getEntity();
- return entity.getContent();
+ URL url = new URL(filename);
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("GET");
+ con.setDoInput(true);
+ con.connect();
+ return con.getInputStream();
+ //The following code is deprecaded by Android
+// HttpGet httpRequest = null;
+// httpRequest = new HttpGet(URI.create(filename));
+// HttpClient httpclient = new DefaultHttpClient();
+// HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);
+// HttpEntity entity = response.getEntity();
+// return entity.getContent();
// can't use BufferedHttpEntity because it may try to allocate a byte
// buffer of the size of the download, bad when DL is 25 MB... [0200]
// BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
@@ -4751,7 +4929,7 @@ public InputStream createInputRaw(String filename) {
} catch (IOException e) {
// changed for 0117, shouldn't be throwing exception
- e.printStackTrace();
+ printStackTrace(e);
//System.err.println("Error downloading from URL " + filename);
return null;
//throw new RuntimeException("Error downloading from URL " + filename);
@@ -4825,7 +5003,7 @@ public InputStream createInputRaw(String filename) {
*/
// Try the assets folder
- AssetManager assets = getAssets();
+ AssetManager assets = surface.getAssets();
try {
stream = assets.open(filename);
if (stream != null) {
@@ -4863,19 +5041,18 @@ public InputStream createInputRaw(String filename) {
}
// Attempt to load the file more directly. Doesn't like paths.
- Context context = getApplicationContext();
- try {
- // MODE_PRIVATE is default, should we use something else?
- stream = context.openFileInput(filename);
- if (stream != null) {
- return stream;
- }
- } catch (FileNotFoundException e) {
- // ignore this and move on
- //e.printStackTrace();
- }
+// try {
+// // MODE_PRIVATE is default, should we use something else?
+// stream = surface.openFileInput(filename);
+// if (stream != null) {
+// return stream;
+// }
+// } catch (FileNotFoundException e) {
+// // ignore this and move on
+// //e.printStackTrace();
+// }
- return null;
+ return surface.openFileInput(filename);
}
@@ -4886,9 +5063,9 @@ static public InputStream createInput(File file) {
try {
InputStream input = new FileInputStream(file);
if (file.getName().toLowerCase().endsWith(".gz")) {
- return new GZIPInputStream(input);
+ return new BufferedInputStream(new GZIPInputStream(input));
}
- return input;
+ return new BufferedInputStream(input);
} catch (IOException e) {
System.err.println("Could not createInput() for " + file);
@@ -5073,7 +5250,7 @@ public OutputStream createOutput(String filename) {
return fos;
} catch (IOException e) {
- e.printStackTrace();
+ printStackTrace(e);
}
return null;
}
@@ -5081,11 +5258,12 @@ public OutputStream createOutput(String filename) {
static public OutputStream createOutput(File file) {
try {
- FileOutputStream fos = new FileOutputStream(file);
+ createPath(file); // make sure the path exists
+ OutputStream output = new FileOutputStream(file);
if (file.getName().toLowerCase().endsWith(".gz")) {
- return new GZIPOutputStream(fos);
+ return new BufferedOutputStream(new GZIPOutputStream(output));
}
- return fos;
+ return new BufferedOutputStream(output);
} catch (IOException e) {
e.printStackTrace();
@@ -5120,33 +5298,25 @@ public boolean saveStream(String targetFilename, InputStream sourceStream) {
}
- static public boolean saveStream(File targetFile, InputStream sourceStream) {
+ static public boolean saveStream(File target, InputStream source) {
File tempFile = null;
try {
- File parentDir = targetFile.getParentFile();
- createPath(targetFile);
- tempFile = File.createTempFile(targetFile.getName(), null, parentDir);
-
- BufferedInputStream bis = new BufferedInputStream(sourceStream, 16384);
- FileOutputStream fos = new FileOutputStream(tempFile);
- BufferedOutputStream bos = new BufferedOutputStream(fos);
-
- byte[] buffer = new byte[8192];
- int bytesRead;
- while ((bytesRead = bis.read(buffer)) != -1) {
- bos.write(buffer, 0, bytesRead);
- }
-
- bos.flush();
- bos.close();
- bos = null;
-
- if (targetFile.exists() && !targetFile.delete()) {
- System.err.println("Could not replace " +
- targetFile.getAbsolutePath() + ".");
+ // make sure that this path actually exists before writing
+ createPath(target);
+ tempFile = createTempFile(target);
+ FileOutputStream targetStream = new FileOutputStream(tempFile);
+
+ saveStream(targetStream, source);
+ targetStream.close();
+ targetStream = null;
+
+ if (target.exists()) {
+ if (!target.delete()) {
+ System.err.println("Could not replace " +
+ target.getAbsolutePath() + ".");
+ }
}
-
- if (!tempFile.renameTo(targetFile)) {
+ if (!tempFile.renameTo(target)) {
System.err.println("Could not rename temporary file " +
tempFile.getAbsolutePath());
return false;
@@ -5163,34 +5333,90 @@ static public boolean saveStream(File targetFile, InputStream sourceStream) {
}
- /**
- * Saves bytes to a file to inside the sketch folder.
+ static public void saveStream(OutputStream target,
+ InputStream source) throws IOException {
+ BufferedInputStream bis = new BufferedInputStream(source, 16384);
+ BufferedOutputStream bos = new BufferedOutputStream(target);
+
+ byte[] buffer = new byte[8192];
+ int bytesRead;
+ while ((bytesRead = bis.read(buffer)) != -1) {
+ bos.write(buffer, 0, bytesRead);
+ }
+
+ bos.flush();
+ }
+
+
+ /**
+ * Saves bytes to a file to inside the sketch folder.
* The filename can be a relative path, i.e. "poo/bytefun.txt"
* would save to a file named "bytefun.txt" to a subfolder
* called 'poo' inside the sketch folder. If the in-between
* subfolders don't exist, they'll be created.
*/
- public void saveBytes(String filename, byte buffer[]) {
- saveBytes(saveFile(filename), buffer);
+ public void saveBytes(String filename, byte[] data) {
+ saveBytes(saveFile(filename), data);
+ }
+
+
+ /**
+ * Creates a temporary file based on the name/extension of another file
+ * and in the same parent directory. Ensures that the same extension is used
+ * (i.e. so that .gz files are gzip compressed on output) and that it's done
+ * from the same directory so that renaming the file later won't cross file
+ * system boundaries.
+ */
+ static private File createTempFile(File file) throws IOException {
+ File parentDir = file.getParentFile();
+ String name = file.getName();
+ String prefix;
+ String suffix = null;
+ int dot = name.lastIndexOf('.');
+ if (dot == -1) {
+ prefix = name;
+ } else {
+ // preserve the extension so that .gz works properly
+ prefix = name.substring(0, dot);
+ suffix = name.substring(dot);
+ }
+ // Prefix must be three characters
+ if (prefix.length() < 3) {
+ prefix += "processing";
+ }
+ return File.createTempFile(prefix, suffix, parentDir);
}
/**
* Saves bytes to a specific File location specified by the user.
*/
- static public void saveBytes(File file, byte buffer[]) {
+ static public void saveBytes(File file, byte[] data) {
+ File tempFile = null;
try {
- String filename = file.getAbsolutePath();
- createPath(filename);
- OutputStream output = new FileOutputStream(file);
- if (file.getName().toLowerCase().endsWith(".gz")) {
- output = new GZIPOutputStream(output);
- }
- saveBytes(output, buffer);
+ tempFile = createTempFile(file);
+
+ OutputStream output = createOutput(tempFile);
+ saveBytes(output, data);
output.close();
+ output = null;
+
+ if (file.exists()) {
+ if (!file.delete()) {
+ System.err.println("Could not replace " + file.getAbsolutePath());
+ }
+ }
+
+ if (!tempFile.renameTo(file)) {
+ System.err.println("Could not rename temporary file " +
+ tempFile.getAbsolutePath());
+ }
} catch (IOException e) {
System.err.println("error saving bytes to " + file);
+ if (tempFile != null) {
+ tempFile.delete();
+ }
e.printStackTrace();
}
}
@@ -5199,9 +5425,9 @@ static public void saveBytes(File file, byte buffer[]) {
/**
* Spews a buffer of bytes to an OutputStream.
*/
- static public void saveBytes(OutputStream output, byte buffer[]) {
+ static public void saveBytes(OutputStream output, byte[] data) {
try {
- output.write(buffer);
+ output.write(data);
output.flush();
} catch (IOException e) {
@@ -5277,8 +5503,7 @@ public String sketchPath(String where) {
if (new File(where).isAbsolute()) return where;
} catch (Exception e) { }
- Context context = getApplicationContext();
- return context.getFileStreamPath(where).getAbsolutePath();
+ return surface.getFileStreamPath(where).getAbsolutePath();
}
@@ -5321,20 +5546,31 @@ public File saveFile(String where) {
/**
* Return a full path to an item in the data folder.
*
- * In this method, the data path is defined not as the applet's actual
- * data path, but a folder titled "data" in the sketch's working
- * directory. When running inside the PDE, this will be the sketch's
- * "data" folder. However, when exported (as application or applet),
- * sketch's data folder is exported as part of the applications jar file,
- * and it's not possible to read/write from the jar file in a generic way.
- * If you need to read data from the jar file, you should use createInput().
+ * The behavior of this function differs from the equivalent on the Java mode: files stored in
+ * the data folder of the sketch get packed as assets in the apk, and the path to the data folder
+ * is no longer valid. Only the name is needed to open them. However, if the file is not an asset,
+ * we can assume it has been created by the sketch, so it should have the sketch path.
+ * Discussed here:
+ * https://github.com/processing/processing-android/issues/450
*/
public String dataPath(String where) {
- // isAbsolute() could throw an access exception, but so will writing
- // to the local disk using the sketch path, so this is safe here.
- if (new File(where).isAbsolute()) return where;
-
- return sketchPath + File.separator + "data" + File.separator + where;
+ // First, we check if it is asset:
+ boolean isAsset = false;
+ AssetManager assets = surface.getAssets();
+ InputStream is = null;
+ try {
+ is = assets.open(where);
+ isAsset = true;
+ } catch (IOException ex) {
+ //file does not exist
+ } finally {
+ try {
+ is.close();
+ } catch (Exception ex) { }
+ }
+ if (isAsset) return where;
+ // Not an asset, let's just use sketch path:
+ return sketchPath(where);
}
@@ -5565,18 +5801,16 @@ static public int[] expand(int list[], int newSize) {
return temp;
}
-
- static public PImage[] expand(PImage list[]) {
- return expand(list, list.length << 1);
+ static public long[] expand(long list[]) {
+ return expand(list, list.length > 0 ? list.length << 1 : 1);
}
- static public PImage[] expand(PImage list[], int newSize) {
- PImage temp[] = new PImage[newSize];
+ static public long[] expand(long list[], int newSize) {
+ long temp[] = new long[newSize];
System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
return temp;
}
-
static public float[] expand(float list[]) {
return expand(list, list.length << 1);
}
@@ -5587,6 +5821,15 @@ static public float[] expand(float list[], int newSize) {
return temp;
}
+ static public double[] expand(double list[]) {
+ return expand(list, list.length > 0 ? list.length << 1 : 1);
+ }
+
+ static public double[] expand(double list[], int newSize) {
+ double temp[] = new double[newSize];
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
static public String[] expand(String list[]) {
return expand(list, list.length << 1);
@@ -5881,6 +6124,15 @@ static public int[] subset(int list[], int start, int count) {
return output;
}
+ static public long[] subset(long[] list, int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public long[] subset(long[] list, int start, int count) {
+ long[] output = new long[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
static public float[] subset(float list[], int start) {
return subset(list, start, list.length - start);
@@ -5892,6 +6144,15 @@ static public float[] subset(float list[], int start, int count) {
return output;
}
+ static public double[] subset(double[] list, int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public double[] subset(double[] list, int start, int count) {
+ double[] output = new double[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
static public String[] subset(String list[], int start) {
return subset(list, start, list.length - start);
@@ -6335,7 +6596,7 @@ static final public boolean parseBoolean(float what) {
* @return true if 'what' is "true" or "TRUE", false otherwise
*/
static final public boolean parseBoolean(String what) {
- return new Boolean(what).booleanValue();
+ return Boolean.valueOf(what);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -6393,7 +6654,7 @@ static final public boolean[] parseBoolean(float what[]) {
static final public boolean[] parseBoolean(String what[]) {
boolean outgoing[] = new boolean[what.length];
for (int i = 0; i < what.length; i++) {
- outgoing[i] = new Boolean(what[i]).booleanValue();
+ outgoing[i] = Boolean.valueOf(what[i]);
}
return outgoing;
}
@@ -6682,7 +6943,7 @@ static final public float parseFloat(String what) {
static final public float parseFloat(String what, float otherwise) {
try {
- return new Float(what).floatValue();
+ return Float.valueOf(what);
} catch (NumberFormatException e) { }
return otherwise;
@@ -6732,7 +6993,7 @@ static final public float[] parseFloat(String what[], float missing) {
float output[] = new float[what.length];
for (int i = 0; i < what.length; i++) {
try {
- output[i] = new Float(what[i]).floatValue();
+ output[i] = Float.valueOf(what[i]);
} catch (NumberFormatException e) {
output[i] = missing;
}
@@ -7221,12 +7482,20 @@ public final int color(float x, float y, float z, float a) {
}
+ public int lerpColor(int c1, int c2, float amt) {
+ if (g != null) {
+ return g.lerpColor(c1, c2, amt);
+ }
+ // use the default mode (RGB) if lerpColor is called before setup()
+ return PGraphics.lerpColor(c1, c2, amt, RGB);
+ }
+
+
static public int blendColor(int c1, int c2, int mode) {
return PImage.blendColor(c1, c2, mode);
}
-
//////////////////////////////////////////////////////////////
// MAIN
@@ -7782,72 +8051,186 @@ public void updatePixels(int x1, int y1, int x2, int y2) {
}
- private void tellPDE(final String message) {
- Log.i(getComponentName().getPackageName(), "PROCESSING " + message);
+ //////////////////////////////////////////////////////////////
+
+ // ANDROID-SPECIFIC API
+
+
+ // Wallpaper and wear API
+
+
+ public boolean wallpaperPreview() {
+ return surface.getEngine().isPreview();
}
- @Override
- protected void onStart() {
- tellPDE("onStart");
- super.onStart();
+ public float wallpaperOffset() {
+ return surface.getEngine().getXOffset();
}
- @Override
- protected void onStop() {
- tellPDE("onStop");
- super.onStop();
+ public int wallpaperHomeCount() {
+ float step = surface.getEngine().getXOffsetStep();
+ if (0 < step) {
+ return (int)(1 + 1 / step);
+ } else {
+ return 1;
+ }
}
- //////////////////////////////////////////////////////////////
+ public boolean wearAmbient() {
+ return surface.getEngine().isInAmbientMode();
+ }
+
+
+ public boolean wearInteractive() {
+ return !surface.getEngine().isInAmbientMode();
+ }
+
+
+ public boolean wearRound() {
+ return surface.getEngine().isRound();
+ }
+
+
+ public boolean wearSquare() {
+ return !surface.getEngine().isRound();
+ }
+
+
+ public Rect wearInsets() {
+ return surface.getEngine().getInsets();
+ }
+
+
+ public boolean wearLowBit() {
+ return surface.getEngine().useLowBitAmbient();
+ }
+
+
+ public boolean wearBurnIn() {
+ return surface.getEngine().requireBurnInProtection();
+ }
+
+
+ // Ray casting API
+
+
+ public PVector[] getRayFromScreen(float screenX, float screenY, PVector[] ray) {
+ return g.getRayFromScreen(screenX, screenY, ray);
+ }
+
+
+ public void getRayFromScreen(float screenX, float screenY, PVector origin, PVector direction) {
+ g.getRayFromScreen(screenX, screenY, origin, direction);
+ }
+
+
+ public boolean intersectsSphere(float r, float screenX, float screenY) {
+ return g.intersectsSphere(r, screenX, screenY);
+ }
+
+
+ public boolean intersectsSphere(float r, PVector origin, PVector direction) {
+ return g.intersectsSphere(r, origin, direction);
+ }
+
+
+ public boolean intersectsBox(float w, float screenX, float screenY) {
+ return g.intersectsBox(w, screenX, screenY);
+ }
+
+
+ public boolean intersectsBox(float w, float h, float d, float screenX, float screenY) {
+ return g.intersectsBox(w, h, d, screenX, screenY);
+ }
+
+
+ public boolean intersectsBox(float size, PVector origin, PVector direction) {
+ return g.intersectsBox(size, origin, direction);
+ }
+
+
+ public boolean intersectsBox(float w, float h, float d, PVector origin, PVector direction) {
+ return g.intersectsBox(w, h, d, origin, direction);
+ }
+
+
+ public PVector intersectsPlane(float screenX, float screenY) {
+ return g.intersectsPlane(screenX, screenY);
+ }
+
+
+ public PVector intersectsPlane(PVector origin, PVector direction) {
+ return g.intersectsPlane(origin, direction);
+ }
- // everything below this line is automatically generated. no touch.
- // public functions for processing.core
+ public void eye() {
+ g.eye();
+ }
+
+
+ public void calculate() {
+ }
+
/**
- * Store data of some kind for the renderer that requires extra metadata of
- * some kind. Usually this is a renderer-specific representation of the
- * image data, for instance a BufferedImage with tint() settings applied for
- * PGraphicsJava2D, or resized image data and OpenGL texture indices for
- * PGraphicsOpenGL.
- * @param renderer The PGraphics renderer associated to the image
- * @param storage The metadata required by the renderer
+ * Sets the coordinate system in 3D centered at (width/2, height/2)
+ * and with the Y axis pointing up.
*/
- public void setCache(PImage image, Object storage) {
- g.setCache(image, storage);
+
+ public void cameraUp() {
+ g.cameraUp();
}
/**
- * Get cache storage data for the specified renderer. Because each renderer
- * will cache data in different formats, it's necessary to store cache data
- * keyed by the renderer object. Otherwise, attempting to draw the same
- * image to both a PGraphicsJava2D and a PGraphicsOpenGL will cause errors.
- * @param renderer The PGraphics renderer associated to the image
- * @return metadata stored for the specified renderer
+ * Returns a copy of the current object matrix.
+ * Pass in null to create a new matrix.
*/
- public Object getCache(PImage image) {
- return g.getCache(image);
+ public PMatrix3D getObjectMatrix() {
+ return g.getObjectMatrix();
}
/**
- * Remove information associated with this renderer from the cache, if any.
- * @param renderer The PGraphics renderer whose cache data should be removed
+ * Copy the current object matrix into the specified target.
+ * Pass in null to create a new matrix.
*/
- public void removeCache(PImage image) {
- g.removeCache(image);
+ public PMatrix3D getObjectMatrix(PMatrix3D target) {
+ return g.getObjectMatrix(target);
}
- public void flush() {
- g.flush();
+ /**
+ * Returns a copy of the current eye matrix.
+ * Pass in null to create a new matrix.
+ */
+ public PMatrix3D getEyeMatrix() {
+ return g.getEyeMatrix();
+ }
+
+
+ /**
+ * Copy the current eye matrix into the specified target.
+ * Pass in null to create a new matrix.
+ */
+ public PMatrix3D getEyeMatrix(PMatrix3D target) {
+ return g.getEyeMatrix(target);
}
+ //////////////////////////////////////////////////////////////
+
+ // EVERYTHING BELOW THIS LINE IS AUTOMATICALLY GENERATED. DO NOT TOUCH!
+ // This includes the Javadoc comments, which are automatically copied from
+ // the PImage and PGraphics source code files.
+
+ // public functions for processing.core
+
+
public PGL beginPGL() {
return g.beginPGL();
}
@@ -7858,24 +8241,11 @@ public void endPGL() {
}
- /**
- * Enable a hint option.
- *
- * 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.
- *
- * Current hint options:
- *
- * DISABLE_DEPTH_TEST -
- * turns off the z-buffer in the P3D or OPENGL renderers.
- *
- */
+ public void flush() {
+ g.flush();
+ }
+
+
public void hint(int which) {
g.hint(which);
}
@@ -7890,26 +8260,41 @@ public void beginShape() {
/**
- * Start a new shape.
- *
- * Differences between beginShape() and line() and point() methods.
- *
- * beginShape() is intended to be more flexible at the expense of being
- * a little more complicated to use. it handles more complicated shapes
- * that can consist of many connected lines (so you get joins) or lines
- * mixed with curves.
- *
- * The line() and point() command are for the far more common cases
- * (particularly for our audience) that simply need to draw a line
- * or a point on the screen.
- *
- * From the code side of things, line() may or may not call beginShape()
- * to do the drawing. In the beta code, they do, but in the alpha code,
- * they did not. they might be implemented one way or the other depending
- * on tradeoffs of runtime efficiency vs. implementation efficiency &mdash
- * meaning the speed that things run at vs. the speed it takes me to write
- * the code and maintain it. for beta, the latter is most important so
- * that's how things are implemented.
+ * ( begin auto-generated from beginShape.xml )
+ *
+ * Using the beginShape() and endShape() functions allow
+ * creating more complex forms. beginShape() begins recording
+ * vertices for a shape and endShape() stops recording. The value of
+ * the MODE parameter tells it which types of shapes to create from
+ * the provided vertices. With no mode specified, the shape can be any
+ * irregular polygon. The parameters available for beginShape() are POINTS,
+ * LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP.
+ * After calling the beginShape() function, a series of
+ * vertex() commands must follow. To stop drawing the shape, call
+ * endShape() . The vertex() function with two parameters
+ * specifies a position in 2D and the vertex() function with three
+ * parameters specifies a position in 3D. Each shape will be outlined with
+ * the current stroke color and filled with the fill color.
+ *
+ * Transformations such as translate() , rotate() , and
+ * scale() do not work within beginShape() . It is also not
+ * possible to use other shapes, such as ellipse() or rect()
+ * within beginShape() .
+ *
+ * The P3D renderer settings allow stroke() and fill()
+ * settings to be altered per-vertex, however the default P2D renderer does
+ * not. Settings such as strokeWeight() , strokeCap() , and
+ * strokeJoin() cannot be changed while inside a
+ * beginShape() /endShape() block with any renderer.
+ *
+ * ( end auto-generated )
+ * @webref shape:vertex
+ * @param kind Either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, or QUAD_STRIP
+ * @see PShape
+ * @see PGraphics#endShape()
+ * @see PGraphics#vertex(float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float, float)
+ * @see PGraphics#bezierVertex(float, float, float, float, float, float, float, float, float)
*/
public void beginShape(int kind) {
g.beginShape(kind);
@@ -7926,40 +8311,116 @@ public void edge(boolean edge) {
/**
- * Sets the current normal vector. Only applies with 3D rendering
- * and inside a beginShape/endShape block.
- *
- * This is for drawing three dimensional shapes and surfaces,
- * allowing you to specify a vector perpendicular to the surface
- * of the shape, which determines how lighting affects it.
- *
- * For people familiar with OpenGL, this function is basically
- * identical to glNormal3f().
+ * ( begin auto-generated from normal.xml )
+ *
+ * Sets the current normal vector. This is for drawing three dimensional
+ * shapes and surfaces and specifies a vector perpendicular to the surface
+ * of the shape which determines how lighting affects it. Processing
+ * attempts to automatically assign normals to shapes, but since that's
+ * imperfect, this is a better option when you want more control. This
+ * function is identical to glNormal3f() in OpenGL.
+ *
+ * ( end auto-generated )
+ * @webref lights_camera:lights
+ * @param nx x direction
+ * @param ny y direction
+ * @param nz z direction
+ * @see PGraphics#beginShape(int)
+ * @see PGraphics#endShape(int)
+ * @see PGraphics#lights()
*/
public void normal(float nx, float ny, float nz) {
g.normal(nx, ny, nz);
}
+ public void attribPosition(String name, float x, float y, float z) {
+ g.attribPosition(name, x, y, z);
+ }
+
+
+ public void attribNormal(String name, float nx, float ny, float nz) {
+ g.attribNormal(name, nx, ny, nz);
+ }
+
+
+ public void attribColor(String name, int color) {
+ g.attribColor(name, color);
+ }
+
+
+ public void attrib(String name, float... values) {
+ g.attrib(name, values);
+ }
+
+
+ public void attrib(String name, int... values) {
+ g.attrib(name, values);
+ }
+
+
+ public void attrib(String name, boolean... values) {
+ g.attrib(name, values);
+ }
+
+
/**
- * Set texture mode to either to use coordinates based on the IMAGE
- * (more intuitive for new users) or NORMALIZED (better for advanced chaps)
+ * ( begin auto-generated from textureMode.xml )
+ *
+ * Sets the coordinate space for texture mapping. There are two options,
+ * IMAGE, which refers to the actual coordinates of the image, and
+ * NORMAL, which refers to a normalized space of values ranging from 0
+ * to 1. The default mode is IMAGE. In IMAGE, if an image is 100 x 200
+ * pixels, mapping the image onto the entire size of a quad would require
+ * the points (0,0) (0,100) (100,200) (0,200). The same mapping in
+ * NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
+ *
+ * ( end auto-generated )
+ * @webref image:textures
+ * @param mode either IMAGE or NORMAL
+ * @see PGraphics#texture(PImage)
+ * @see PGraphics#textureWrap(int)
*/
public void textureMode(int mode) {
g.textureMode(mode);
}
+ /**
+ * ( begin auto-generated from textureWrap.xml )
+ *
+ * Description to come...
+ *
+ * ( end auto-generated from textureWrap.xml )
+ *
+ * @webref image:textures
+ * @param wrap Either CLAMP (default) or REPEAT
+ * @see PGraphics#texture(PImage)
+ * @see PGraphics#textureMode(int)
+ */
public void textureWrap(int wrap) {
g.textureWrap(wrap);
}
/**
- * Set texture image for current shape.
- * Needs to be called between @see beginShape and @see endShape
+ * ( begin auto-generated from texture.xml )
*
+ * Sets a texture to be applied to vertex points. The texture()
+ * function must be called between beginShape() and
+ * endShape() and before any calls to vertex() .
+ *
+ * When textures are in use, the fill color is ignored. Instead, use tint()
+ * to specify the color of the texture as it is applied to the shape.
+ *
+ * ( end auto-generated )
+ * @webref image:textures
* @param image reference to a PImage object
+ * @see PGraphics#textureMode(int)
+ * @see PGraphics#textureWrap(int)
+ * @see PGraphics#beginShape(int)
+ * @see PGraphics#endShape(int)
+ * @see PGraphics#vertex(float, float, float, float, float)
*/
public void texture(PImage image) {
g.texture(image);
@@ -7968,7 +8429,7 @@ public void texture(PImage image) {
/**
* Removes texture image for current shape.
- * Needs to be called between @see beginShape and @see endShape
+ * Needs to be called between beginShape and endShape
*
*/
public void noTexture() {
@@ -8001,22 +8462,54 @@ public void vertex(float x, float y, float u, float v) {
}
+ /**
+ * ( begin auto-generated from vertex.xml )
+ *
+ * All shapes are constructed by connecting a series of vertices.
+ * vertex() is used to specify the vertex coordinates for points,
+ * lines, triangles, quads, and polygons and is used exclusively within the
+ * beginShape() and endShape() function.
+ *
+ * Drawing a vertex in 3D using the z parameter requires the P3D
+ * parameter in combination with size as shown in the above example.
+ *
+ * This function is also used to map a texture onto the geometry. The
+ * texture() function declares the texture to apply to the geometry
+ * and the u and v coordinates set define the mapping of this
+ * texture to the form. By default, the coordinates used for u and
+ * v are specified in relation to the image's size in pixels, but
+ * this relation can be changed with textureMode() .
+ *
+ * ( end auto-generated )
+ * @webref shape:vertex
+ * @param x x-coordinate of the vertex
+ * @param y y-coordinate of the vertex
+ * @param z z-coordinate of the vertex
+ * @param u horizontal coordinate for the texture mapping
+ * @param v vertical coordinate for the texture mapping
+ * @see PGraphics#beginShape(int)
+ * @see PGraphics#endShape(int)
+ * @see PGraphics#bezierVertex(float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#quadraticVertex(float, float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float, float)
+ * @see PGraphics#texture(PImage)
+ */
public void vertex(float x, float y, float z, float u, float v) {
g.vertex(x, y, z, u, v);
}
- /** This feature is in testing, do not use or rely upon its implementation */
- public void breakShape() {
- g.breakShape();
- }
-
-
+ /**
+ * @webref shape:vertex
+ */
public void beginContour() {
g.beginContour();
}
+ /**
+ * @webref shape:vertex
+ */
public void endContour() {
g.endContour();
}
@@ -8027,36 +8520,52 @@ public void endShape() {
}
+ /**
+ * ( begin auto-generated from endShape.xml )
+ *
+ * The endShape() function is the companion to beginShape()
+ * and may only be called after beginShape() . When endshape()
+ * is called, all of image data defined since the previous call to
+ * beginShape() is written into the image buffer. The constant CLOSE
+ * as the value for the MODE parameter to close the shape (to connect the
+ * beginning and the end).
+ *
+ * ( end auto-generated )
+ * @webref shape:vertex
+ * @param mode use CLOSE to close the shape
+ * @see PShape
+ * @see PGraphics#beginShape(int)
+ */
public void endShape(int mode) {
g.endShape(mode);
}
- public void clip(float a, float b, float c, float d) {
- g.clip(a, b, c, d);
- }
-
-
- public void noClip() {
- g.noClip();
- }
-
-
- public void blendMode(int mode) {
- g.blendMode(mode);
- }
-
-
+ /**
+ * @webref shape
+ * @param filename name of file to load, can be .svg or .obj
+ * @see PShape
+ * @see PApplet#createShape()
+ */
public PShape loadShape(String filename) {
return g.loadShape(filename);
}
- public PShape createShape(PShape source) {
- return g.createShape(source);
+ /**
+ * @nowebref
+ */
+ public PShape loadShape(String filename, String options) {
+ return g.loadShape(filename, options);
}
+ /**
+ * @webref shape
+ * @see PShape
+ * @see PShape#endShape()
+ * @see PApplet#loadShape(String)
+ */
public PShape createShape() {
return g.createShape();
}
@@ -8067,48 +8576,137 @@ public PShape createShape(int type) {
}
+ /**
+ * @param kind either POINT, LINE, TRIANGLE, QUAD, RECT, ELLIPSE, ARC, BOX, SPHERE
+ * @param p parameters that match the kind of shape
+ */
public PShape createShape(int kind, float... p) {
return g.createShape(kind, p);
}
+ /**
+ * ( begin auto-generated from loadShader.xml )
+ *
+ * This is a new reference entry for Processing 2.0. It will be updated shortly.
+ *
+ * ( end auto-generated )
+ *
+ * @webref rendering:shaders
+ * @param fragFilename name of fragment shader file
+ */
public PShader loadShader(String fragFilename) {
return g.loadShader(fragFilename);
}
+ /**
+ * @param vertFilename name of vertex shader file
+ */
public PShader loadShader(String fragFilename, String vertFilename) {
return g.loadShader(fragFilename, vertFilename);
}
- public void shader(PShader shader) {
+ /**
+ * ( begin auto-generated from shader.xml )
+ *
+ * This is a new reference entry for Processing 2.0. It will be updated shortly.
+ *
+ * ( end auto-generated )
+ *
+ * @webref rendering:shaders
+ * @param shader name of shader file
+ */
+ public void shader(PShader shader) {
g.shader(shader);
}
+ /**
+ * @param kind type of shader, either POINTS, LINES, or TRIANGLES
+ */
public void shader(PShader shader, int kind) {
g.shader(shader, kind);
}
+ /**
+ * ( begin auto-generated from resetShader.xml )
+ *
+ * This is a new reference entry for Processing 2.0. It will be updated shortly.
+ *
+ * ( end auto-generated )
+ *
+ * @webref rendering:shaders
+ */
public void resetShader() {
g.resetShader();
}
+ /**
+ * @param kind type of shader, either POINTS, LINES, or TRIANGLES
+ */
public void resetShader(int kind) {
g.resetShader(kind);
}
- public PShader getShader(int kind) {
- return g.getShader(kind);
+ /**
+ * @param shader the fragment shader to apply
+ */
+ public void filter(PShader shader) {
+ g.filter(shader);
}
- public void filter(PShader shader) {
- g.filter(shader);
+ /**
+ * ( begin auto-generated from clip.xml )
+ *
+ * Limits the rendering to the boundaries of a rectangle defined
+ * by the parameters. The boundaries are drawn based on the state
+ * of the imageMode() fuction, either CORNER, CORNERS, or CENTER.
+ *
+ * ( end auto-generated )
+ *
+ * @webref rendering
+ * @param a x-coordinate of the rectangle, by default
+ * @param b y-coordinate of the rectangle, by default
+ * @param c width of the rectangle, by default
+ * @param d height of the rectangle, by default
+ */
+ public void clip(float a, float b, float c, float d) {
+ g.clip(a, b, c, d);
+ }
+
+
+ /**
+ * ( begin auto-generated from noClip.xml )
+ *
+ * Disables the clipping previously started by the clip() function.
+ *
+ * ( end auto-generated )
+ *
+ * @webref rendering
+ */
+ public void noClip() {
+ g.noClip();
+ }
+
+
+ /**
+ * ( begin auto-generated from blendMode.xml )
+ *
+ * This is a new reference entry for Processing 2.0. It will be updated shortly.
+ *
+ * ( end auto-generated )
+ *
+ * @webref rendering
+ * @param mode the blending mode to use
+ */
+ public void blendMode(int mode) {
+ g.blendMode(mode);
}
@@ -8119,6 +8717,36 @@ public void bezierVertex(float x2, float y2,
}
+ /**
+ * ( begin auto-generated from bezierVertex.xml )
+ *
+ * Specifies vertex coordinates for Bezier curves. Each call to
+ * bezierVertex() defines the position of two control points and one
+ * anchor point of a Bezier curve, adding a new segment to a line or shape.
+ * The first time bezierVertex() is used within a
+ * beginShape() call, it must be prefaced with a call to
+ * vertex() to set the first anchor point. This function must be
+ * used between beginShape() and endShape() and only when
+ * there is no MODE parameter specified to beginShape() . Using the
+ * 3D version requires rendering with P3D (see the Environment reference
+ * for more information).
+ *
+ * ( end auto-generated )
+ * @webref shape:vertex
+ * @param x2 the x-coordinate of the 1st control point
+ * @param y2 the y-coordinate of the 1st control point
+ * @param z2 the z-coordinate of the 1st control point
+ * @param x3 the x-coordinate of the 2nd control point
+ * @param y3 the y-coordinate of the 2nd control point
+ * @param z3 the z-coordinate of the 2nd control point
+ * @param x4 the x-coordinate of the anchor point
+ * @param y4 the y-coordinate of the anchor point
+ * @param z4 the z-coordinate of the anchor point
+ * @see PGraphics#curveVertex(float, float, float)
+ * @see PGraphics#vertex(float, float, float, float, float)
+ * @see PGraphics#quadraticVertex(float, float, float, float, float, float)
+ * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
+ */
public void bezierVertex(float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4) {
@@ -8126,100 +8754,356 @@ public void bezierVertex(float x2, float y2, float z2,
}
+ /**
+ * @webref shape:vertex
+ * @param cx the x-coordinate of the control point
+ * @param cy the y-coordinate of the control point
+ * @param x3 the x-coordinate of the anchor point
+ * @param y3 the y-coordinate of the anchor point
+ * @see PGraphics#curveVertex(float, float, float)
+ * @see PGraphics#vertex(float, float, float, float, float)
+ * @see PGraphics#bezierVertex(float, float, float, float, float, float)
+ * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
+ */
public void quadraticVertex(float cx, float cy,
float x3, float y3) {
g.quadraticVertex(cx, cy, x3, y3);
}
+ /**
+ * @param cz the z-coordinate of the control point
+ * @param z3 the z-coordinate of the anchor point
+ */
public void quadraticVertex(float cx, float cy, float cz,
float x3, float y3, float z3) {
g.quadraticVertex(cx, cy, cz, x3, y3, z3);
}
+ /**
+ * ( begin auto-generated from curveVertex.xml )
+ *
+ * Specifies vertex coordinates for curves. This function may only be used
+ * between beginShape() and endShape() and only when there is
+ * no MODE parameter specified to beginShape() . The first and last
+ * points in a series of curveVertex() lines will be used to guide
+ * the beginning and end of a the curve. A minimum of four points is
+ * required to draw a tiny curve between the second and third points.
+ * Adding a fifth point with curveVertex() will draw the curve
+ * between the second, third, and fourth points. The curveVertex()
+ * function is an implementation of Catmull-Rom splines. Using the 3D
+ * version requires rendering with P3D (see the Environment reference for
+ * more information).
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:vertex
+ * @param x the x-coordinate of the vertex
+ * @param y the y-coordinate of the vertex
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#beginShape(int)
+ * @see PGraphics#endShape(int)
+ * @see PGraphics#vertex(float, float, float, float, float)
+ * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#quadraticVertex(float, float, float, float, float, float)
+ */
public void curveVertex(float x, float y) {
g.curveVertex(x, y);
}
+ /**
+ * @param z the z-coordinate of the vertex
+ */
public void curveVertex(float x, float y, float z) {
g.curveVertex(x, y, z);
}
+ /**
+ * ( begin auto-generated from point.xml )
+ *
+ * Draws a point, a coordinate in space at the dimension of one pixel. The
+ * first parameter is the horizontal value for the point, the second value
+ * is the vertical value for the point, and the optional third value is the
+ * depth value. Drawing this shape in 3D with the z parameter
+ * requires the P3D parameter in combination with size() as shown in
+ * the above example.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:2d_primitives
+ * @param x x-coordinate of the point
+ * @param y y-coordinate of the point
+ * @see PGraphics#stroke(int)
+ */
public void point(float x, float y) {
g.point(x, y);
}
+ /**
+ * @param z z-coordinate of the point
+ */
public void point(float x, float y, float z) {
g.point(x, y, z);
}
+ /**
+ * ( begin auto-generated from line.xml )
+ *
+ * Draws a line (a direct path between two points) to the screen. The
+ * version of line() with four parameters draws the line in 2D. To
+ * color a line, use the stroke() function. A line cannot be filled,
+ * therefore the fill() function will not affect the color of a
+ * line. 2D lines are drawn with a width of one pixel by default, but this
+ * can be changed with the strokeWeight() function. The version with
+ * six parameters allows the line to be placed anywhere within XYZ space.
+ * Drawing this shape in 3D with the z parameter requires the P3D
+ * parameter in combination with size() as shown in the above example.
+ *
+ * ( end auto-generated )
+ * @webref shape:2d_primitives
+ * @param x1 x-coordinate of the first point
+ * @param y1 y-coordinate of the first point
+ * @param x2 x-coordinate of the second point
+ * @param y2 y-coordinate of the second point
+ * @see PGraphics#strokeWeight(float)
+ * @see PGraphics#strokeJoin(int)
+ * @see PGraphics#strokeCap(int)
+ * @see PGraphics#beginShape()
+ */
public void line(float x1, float y1, float x2, float y2) {
g.line(x1, y1, x2, y2);
}
+ /**
+ * @param z1 z-coordinate of the first point
+ * @param z2 z-coordinate of the second point
+ */
public void line(float x1, float y1, float z1,
float x2, float y2, float z2) {
g.line(x1, y1, z1, x2, y2, z2);
}
+ /**
+ * ( begin auto-generated from triangle.xml )
+ *
+ * A triangle is a plane created by connecting three points. The first two
+ * arguments specify the first point, the middle two arguments specify the
+ * second point, and the last two arguments specify the third point.
+ *
+ * ( end auto-generated )
+ * @webref shape:2d_primitives
+ * @param x1 x-coordinate of the first point
+ * @param y1 y-coordinate of the first point
+ * @param x2 x-coordinate of the second point
+ * @param y2 y-coordinate of the second point
+ * @param x3 x-coordinate of the third point
+ * @param y3 y-coordinate of the third point
+ * @see PApplet#beginShape()
+ */
public void triangle(float x1, float y1, float x2, float y2,
float x3, float y3) {
g.triangle(x1, y1, x2, y2, x3, y3);
}
+ /**
+ * ( begin auto-generated from quad.xml )
+ *
+ * A quad is a quadrilateral, a four sided polygon. It is similar to a
+ * rectangle, but the angles between its edges are not constrained to
+ * ninety degrees. The first pair of parameters (x1,y1) sets the first
+ * vertex and the subsequent pairs should proceed clockwise or
+ * counter-clockwise around the defined shape.
+ *
+ * ( end auto-generated )
+ * @webref shape:2d_primitives
+ * @param x1 x-coordinate of the first corner
+ * @param y1 y-coordinate of the first corner
+ * @param x2 x-coordinate of the second corner
+ * @param y2 y-coordinate of the second corner
+ * @param x3 x-coordinate of the third corner
+ * @param y3 y-coordinate of the third corner
+ * @param x4 x-coordinate of the fourth corner
+ * @param y4 y-coordinate of the fourth corner
+ */
public void quad(float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4) {
g.quad(x1, y1, x2, y2, x3, y3, x4, y4);
}
+ /**
+ * ( begin auto-generated from rectMode.xml )
+ *
+ * Modifies the location from which rectangles draw. The default mode is
+ * rectMode(CORNER) , which specifies the location to be the upper
+ * left corner of the shape and uses the third and fourth parameters of
+ * rect() to specify the width and height. The syntax
+ * rectMode(CORNERS) uses the first and second parameters of
+ * rect() to set the location of one corner and uses the third and
+ * fourth parameters to set the opposite corner. The syntax
+ * rectMode(CENTER) draws the image from its center point and uses
+ * the third and forth parameters of rect() to specify the image's
+ * width and height. The syntax rectMode(RADIUS) draws the image
+ * from its center point and uses the third and forth parameters of
+ * rect() to specify half of the image's width and height. The
+ * parameter must be written in ALL CAPS because Processing is a case
+ * sensitive language. Note: In version 125, the mode named CENTER_RADIUS
+ * was shortened to RADIUS.
+ *
+ * ( end auto-generated )
+ * @webref shape:attributes
+ * @param mode either CORNER, CORNERS, CENTER, or RADIUS
+ * @see PGraphics#rect(float, float, float, float)
+ */
public void rectMode(int mode) {
g.rectMode(mode);
}
+ /**
+ * ( begin auto-generated from rect.xml )
+ *
+ * Draws a rectangle to the screen. A rectangle is a four-sided shape with
+ * every angle at ninety degrees. By default, the first two parameters set
+ * the location of the upper-left corner, the third sets the width, and the
+ * fourth sets the height. These parameters may be changed with the
+ * rectMode() function.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:2d_primitives
+ * @param a x-coordinate of the rectangle by default
+ * @param b y-coordinate of the rectangle by default
+ * @param c width of the rectangle by default
+ * @param d height of the rectangle by default
+ * @see PGraphics#rectMode(int)
+ * @see PGraphics#quad(float, float, float, float, float, float, float, float)
+ */
public void rect(float a, float b, float c, float d) {
g.rect(a, b, c, d);
}
+ /**
+ * @param r radii for all four corners
+ */
public void rect(float a, float b, float c, float d, float r) {
g.rect(a, b, c, d, r);
}
+ /**
+ * @param tl radius for top-left corner
+ * @param tr radius for top-right corner
+ * @param br radius for bottom-right corner
+ * @param bl radius for bottom-left corner
+ */
public void rect(float a, float b, float c, float d,
float tl, float tr, float br, float bl) {
g.rect(a, b, c, d, tl, tr, br, bl);
}
+ /**
+ * ( begin auto-generated from square.xml )
+ *
+ * Draws a square to the screen. A square is a four-sided shape with
+ * every angle at ninety degrees and each side is the same length.
+ * By default, the first two parameters set the location of the
+ * upper-left corner, the third sets the width and height. The way
+ * these parameters are interpreted, however, may be changed with the
+ * rectMode() function.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:2d_primitives
+ * @param x x-coordinate of the rectangle by default
+ * @param y y-coordinate of the rectangle by default
+ * @param extent width and height of the rectangle by default
+ * @see PGraphics#rect(float, float, float, float)
+ * @see PGraphics#rectMode(int)
+ */
+ public void square(float x, float y, float extent) {
+ g.square(x, y, extent);
+ }
+
+
+ /**
+ * ( begin auto-generated from ellipseMode.xml )
+ *
+ * The origin of the ellipse is modified by the ellipseMode()
+ * function. The default configuration is ellipseMode(CENTER) , which
+ * specifies the location of the ellipse as the center of the shape. The
+ * RADIUS mode is the same, but the width and height parameters to
+ * ellipse() specify the radius of the ellipse, rather than the
+ * diameter. The CORNER mode draws the shape from the upper-left
+ * corner of its bounding box. The CORNERS mode uses the four
+ * parameters to ellipse() to set two opposing corners of the
+ * ellipse's bounding box. The parameter must be written in ALL CAPS
+ * because Processing is a case-sensitive language.
+ *
+ * ( end auto-generated )
+ * @webref shape:attributes
+ * @param mode either CENTER, RADIUS, CORNER, or CORNERS
+ * @see PApplet#ellipse(float, float, float, float)
+ * @see PApplet#arc(float, float, float, float, float, float)
+ */
public void ellipseMode(int mode) {
g.ellipseMode(mode);
}
+ /**
+ * ( begin auto-generated from ellipse.xml )
+ *
+ * Draws an ellipse (oval) in the display window. An ellipse with an equal
+ * width and height is a circle. The first two parameters set
+ * the location, the third sets the width, and the fourth sets the height.
+ * The origin may be changed with the ellipseMode() function.
+ *
+ * ( end auto-generated )
+ * @webref shape:2d_primitives
+ * @param a x-coordinate of the ellipse
+ * @param b y-coordinate of the ellipse
+ * @param c width of the ellipse by default
+ * @param d height of the ellipse by default
+ * @see PApplet#ellipseMode(int)
+ * @see PApplet#arc(float, float, float, float, float, float)
+ */
public void ellipse(float a, float b, float c, float d) {
g.ellipse(a, b, c, d);
}
/**
- * Identical parameters and placement to ellipse,
- * but draws only an arc of that ellipse.
- *
- * start and stop are always radians because angleMode() was goofy.
- * ellipseMode() sets the placement.
- *
- * also tries to be smart about start < stop.
+ * ( begin auto-generated from arc.xml )
+ *
+ * Draws an arc in the display window. Arcs are drawn along the outer edge
+ * of an ellipse defined by the x , y , width and
+ * height parameters. The origin or the arc's ellipse may be changed
+ * with the ellipseMode() function. The start and stop
+ * parameters specify the angles at which to draw the arc.
+ *
+ * ( end auto-generated )
+ * @webref shape:2d_primitives
+ * @param a x-coordinate of the arc's ellipse
+ * @param b y-coordinate of the arc's ellipse
+ * @param c width of the arc's ellipse by default
+ * @param d height of the arc's ellipse by default
+ * @param start angle to start the arc, specified in radians
+ * @param stop angle to stop the arc, specified in radians
+ * @see PApplet#ellipse(float, float, float, float)
+ * @see PApplet#ellipseMode(int)
+ * @see PApplet#radians(float)
+ * @see PApplet#degrees(float)
*/
public void arc(float a, float b, float c, float d,
float start, float stop) {
@@ -8227,33 +9111,97 @@ public void arc(float a, float b, float c, float d,
}
+ /*
+ * @param mode either OPEN, CHORD, or PIE
+ */
public void arc(float a, float b, float c, float d,
float start, float stop, int mode) {
g.arc(a, b, c, d, start, stop, mode);
}
+ /**
+ * ( begin auto-generated from circle.xml )
+ *
+ * Draws a circle to the screen. By default, the first two parameters
+ * set the location of the center, and the third sets the shape's width
+ * and height. The origin may be changed with the ellipseMode()
+ * function.
+ *
+ * ( end auto-generated )
+ * @webref shape:2d_primitives
+ * @param x x-coordinate of the ellipse
+ * @param y y-coordinate of the ellipse
+ * @param extent width and height of the ellipse by default
+ * @see PApplet#ellipse(float, float, float, float)
+ * @see PApplet#ellipseMode(int)
+ */
+ public void circle(float x, float y, float extent) {
+ g.circle(x, y, extent);
+ }
+
+
+ /**
+ * ( begin auto-generated from box.xml )
+ *
+ * A box is an extruded rectangle. A box with equal dimension on all sides
+ * is a cube.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:3d_primitives
+ * @param size dimension of the box in all dimensions (creates a cube)
+ * @see PGraphics#sphere(float)
+ */
public void box(float size) {
g.box(size);
}
+ /**
+ * @param w dimension of the box in the x-dimension
+ * @param h dimension of the box in the y-dimension
+ * @param d dimension of the box in the z-dimension
+ */
public void box(float w, float h, float d) {
g.box(w, h, d);
}
+ /**
+ * ( begin auto-generated from sphereDetail.xml )
+ *
+ * Controls the detail used to render a sphere by adjusting the number of
+ * vertices of the sphere mesh. The default resolution is 30, which creates
+ * a fairly detailed sphere definition with vertices every 360/30 = 12
+ * degrees. If you're going to render a great number of spheres per frame,
+ * it is advised to reduce the level of detail using this function. The
+ * setting stays active until sphereDetail() is called again with a
+ * new parameter and so should not be called prior to every
+ * sphere() statement, unless you wish to render spheres with
+ * different settings, e.g. using less detail for smaller spheres or ones
+ * further away from the camera. To control the detail of the horizontal
+ * and vertical resolution independently, use the version of the functions
+ * with two parameters.
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
+ * Code for sphereDetail() submitted by toxi [031031].
+ * Code for enhanced u/v version from davbol [080801].
+ *
+ * @param res number of segments (minimum 3) used per full circle revolution
+ * @webref shape:3d_primitives
+ * @see PGraphics#sphere(float)
+ */
public void sphereDetail(int res) {
g.sphereDetail(res);
}
/**
- * Set the detail level for approximating a sphere. The ures and vres params
- * control the horizontal and vertical resolution.
- *
- * Code for sphereDetail() submitted by toxi [031031].
- * Code for enhanced u/v version from davbol [080801].
+ * @param ures number of segments used longitudinally per full circle revolutoin
+ * @param vres number of segments used latitudinally from top to bottom
*/
public void sphereDetail(int ures, int vres) {
g.sphereDetail(ures, vres);
@@ -8261,7 +9209,13 @@ public void sphereDetail(int ures, int vres) {
/**
- * Draw a sphere with radius r centered at coordinate 0, 0, 0.
+ * ( begin auto-generated from sphere.xml )
+ *
+ * A sphere is a hollow ball made from tessellated triangles.
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
*
* Implementation notes:
*
@@ -8281,6 +9235,10 @@ public void sphereDetail(int ures, int vres) {
*
* [davbol 080801] now using separate sphereDetailU/V
*
+ *
+ * @webref shape:3d_primitives
+ * @param r the radius of the sphere
+ * @see PGraphics#sphereDetail(int)
*/
public void sphere(float r) {
g.sphere(r);
@@ -8288,12 +9246,17 @@ public void sphere(float r) {
/**
- * 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.
- *
+ * ( begin auto-generated from bezierPoint.xml )
+ *
+ * Evaluates the Bezier at point t for points a, b, c, d. The parameter t
+ * varies between 0 and 1, a and d are points on the curve, and b and c are
+ * the control points. This can be done once with the x coordinates and a
+ * second time with the y coordinates to get the location of a bezier curve
+ * at t.
+ *
+ * ( end auto-generated )
+ *
+ *
Advanced
* For instance, to convert the following example:
* stroke(255, 102, 0);
* line(85, 20, 10, 10);
@@ -8313,6 +9276,16 @@ public void sphere(float r) {
* vertex(x, y);
* }
* endShape();
+ *
+ * @webref shape:curves
+ * @param a coordinate of first point on the curve
+ * @param b coordinate of first control point
+ * @param c coordinate of second control point
+ * @param d coordinate of second point on the curve
+ * @param t value between 0 and 1
+ * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#bezierVertex(float, float, float, float, float, float)
+ * @see PGraphics#curvePoint(float, float, float, float, float)
*/
public float bezierPoint(float a, float b, float c, float d, float t) {
return g.bezierPoint(a, b, c, d, t);
@@ -8320,20 +9293,74 @@ public float bezierPoint(float a, float b, float c, float d, float t) {
/**
- * Provide the tangent at the given point on the bezier curve.
- * Fix from davbol for 0136.
+ * ( begin auto-generated from bezierTangent.xml )
+ *
+ * Calculates the tangent of a point on a Bezier curve. There is a good
+ * definition of tangent on Wikipedia .
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
+ * Code submitted by Dave Bollinger (davol) for release 0136.
+ *
+ * @webref shape:curves
+ * @param a coordinate of first point on the curve
+ * @param b coordinate of first control point
+ * @param c coordinate of second control point
+ * @param d coordinate of second point on the curve
+ * @param t value between 0 and 1
+ * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#bezierVertex(float, float, float, float, float, float)
+ * @see PGraphics#curvePoint(float, float, float, float, float)
*/
public float bezierTangent(float a, float b, float c, float d, float t) {
return g.bezierTangent(a, b, c, d, t);
}
+ /**
+ * ( begin auto-generated from bezierDetail.xml )
+ *
+ * Sets the resolution at which Beziers display. The default value is 20.
+ * This function is only useful when using the P3D renderer as the default
+ * P2D renderer does not use this information.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:curves
+ * @param detail resolution of the curves
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float, float)
+ * @see PGraphics#curveTightness(float)
+ */
public void bezierDetail(int detail) {
g.bezierDetail(detail);
}
+ public void bezier(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ float x4, float y4) {
+ g.bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ }
+
+
/**
+ * ( begin auto-generated from bezier.xml )
+ *
+ * Draws a Bezier curve on the screen. These curves are defined by a series
+ * of anchor and control points. The first two parameters specify the first
+ * anchor point and the last two parameters specify the other anchor point.
+ * The middle parameters specify the control points which define the shape
+ * of the curve. Bezier curves were developed by French engineer Pierre
+ * Bezier. Using the 3D version requires rendering with P3D (see the
+ * Environment reference for more information).
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
* Draw a cubic bezier curve. The first and last points are
* the on-curve points. The middle two are the 'control' points,
* or 'handles' in an application like Illustrator.
@@ -8355,15 +9382,24 @@ public void bezierDetail(int detail) {
* To draw a quadratic (instead of cubic) curve,
* use the control point twice by doubling it:
* bezier(x1, y1, cx, cy, cx, cy, x2, y2);
+ *
+ * @webref shape:curves
+ * @param x1 coordinates for the first anchor point
+ * @param y1 coordinates for the first anchor point
+ * @param z1 coordinates for the first anchor point
+ * @param x2 coordinates for the first control point
+ * @param y2 coordinates for the first control point
+ * @param z2 coordinates for the first control point
+ * @param x3 coordinates for the second control point
+ * @param y3 coordinates for the second control point
+ * @param z3 coordinates for the second control point
+ * @param x4 coordinates for the second anchor point
+ * @param y4 coordinates for the second anchor point
+ * @param z4 coordinates for the second anchor point
+ *
+ * @see PGraphics#bezierVertex(float, float, float, float, float, float)
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
*/
- public void bezier(float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x4, float y4) {
- g.bezier(x1, y1, x2, y2, x3, y3, x4, y4);
- }
-
-
public void bezier(float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3,
@@ -8373,9 +9409,24 @@ public void bezier(float x1, float y1, float z1,
/**
- * Get a location along a catmull-rom curve segment.
+ * ( begin auto-generated from curvePoint.xml )
+ *
+ * Evalutes the curve at point t for points a, b, c, d. The parameter t
+ * varies between 0 and 1, a and d are the control points, and b and c are
+ * the points on the curve. This can be done once with the x coordinates and a
+ * second time with the y coordinates to get the location of a curve at t.
+ *
+ * ( end auto-generated )
*
- * @param t Value between zero and one for how far along the segment
+ * @webref shape:curves
+ * @param a coordinate of first control point
+ * @param b coordinate of first point on the curve
+ * @param c coordinate of second point on the curve
+ * @param d coordinate of second control point
+ * @param t value between 0 and 1
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float)
+ * @see PGraphics#bezierPoint(float, float, float, float, float)
*/
public float curvePoint(float a, float b, float c, float d, float t) {
return g.curvePoint(a, b, c, d, t);
@@ -8383,29 +9434,94 @@ public float curvePoint(float a, float b, float c, float d, float t) {
/**
- * Calculate the tangent at a t value (0..1) on a Catmull-Rom curve.
+ * ( begin auto-generated from curveTangent.xml )
+ *
+ * Calculates the tangent of a point on a curve. There's a good definition
+ * of tangent on Wikipedia.
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
* Code thanks to Dave Bollinger (Bug #715)
+ *
+ * @webref shape:curves
+ * @param a coordinate of first point on the curve
+ * @param b coordinate of first control point
+ * @param c coordinate of second control point
+ * @param d coordinate of second point on the curve
+ * @param t value between 0 and 1
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float)
+ * @see PGraphics#curvePoint(float, float, float, float, float)
+ * @see PGraphics#bezierTangent(float, float, float, float, float)
*/
public float curveTangent(float a, float b, float c, float d, float t) {
return g.curveTangent(a, b, c, d, t);
}
+ /**
+ * ( begin auto-generated from curveDetail.xml )
+ *
+ * Sets the resolution at which curves display. The default value is 20.
+ * This function is only useful when using the P3D renderer as the default
+ * P2D renderer does not use this information.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:curves
+ * @param detail resolution of the curves
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float)
+ * @see PGraphics#curveTightness(float)
+ */
public void curveDetail(int detail) {
g.curveDetail(detail);
}
+ /**
+ * ( begin auto-generated from curveTightness.xml )
+ *
+ * Modifies the quality of forms created with curve() and
+ * curveVertex() . The parameter squishy determines how the
+ * curve fits to the vertex points. The value 0.0 is the default value for
+ * squishy (this value defines the curves to be Catmull-Rom splines)
+ * and the value 1.0 connects all the points with straight lines. Values
+ * within the range -5.0 and 5.0 will deform the curves but will leave them
+ * recognizable and as values increase in magnitude, they will continue to deform.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:curves
+ * @param tightness amount of deformation from the original vertices
+ * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#curveVertex(float, float)
+ */
public void curveTightness(float tightness) {
g.curveTightness(tightness);
}
/**
- * Draws a segment of Catmull-Rom curve.
- *
- * As of 0070, this function no longer doubles the first and
- * last points. The curves are a bit more boring, but it's more
+ * ( begin auto-generated from curve.xml )
+ *
+ * Draws a curved line on the screen. The first and second parameters
+ * specify the beginning control point and the last two parameters specify
+ * the ending control point. The middle parameters specify the start and
+ * stop of the curve. Longer curves can be created by putting a series of
+ * curve() functions together or using curveVertex() . An
+ * additional function called curveTightness() provides control for
+ * the visual quality of the curve. The curve() function is an
+ * implementation of Catmull-Rom splines. Using the 3D version requires
+ * rendering with P3D (see the Environment reference for more information).
+ *
+ * ( end auto-generated )
+ *
+ *
Advanced
+ * As of revision 0070, this function no longer doubles the first
+ * and last points. The curves are a bit more boring, but it's more
* mathematically correct, and properly mirrored in curvePoint().
*
* Identical to typing out:
@@ -8416,6 +9532,19 @@ public void curveTightness(float tightness) {
* curveVertex(x4, y4);
* endShape();
*
+ *
+ * @webref shape:curves
+ * @param x1 coordinates for the beginning control point
+ * @param y1 coordinates for the beginning control point
+ * @param x2 coordinates for the first point
+ * @param y2 coordinates for the first point
+ * @param x3 coordinates for the second point
+ * @param y3 coordinates for the second point
+ * @param x4 coordinates for the ending control point
+ * @param y4 coordinates for the ending control point
+ * @see PGraphics#curveVertex(float, float)
+ * @see PGraphics#curveTightness(float)
+ * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
*/
public void curve(float x1, float y1,
float x2, float y2,
@@ -8425,6 +9554,12 @@ public void curve(float x1, float y1,
}
+ /**
+ * @param z1 coordinates for the beginning control point
+ * @param z2 coordinates for the first point
+ * @param z3 coordinates for the second point
+ * @param z4 coordinates for the ending control point
+ */
public void curve(float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3,
@@ -8434,44 +9569,83 @@ public void curve(float x1, float y1, float z1,
/**
- * If true in PImage, use bilinear interpolation for copy()
- * operations. When inherited by PGraphics, also controls shapes.
+ * ( begin auto-generated from imageMode.xml )
+ *
+ * Modifies the location from which images draw. The default mode is
+ * imageMode(CORNER) , which specifies the location to be the upper
+ * left corner and uses the fourth and fifth parameters of image()
+ * to set the image's width and height. The syntax
+ * imageMode(CORNERS) uses the second and third parameters of
+ * image() to set the location of one corner of the image and uses
+ * the fourth and fifth parameters to set the opposite corner. Use
+ * imageMode(CENTER) to draw images centered at the given x and y
+ * position.
+ *
+ * The parameter to imageMode() must be written in ALL CAPS because
+ * Processing is a case-sensitive language.
+ *
+ * ( end auto-generated )
+ *
+ * @webref image:loading_displaying
+ * @param mode either CORNER, CORNERS, or CENTER
+ * @see PApplet#loadImage(String, String)
+ * @see PImage
+ * @see PGraphics#image(PImage, float, float, float, float)
+ * @see PGraphics#background(float, float, float, float)
*/
- public void smooth() {
- g.smooth();
- }
-
-
- public void smooth(int level) {
- g.smooth(level);
+ public void imageMode(int mode) {
+ g.imageMode(mode);
}
/**
- * Disable smoothing. See smooth().
+ * ( begin auto-generated from image.xml )
+ *
+ * Displays images to the screen. The images must be in the sketch's "data"
+ * directory to load correctly. Select "Add file..." from the "Sketch" menu
+ * to add the image. Processing currently works with GIF, JPEG, and Targa
+ * images. The img parameter specifies the image to display and the
+ * x and y parameters define the location of the image from
+ * its upper-left corner. The image is displayed at its original size
+ * unless the width and height parameters specify a different
+ * size.
+ *
+ * The imageMode() function changes the way the parameters work. For
+ * example, a call to imageMode(CORNERS) will change the
+ * width and height parameters to define the x and y values
+ * of the opposite corner of the image.
+ *
+ * The color of an image may be modified with the tint() function.
+ * This function will maintain transparency for GIF and PNG images.
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
+ * Starting with release 0124, when using the default (JAVA2D) renderer,
+ * smooth() will also improve image quality of resized images.
+ *
+ * @webref image:loading_displaying
+ * @param img the image to display
+ * @param a x-coordinate of the image by default
+ * @param b y-coordinate of the image by default
+ * @see PApplet#loadImage(String, String)
+ * @see PImage
+ * @see PGraphics#imageMode(int)
+ * @see PGraphics#tint(float)
+ * @see PGraphics#background(float, float, float, float)
+ * @see PGraphics#alpha(int)
*/
- public void noSmooth() {
- g.noSmooth();
+ public void image(PImage img, float a, float b) {
+ g.image(img, a, b);
}
/**
- * The mode can only be set to CORNERS, CORNER, and CENTER.
- *
- * Support for CENTER was added in release 0146.
+ * @param c width to display the image by default
+ * @param d height to display the image by default
*/
- public void imageMode(int mode) {
- g.imageMode(mode);
- }
-
-
- public void image(PImage image, float x, float y) {
- g.image(image, x, y);
- }
-
-
- public void image(PImage image, float x, float y, float c, float d) {
- g.image(image, x, y, c, d);
+ public void image(PImage img, float a, float b, float c, float d) {
+ g.image(img, a, b, c, d);
}
@@ -8479,17 +9653,38 @@ public void image(PImage image, float x, float y, float c, float d) {
* Draw an image(), also specifying u/v coordinates.
* In this method, the u, v coordinates are always based on image space
* location, regardless of the current textureMode().
+ *
+ * @nowebref
*/
- public void image(PImage image,
+ public void image(PImage img,
float a, float b, float c, float d,
int u1, int v1, int u2, int v2) {
- g.image(image, a, b, c, d, u1, v1, u2, v2);
+ g.image(img, a, b, c, d, u1, v1, u2, v2);
}
/**
- * Set the orientation for the shape() command (like imageMode() or rectMode()).
- * @param mode Either CORNER, CORNERS, or CENTER.
+ * ( begin auto-generated from shapeMode.xml )
+ *
+ * Modifies the location from which shapes draw. The default mode is
+ * shapeMode(CORNER) , which specifies the location to be the upper
+ * left corner of the shape and uses the third and fourth parameters of
+ * shape() to specify the width and height. The syntax
+ * shapeMode(CORNERS) uses the first and second parameters of
+ * shape() to set the location of one corner and uses the third and
+ * fourth parameters to set the opposite corner. The syntax
+ * shapeMode(CENTER) draws the shape from its center point and uses
+ * the third and forth parameters of shape() to specify the width
+ * and height. The parameter must be written in "ALL CAPS" because
+ * Processing is a case sensitive language.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:loading_displaying
+ * @param mode either CORNER, CORNERS, CENTER
+ * @see PShape
+ * @see PGraphics#shape(PShape)
+ * @see PGraphics#rectMode(int)
*/
public void shapeMode(int mode) {
g.shapeMode(mode);
@@ -8502,6 +9697,33 @@ public void shape(PShape shape) {
/**
+ * ( begin auto-generated from shape.xml )
+ *
+ * Displays shapes to the screen. The shapes must be in the sketch's "data"
+ * directory to load correctly. Select "Add file..." from the "Sketch" menu
+ * to add the shape. Processing currently works with SVG shapes only. The
+ * sh parameter specifies the shape to display and the x and
+ * y parameters define the location of the shape from its upper-left
+ * corner. The shape is displayed at its original size unless the
+ * width and height parameters specify a different size. The
+ * shapeMode() function changes the way the parameters work. A call
+ * to shapeMode(CORNERS) , for example, will change the width and
+ * height parameters to define the x and y values of the opposite corner of
+ * the shape.
+ *
+ * Note complex shapes may draw awkwardly with P3D. This renderer does not
+ * yet support shapes that have holes or complicated breaks.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:loading_displaying
+ * @param shape the shape to display
+ * @param x x-coordinate of the shape
+ * @param y y-coordinate of the shape
+ * @see PShape
+ * @see PApplet#loadShape(String)
+ * @see PGraphics#shapeMode(int)
+ *
* Convenience method to draw at a particular location.
*/
public void shape(PShape shape, float x, float y) {
@@ -8509,24 +9731,61 @@ public void shape(PShape shape, float x, float y) {
}
- public void shape(PShape shape, float x, float y, float c, float d) {
- g.shape(shape, x, y, c, d);
+ /**
+ * @param a x-coordinate of the shape
+ * @param b y-coordinate of the shape
+ * @param c width to display the shape
+ * @param d height to display the shape
+ */
+ public void shape(PShape shape, float a, float b, float c, float d) {
+ g.shape(shape, a, b, c, d);
}
- /**
- * Sets the alignment of the text to one of LEFT, CENTER, or RIGHT.
- * This will also reset the vertical text alignment to BASELINE.
- */
- public void textAlign(int align) {
- g.textAlign(align);
+ public void textAlign(int alignX) {
+ g.textAlign(alignX);
}
/**
- * Sets the horizontal and vertical alignment of the text. The horizontal
- * alignment can be one of LEFT, CENTER, or RIGHT. The vertical alignment
- * can be TOP, BOTTOM, CENTER, or the BASELINE (the default).
+ * ( begin auto-generated from textAlign.xml )
+ *
+ * Sets the current alignment for drawing text. The parameters LEFT,
+ * CENTER, and RIGHT set the display characteristics of the letters in
+ * relation to the values for the x and y parameters of the
+ * text() function.
+ *
+ * In Processing 0125 and later, an optional second parameter can be used
+ * to vertically align the text. BASELINE is the default, and the vertical
+ * alignment will be reset to BASELINE if the second parameter is not used.
+ * The TOP and CENTER parameters are straightforward. The BOTTOM parameter
+ * offsets the line based on the current textDescent() . For multiple
+ * lines, the final line will be aligned to the bottom, with the previous
+ * lines appearing above it.
+ *
+ * When using text() with width and height parameters, BASELINE is
+ * ignored, and treated as TOP. (Otherwise, text would by default draw
+ * outside the box, since BASELINE is the default setting. BASELINE is not
+ * a useful drawing mode for text drawn in a rectangle.)
+ *
+ * The vertical alignment is based on the value of textAscent() ,
+ * which many fonts do not specify correctly. It may be necessary to use a
+ * hack and offset by a few pixels by hand so that the offset looks
+ * correct. To do this as less of a hack, use some percentage of
+ * textAscent() or textDescent() so that the hack works even
+ * if you change the size of the font.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:attributes
+ * @param alignX horizontal alignment, either LEFT, CENTER, or RIGHT
+ * @param alignY vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
+ * @see PApplet#loadFont(String)
+ * @see PFont
+ * @see PGraphics#text(String, float, float)
+ * @see PGraphics#textSize(float)
+ * @see PGraphics#textAscent()
+ * @see PGraphics#textDescent()
*/
public void textAlign(int alignX, int alignY) {
g.textAlign(alignX, alignY);
@@ -8534,9 +9793,17 @@ public void textAlign(int alignX, int alignY) {
/**
- * Returns the ascent of the current font at the current size.
- * This is a method, rather than a variable inside the PGraphics object
- * because it requires calculation.
+ * ( begin auto-generated from textAscent.xml )
+ *
+ * Returns ascent of the current font at its current size. This information
+ * is useful for determining the height of the font above the baseline. For
+ * example, adding the textAscent() and textDescent() values
+ * will give you the total height of the line.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:metrics
+ * @see PGraphics#textDescent()
*/
public float textAscent() {
return g.textAscent();
@@ -8544,9 +9811,17 @@ public float textAscent() {
/**
- * Returns the descent of the current font at the current size.
- * This is a method, rather than a variable inside the PGraphics object
- * because it requires calculation.
+ * ( begin auto-generated from textDescent.xml )
+ *
+ * Returns descent of the current font at its current size. This
+ * information is useful for determining the height of the font below the
+ * baseline. For example, adding the textAscent() and
+ * textDescent() values will give you the total height of the line.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:metrics
+ * @see PGraphics#textAscent()
*/
public float textDescent() {
return g.textDescent();
@@ -8554,9 +9829,33 @@ public float textDescent() {
/**
- * Sets the current font. The font's size will be the "natural"
- * size of this font (the size that was set when using "Create Font").
- * The leading will also be reset.
+ * ( begin auto-generated from textFont.xml )
+ *
+ * Sets the current font that will be drawn with the text()
+ * function. Fonts must be loaded with loadFont() before it can be
+ * used. This font will be used in all subsequent calls to the
+ * text() function. If no size parameter is input, the font
+ * will appear at its original size (the size it was created at with the
+ * "Create Font..." tool) until it is changed with textSize() . Because fonts are usually bitmaped, you should create fonts at
+ * the sizes that will be used most commonly. Using textFont()
+ * without the size parameter will result in the cleanest-looking text. With the default (JAVA2D) and PDF renderers, it's also possible
+ * to enable the use of native fonts via the command
+ * hint(ENABLE_NATIVE_FONTS) . This will produce vector text in
+ * JAVA2D sketches and PDF output in cases where the vector data is
+ * available: when the font is still installed, or the font is created via
+ * the createFont() function (rather than the Create Font tool).
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:loading_displaying
+ * @param which any variable of the type PFont
+ * @see PApplet#createFont(String, float, boolean)
+ * @see PApplet#loadFont(String)
+ * @see PFont
+ * @see PGraphics#text(String, float, float)
+ * @see PGraphics#textSize(float)
*/
public void textFont(PFont which) {
g.textFont(which);
@@ -8564,7 +9863,7 @@ public void textFont(PFont which) {
/**
- * Useful function to set the font and size at the same time.
+ * @param size the size of the letters in units of pixels
*/
public void textFont(PFont which, float size) {
g.textFont(which, size);
@@ -8572,9 +9871,20 @@ public void textFont(PFont which, float size) {
/**
- * Set the text leading to a specific value. If using a custom
- * value for the text leading, you'll have to call textLeading()
- * again after any calls to textSize().
+ * ( begin auto-generated from textLeading.xml )
+ *
+ * Sets the spacing between lines of text in units of pixels. This setting
+ * will be used in all subsequent calls to the text() function.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:attributes
+ * @param leading the size in pixels for spacing between lines
+ * @see PApplet#loadFont(String)
+ * @see PFont#PFont
+ * @see PGraphics#text(String, float, float)
+ * @see PGraphics#textFont(PFont)
+ * @see PGraphics#textSize(float)
*/
public void textLeading(float leading) {
g.textLeading(leading);
@@ -8582,11 +9892,36 @@ public void textLeading(float leading) {
/**
- * Sets the text rendering/placement to be either SCREEN (direct
- * to the screen, exact coordinates, only use the font's original size)
- * or MODEL (the default, where text is manipulated by translate() and
- * can have a textSize). The text size cannot be set when using
- * textMode(SCREEN), because it uses the pixels directly from the font.
+ * ( begin auto-generated from textMode.xml )
+ *
+ * Sets the way text draws to the screen. In the default configuration, the
+ * MODEL mode, it's possible to rotate, scale, and place letters in
+ * two and three dimensional space.
+ *
+ * The SHAPE mode draws text using the the glyph outlines of
+ * individual characters rather than as textures. This mode is only
+ * supported with the PDF and P3D renderer settings. With the
+ * PDF renderer, you must call textMode(SHAPE) before any
+ * other drawing occurs. If the outlines are not available, then
+ * textMode(SHAPE) will be ignored and textMode(MODEL) will
+ * be used instead.
+ *
+ * The textMode(SHAPE) option in P3D can be combined with
+ * beginRaw() to write vector-accurate text to 2D and 3D output
+ * files, for instance DXF or PDF . The SHAPE mode is
+ * not currently optimized for P3D , so if recording shape data, use
+ * textMode(MODEL) until you're ready to capture the geometry with beginRaw() .
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:attributes
+ * @param mode either MODEL or SHAPE
+ * @see PApplet#loadFont(String)
+ * @see PFont#PFont
+ * @see PGraphics#text(String, float, float)
+ * @see PGraphics#textFont(PFont)
+ * @see PGraphics#beginRaw(PGraphics)
+ * @see PApplet#createFont(String, float, boolean)
*/
public void textMode(int mode) {
g.textMode(mode);
@@ -8594,21 +9929,47 @@ public void textMode(int mode) {
/**
- * Sets the text size, also resets the value for the leading.
+ * ( begin auto-generated from textSize.xml )
+ *
+ * Sets the current font size. This size will be used in all subsequent
+ * calls to the text() function. Font size is measured in units of pixels.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:attributes
+ * @param size the size of the letters in units of pixels
+ * @see PApplet#loadFont(String)
+ * @see PFont#PFont
+ * @see PGraphics#text(String, float, float)
+ * @see PGraphics#textFont(PFont)
*/
public void textSize(float size) {
g.textSize(size);
}
+ /**
+ * @param c the character to measure
+ */
public float textWidth(char c) {
return g.textWidth(c);
}
/**
- * Return the width of a line of text. If the text has multiple
- * lines, this returns the length of the longest line.
+ * ( begin auto-generated from textWidth.xml )
+ *
+ * Calculates and returns the width of any character or text string.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:attributes
+ * @param str the String of characters to measure
+ * @see PApplet#loadFont(String)
+ * @see PFont#PFont
+ * @see PGraphics#text(String, float, float)
+ * @see PGraphics#textFont(PFont)
+ * @see PGraphics#textSize(float)
*/
public float textWidth(String str) {
return g.textWidth(str);
@@ -8616,9 +9977,47 @@ public float textWidth(String str) {
/**
- * Draw a single character on screen.
- * Extremely slow when used with textMode(SCREEN) and Java 2D,
- * because loadPixels has to be called first and updatePixels last.
+ * @nowebref
+ */
+ public float textWidth(char[] chars, int start, int length) {
+ return g.textWidth(chars, start, length);
+ }
+
+
+ /**
+ * ( begin auto-generated from text.xml )
+ *
+ * Draws text to the screen. Displays the information specified in the
+ * data or stringdata parameters on the screen in the
+ * position specified by the x and y parameters and the
+ * optional z parameter. A default font will be used unless a font
+ * is set with the textFont() function. Change the color of the text
+ * with the fill() function. The text displays in relation to the
+ * textAlign() function, which gives the option to draw to the left,
+ * right, and center of the coordinates.
+ *
+ * The x2 and y2 parameters define a rectangular area to
+ * display within and may only be used with string data. For text drawn
+ * inside a rectangle, the coordinates are interpreted based on the current
+ * rectMode() setting.
+ *
+ * ( end auto-generated )
+ *
+ * @webref typography:loading_displaying
+ * @param c the alphanumeric character to be displayed
+ * @param x x-coordinate of text
+ * @param y y-coordinate of text
+ * @see PGraphics#textAlign(int, int)
+ * @see PGraphics#textFont(PFont)
+ * @see PGraphics#textMode(int)
+ * @see PGraphics#textSize(float)
+ * @see PGraphics#textLeading(float)
+ * @see PGraphics#textWidth(String)
+ * @see PGraphics#textAscent()
+ * @see PGraphics#textDescent()
+ * @see PGraphics#rectMode(int)
+ * @see PGraphics#fill(int, float)
+ * @see_external String
*/
public void text(char c, float x, float y) {
g.text(c, x, y);
@@ -8626,7 +10025,7 @@ public void text(char c, float x, float y) {
/**
- * Draw a single character on screen (with a z coordinate)
+ * @param z z-coordinate of text
*/
public void text(char c, float x, float y, float z) {
g.text(c, x, y, z);
@@ -8634,6 +10033,7 @@ public void text(char c, float x, float y, float z) {
/**
+ * Advanced
* Draw a chunk of text.
* Newlines that are \n (Unix newline or linefeed char, ascii 10)
* are honored, but \r (carriage return, Windows and Mac OS) are
@@ -8644,6 +10044,20 @@ public void text(String str, float x, float y) {
}
+ /**
+ * Advanced
+ * Method to draw text from an array of chars. This method will usually be
+ * more efficient than drawing from a String object, because the String will
+ * not be converted to a char array before drawing.
+ * @param chars the alphanumberic symbols to be displayed
+ * @param start array index at which to start writing characters
+ * @param stop array index at which to stop writing characters
+ */
+ public void text(char[] chars, int start, int stop, float x, float y) {
+ g.text(chars, start, stop, x, y);
+ }
+
+
/**
* Same as above but with a z coordinate.
*/
@@ -8652,7 +10066,14 @@ public void text(String str, float x, float y, float z) {
}
+ public void text(char[] chars, int start, int stop,
+ float x, float y, float z) {
+ g.text(chars, start, stop, x, y, z);
+ }
+
+
/**
+ * Advanced
* Draw text in a box that is constrained to a particular size.
* The current rectMode() determines what the coordinates mean
* (whether x1/y1/x2/y2 or x/y/w/h).
@@ -8664,6 +10085,11 @@ public void text(String str, float x, float y, float z) {
* Newlines that are \n (Unix newline or linefeed char, ascii 10)
* are honored, and \r (carriage return, Windows and Mac OS) are
* ignored.
+ *
+ * @param x1 by default, the x-coordinate of text, see rectMode() for more info
+ * @param y1 by default, the y-coordinate of text, see rectMode() for more info
+ * @param x2 by default, the width of the text box, see rectMode() for more info
+ * @param y2 by default, the height of the text box, see rectMode() for more info
*/
public void text(String str, float x1, float y1, float x2, float y2) {
g.text(str, x1, y1, x2, y2);
@@ -8686,6 +10112,8 @@ public void text(int num, float x, float y, float z) {
* Users who want more control should use their own nf() cmmand,
* or if they want the long, ugly version of float,
* use String.valueOf() to convert the float to a String first.
+ *
+ * @param num the numeric value to be displayed
*/
public void text(float num, float x, float y) {
g.text(num, x, y);
@@ -8698,7 +10126,101 @@ public void text(float num, float x, float y, float z) {
/**
- * Push a copy of the current transformation matrix onto the stack.
+ * ( begin auto-generated from push.xml )
+ *
+ * The push() function saves the current drawing style
+ * settings and transformations, while pop() restores these
+ * settings. Note that these functions are always used together.
+ * They allow you to change the style and transformation settings
+ * and later return to what you had. When a new state is started
+ * with push(), it builds on the current style and transform
+ * information.
+ *
+ * push() stores information related to the current
+ * transformation state and style settings controlled by the
+ * following functions: rotate() , translate() ,
+ * scale() , fill() , stroke() , tint() ,
+ * strokeWeight() , strokeCap() , strokeJoin() ,
+ * imageMode() , rectMode() , ellipseMode() ,
+ * colorMode() , textAlign() , textFont() ,
+ * textMode() , textSize() , textLeading() .
+ *
+ * The push() and pop() functions were added with
+ * Processing 3.5. They can be used in place of pushMatrix() ,
+ * popMatrix() , pushStyles() , and popStyles() .
+ * The difference is that push() and pop() control both the
+ * transformations (rotate, scale, translate) and the drawing styles
+ * at the same time.
+ *
+ * ( end auto-generated )
+ *
+ * @webref structure
+ * @see PGraphics#pop()
+ */
+ public void push() {
+ g.push();
+ }
+
+
+ /**
+ * ( begin auto-generated from pop.xml )
+ *
+ * The pop() function restores the previous drawing style
+ * settings and transformations after push() has changed them.
+ * Note that these functions are always used together. They allow
+ * you to change the style and transformation settings and later
+ * return to what you had. When a new state is started with push(),
+ * it builds on the current style and transform information.
+ *
+ *
+ * push() stores information related to the current
+ * transformation state and style settings controlled by the
+ * following functions: rotate() , translate() ,
+ * scale() , fill() , stroke() , tint() ,
+ * strokeWeight() , strokeCap() , strokeJoin() ,
+ * imageMode() , rectMode() , ellipseMode() ,
+ * colorMode() , textAlign() , textFont() ,
+ * textMode() , textSize() , textLeading() .
+ *
+ * The push() and pop() functions were added with
+ * Processing 3.5. They can be used in place of pushMatrix() ,
+ * popMatrix() , pushStyles() , and popStyles() .
+ * The difference is that push() and pop() control both the
+ * transformations (rotate, scale, translate) and the drawing styles
+ * at the same time.
+ *
+ * ( end auto-generated )
+ *
+ * @webref structure
+ * @see PGraphics#push()
+ */
+ public void pop() {
+ g.pop();
+ }
+
+
+ /**
+ * ( begin auto-generated from pushMatrix.xml )
+ *
+ * Pushes the current transformation matrix onto the matrix stack.
+ * Understanding pushMatrix() and popMatrix() requires
+ * understanding the concept of a matrix stack. The pushMatrix()
+ * function saves the current coordinate system to the stack and
+ * popMatrix() restores the prior coordinate system.
+ * pushMatrix() and popMatrix() are used in conjuction with
+ * the other transformation functions and may be embedded to control the
+ * scope of the transformations.
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#translate(float, float, float)
+ * @see PGraphics#scale(float)
+ * @see PGraphics#rotate(float)
+ * @see PGraphics#rotateX(float)
+ * @see PGraphics#rotateY(float)
+ * @see PGraphics#rotateZ(float)
*/
public void pushMatrix() {
g.pushMatrix();
@@ -8706,7 +10228,20 @@ public void pushMatrix() {
/**
- * Replace the current transformation matrix with the top of the stack.
+ * ( begin auto-generated from popMatrix.xml )
+ *
+ * Pops the current transformation matrix off the matrix stack.
+ * Understanding pushing and popping requires understanding the concept of
+ * a matrix stack. The pushMatrix() function saves the current
+ * coordinate system to the stack and popMatrix() restores the prior
+ * coordinate system. pushMatrix() and popMatrix() are used
+ * in conjuction with the other transformation functions and may be
+ * embedded to control the scope of the transformations.
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @see PGraphics#pushMatrix()
*/
public void popMatrix() {
g.popMatrix();
@@ -8714,29 +10249,77 @@ public void popMatrix() {
/**
- * Translate in X and Y.
+ * ( begin auto-generated from translate.xml )
+ *
+ * Specifies an amount to displace objects within the display window. The
+ * x parameter specifies left/right translation, the y
+ * parameter specifies up/down translation, and the z parameter
+ * specifies translations toward/away from the screen. Using this function
+ * with the z parameter requires using P3D as a parameter in
+ * combination with size as shown in the above example. Transformations
+ * apply to everything that happens after and subsequent calls to the
+ * function accumulates the effect. For example, calling translate(50,
+ * 0) and then translate(20, 0) is the same as translate(70,
+ * 0) . If translate() is called within draw() , the
+ * transformation is reset when the loop begins again. This function can be
+ * further controlled by the pushMatrix() and popMatrix() .
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param x left/right translation
+ * @param y up/down translation
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#rotate(float)
+ * @see PGraphics#rotateX(float)
+ * @see PGraphics#rotateY(float)
+ * @see PGraphics#rotateZ(float)
+ * @see PGraphics#scale(float, float, float)
*/
- public void translate(float tx, float ty) {
- g.translate(tx, ty);
+ public void translate(float x, float y) {
+ g.translate(x, y);
}
/**
- * Translate in X, Y, and Z.
+ * @param z forward/backward translation
*/
- public void translate(float tx, float ty, float tz) {
- g.translate(tx, ty, tz);
+ public void translate(float x, float y, float z) {
+ g.translate(x, y, z);
}
/**
- * Two dimensional rotation.
+ * ( begin auto-generated from rotate.xml )
+ *
+ * Rotates a shape the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to TWO_PI) or
+ * converted to radians with the radians() function.
+ *
+ * Objects are always rotated around their relative position to the origin
+ * and positive numbers rotate objects in a clockwise direction.
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * rotate(HALF_PI) and then rotate(HALF_PI) is the same as
+ * rotate(PI) . All tranformations are reset when draw()
+ * begins again.
+ *
+ * Technically, rotate() multiplies the current transformation
+ * matrix by a rotation matrix. This function can be further controlled by
+ * the pushMatrix() and popMatrix() .
*
- * 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.
+ * ( end auto-generated )
*
- * Additional background .
+ * @webref transform
+ * @param angle angle of rotation specified in radians
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#rotateX(float)
+ * @see PGraphics#rotateY(float)
+ * @see PGraphics#rotateZ(float)
+ * @see PGraphics#scale(float, float, float)
+ * @see PApplet#radians(float)
*/
public void rotate(float angle) {
g.rotate(angle);
@@ -8744,7 +10327,32 @@ public void rotate(float angle) {
/**
- * Rotate around the X axis.
+ * ( begin auto-generated from rotateX.xml )
+ *
+ * Rotates a shape around the x-axis the amount specified by the
+ * angle parameter. Angles should be specified in radians (values
+ * from 0 to PI*2) or converted to radians with the radians()
+ * function. Objects are always rotated around their relative position to
+ * the origin and positive numbers rotate objects in a counterclockwise
+ * direction. Transformations apply to everything that happens after and
+ * subsequent calls to the function accumulates the effect. For example,
+ * calling rotateX(PI/2) and then rotateX(PI/2) is the same
+ * as rotateX(PI) . If rotateX() is called within the
+ * draw() , the transformation is reset when the loop begins again.
+ * This function requires using P3D as a third parameter to size()
+ * as shown in the example above.
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param angle angle of rotation specified in radians
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#rotate(float)
+ * @see PGraphics#rotateY(float)
+ * @see PGraphics#rotateZ(float)
+ * @see PGraphics#scale(float, float, float)
+ * @see PGraphics#translate(float, float, float)
*/
public void rotateX(float angle) {
g.rotateX(angle);
@@ -8752,7 +10360,32 @@ public void rotateX(float angle) {
/**
- * Rotate around the Y axis.
+ * ( begin auto-generated from rotateY.xml )
+ *
+ * Rotates a shape around the y-axis the amount specified by the
+ * angle parameter. Angles should be specified in radians (values
+ * from 0 to PI*2) or converted to radians with the radians()
+ * function. Objects are always rotated around their relative position to
+ * the origin and positive numbers rotate objects in a counterclockwise
+ * direction. Transformations apply to everything that happens after and
+ * subsequent calls to the function accumulates the effect. For example,
+ * calling rotateY(PI/2) and then rotateY(PI/2) is the same
+ * as rotateY(PI) . If rotateY() is called within the
+ * draw() , the transformation is reset when the loop begins again.
+ * This function requires using P3D as a third parameter to size()
+ * as shown in the examples above.
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param angle angle of rotation specified in radians
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#rotate(float)
+ * @see PGraphics#rotateX(float)
+ * @see PGraphics#rotateZ(float)
+ * @see PGraphics#scale(float, float, float)
+ * @see PGraphics#translate(float, float, float)
*/
public void rotateY(float angle) {
g.rotateY(angle);
@@ -8760,12 +10393,32 @@ public void rotateY(float angle) {
/**
- * Rotate around the Z axis.
+ * ( begin auto-generated from rotateZ.xml )
+ *
+ * Rotates a shape around the z-axis the amount specified by the
+ * angle parameter. Angles should be specified in radians (values
+ * from 0 to PI*2) or converted to radians with the radians()
+ * function. Objects are always rotated around their relative position to
+ * the origin and positive numbers rotate objects in a counterclockwise
+ * direction. Transformations apply to everything that happens after and
+ * subsequent calls to the function accumulates the effect. For example,
+ * calling rotateZ(PI/2) and then rotateZ(PI/2) is the same
+ * as rotateZ(PI) . If rotateZ() is called within the
+ * draw() , the transformation is reset when the loop begins again.
+ * This function requires using P3D as a third parameter to size()
+ * as shown in the examples above.
*
- * The functions rotate() and rotateZ() are identical, it's just that it make
- * sense to have rotate() and then rotateX() and rotateY() when using 3D;
- * nor does it make sense to use a function called rotateZ() if you're only
- * doing things in 2D. so we just decided to have them both be the same.
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param angle angle of rotation specified in radians
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#rotate(float)
+ * @see PGraphics#rotateX(float)
+ * @see PGraphics#rotateY(float)
+ * @see PGraphics#scale(float, float, float)
+ * @see PGraphics#translate(float, float, float)
*/
public void rotateZ(float angle) {
g.rotateZ(angle);
@@ -8773,15 +10426,45 @@ public void rotateZ(float angle) {
/**
+ * Advanced
* Rotate about a vector in space. Same as the glRotatef() function.
+ * @nowebref
+ * @param x
+ * @param y
+ * @param z
*/
- public void rotate(float angle, float vx, float vy, float vz) {
- g.rotate(angle, vx, vy, vz);
+ public void rotate(float angle, float x, float y, float z) {
+ g.rotate(angle, x, y, z);
}
/**
- * Scale in all dimensions.
+ * ( begin auto-generated from scale.xml )
+ *
+ * Increases or decreases the size of a shape by expanding and contracting
+ * vertices. Objects always scale from their relative origin to the
+ * coordinate system. Scale values are specified as decimal percentages.
+ * For example, the function call scale(2.0) increases the dimension
+ * of a shape by 200%. Transformations apply to everything that happens
+ * after and subsequent calls to the function multiply the effect. For
+ * example, calling scale(2.0) and then scale(1.5) is the
+ * same as scale(3.0) . If scale() is called within
+ * draw() , the transformation is reset when the loop begins again.
+ * Using this fuction with the z parameter requires using P3D as a
+ * parameter for size() as shown in the example above. This function
+ * can be further controlled by pushMatrix() and popMatrix() .
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param s percentage to scale the object
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#translate(float, float, float)
+ * @see PGraphics#rotate(float)
+ * @see PGraphics#rotateX(float)
+ * @see PGraphics#rotateY(float)
+ * @see PGraphics#rotateZ(float)
*/
public void scale(float s) {
g.scale(s);
@@ -8789,18 +10472,22 @@ public void scale(float s) {
/**
+ * Advanced
* Scale in X and Y. Equivalent to scale(sx, sy, 1).
*
* Not recommended for use in 3D, because the z-dimension is just
* scaled by 1, since there's no way to know what else to scale it by.
+ *
+ * @param x percentage to scale the object in the x-axis
+ * @param y percentage to scale the object in the y-axis
*/
- public void scale(float sx, float sy) {
- g.scale(sx, sy);
+ public void scale(float x, float y) {
+ g.scale(x, y);
}
/**
- * Scale in X, Y, and Z.
+ * @param z percentage to scale the object in the z-axis
*/
public void scale(float x, float y, float z) {
g.scale(x, y, z);
@@ -8808,7 +10495,33 @@ public void scale(float x, float y, float z) {
/**
- * Shear along X axis
+ * ( begin auto-generated from shearX.xml )
+ *
+ * Shears a shape around the x-axis the amount specified by the
+ * angle parameter. Angles should be specified in radians (values
+ * from 0 to PI*2) or converted to radians with the radians()
+ * function. Objects are always sheared around their relative position to
+ * the origin and positive numbers shear objects in a clockwise direction.
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * shearX(PI/2) and then shearX(PI/2) is the same as
+ * shearX(PI) . If shearX() is called within the
+ * draw() , the transformation is reset when the loop begins again.
+ *
+ * Technically, shearX() multiplies the current transformation
+ * matrix by a rotation matrix. This function can be further controlled by
+ * the pushMatrix() and popMatrix() functions.
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param angle angle of shear specified in radians
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#shearY(float)
+ * @see PGraphics#scale(float, float, float)
+ * @see PGraphics#translate(float, float, float)
+ * @see PApplet#radians(float)
*/
public void shearX(float angle) {
g.shearX(angle);
@@ -8816,7 +10529,33 @@ public void shearX(float angle) {
/**
- * Skew along Y axis
+ * ( begin auto-generated from shearY.xml )
+ *
+ * Shears a shape around the y-axis the amount specified by the
+ * angle parameter. Angles should be specified in radians (values
+ * from 0 to PI*2) or converted to radians with the radians()
+ * function. Objects are always sheared around their relative position to
+ * the origin and positive numbers shear objects in a clockwise direction.
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * shearY(PI/2) and then shearY(PI/2) is the same as
+ * shearY(PI) . If shearY() is called within the
+ * draw() , the transformation is reset when the loop begins again.
+ *
+ * Technically, shearY() multiplies the current transformation
+ * matrix by a rotation matrix. This function can be further controlled by
+ * the pushMatrix() and popMatrix() functions.
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @param angle angle of shear specified in radians
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#shearX(float)
+ * @see PGraphics#scale(float, float, float)
+ * @see PGraphics#translate(float, float, float)
+ * @see PApplet#radians(float)
*/
public void shearY(float angle) {
g.shearY(angle);
@@ -8824,13 +10563,41 @@ public void shearY(float angle) {
/**
- * Set the current transformation matrix to identity.
+ * ( begin auto-generated from resetMatrix.xml )
+ *
+ * Replaces the current matrix with the identity matrix. The equivalent
+ * function in OpenGL is glLoadIdentity().
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#applyMatrix(PMatrix)
+ * @see PGraphics#printMatrix()
*/
public void resetMatrix() {
g.resetMatrix();
}
+ /**
+ * ( begin auto-generated from applyMatrix.xml )
+ *
+ * Multiplies the current matrix by the one specified through the
+ * parameters. This is very slow because it will try to calculate the
+ * inverse of the transform, so avoid it whenever possible. The equivalent
+ * function in OpenGL is glMultMatrix().
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @source
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#resetMatrix()
+ * @see PGraphics#printMatrix()
+ */
public void applyMatrix(PMatrix source) {
g.applyMatrix(source);
}
@@ -8842,7 +10609,12 @@ public void applyMatrix(PMatrix2D source) {
/**
- * Apply a 3x2 affine transformation matrix.
+ * @param n00 numbers which define the 4x4 matrix to be multiplied
+ * @param n01 numbers which define the 4x4 matrix to be multiplied
+ * @param n02 numbers which define the 4x4 matrix to be multiplied
+ * @param n10 numbers which define the 4x4 matrix to be multiplied
+ * @param n11 numbers which define the 4x4 matrix to be multiplied
+ * @param n12 numbers which define the 4x4 matrix to be multiplied
*/
public void applyMatrix(float n00, float n01, float n02,
float n10, float n11, float n12) {
@@ -8856,7 +10628,16 @@ public void applyMatrix(PMatrix3D source) {
/**
- * Apply a 4x4 transformation matrix.
+ * @param n03 numbers which define the 4x4 matrix to be multiplied
+ * @param n13 numbers which define the 4x4 matrix to be multiplied
+ * @param n20 numbers which define the 4x4 matrix to be multiplied
+ * @param n21 numbers which define the 4x4 matrix to be multiplied
+ * @param n22 numbers which define the 4x4 matrix to be multiplied
+ * @param n23 numbers which define the 4x4 matrix to be multiplied
+ * @param n30 numbers which define the 4x4 matrix to be multiplied
+ * @param n31 numbers which define the 4x4 matrix to be multiplied
+ * @param n32 numbers which define the 4x4 matrix to be multiplied
+ * @param n33 numbers which define the 4x4 matrix to be multiplied
*/
public void applyMatrix(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
@@ -8914,28 +10695,113 @@ public void setMatrix(PMatrix3D source) {
/**
- * Print the current model (or "transformation") matrix.
+ * ( begin auto-generated from printMatrix.xml )
+ *
+ * Prints the current matrix to the Console (the text window at the bottom
+ * of Processing).
+ *
+ * ( end auto-generated )
+ *
+ * @webref transform
+ * @see PGraphics#pushMatrix()
+ * @see PGraphics#popMatrix()
+ * @see PGraphics#resetMatrix()
+ * @see PGraphics#applyMatrix(PMatrix)
*/
public void printMatrix() {
g.printMatrix();
}
+ /**
+ * ( begin auto-generated from beginCamera.xml )
+ *
+ * The beginCamera() and endCamera() functions enable
+ * advanced customization of the camera space. The functions are useful if
+ * you want to more control over camera movement, however for most users,
+ * the camera() function will be sufficient. The camera
+ * functions will replace any transformations (such as rotate() or
+ * translate() ) that occur before them in draw() , but they
+ * will not automatically replace the camera transform itself. For this
+ * reason, camera functions should be placed at the beginning of
+ * draw() (so that transformations happen afterwards), and the
+ * camera() function can be used after beginCamera() if you
+ * want to reset the camera before applying transformations. This function sets the matrix mode to the camera matrix so calls such
+ * as translate() , rotate() , applyMatrix() and resetMatrix()
+ * affect the camera. beginCamera() should always be used with a
+ * following endCamera() and pairs of beginCamera() and
+ * endCamera() cannot be nested.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ * @see PGraphics#camera()
+ * @see PGraphics#endCamera()
+ * @see PGraphics#applyMatrix(PMatrix)
+ * @see PGraphics#resetMatrix()
+ * @see PGraphics#translate(float, float, float)
+ * @see PGraphics#scale(float, float, float)
+ */
public void beginCamera() {
g.beginCamera();
}
+ /**
+ * ( begin auto-generated from endCamera.xml )
+ *
+ * The beginCamera() and endCamera() functions enable
+ * advanced customization of the camera space. Please see the reference for
+ * beginCamera() for a description of how the functions are used.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ * @see PGraphics#beginCamera()
+ * @see PGraphics#camera(float, float, float, float, float, float, float, float, float)
+ */
public void endCamera() {
g.endCamera();
}
+ /**
+ * ( begin auto-generated from camera.xml )
+ *
+ * Sets the position of the camera through setting the eye position, the
+ * center of the scene, and which axis is facing upward. Moving the eye
+ * position and the direction it is pointing (the center of the scene)
+ * allows the images to be seen from different angles. The version without
+ * any parameters sets the camera to the default position, pointing to the
+ * center of the display window with the Y axis as up. The default values
+ * are camera(width/2.0, height/2.0, (height/2.0) / tan(PI*30.0 /
+ * 180.0), width/2.0, height/2.0, 0, 0, 1, 0) . This function is similar
+ * to gluLookAt() in OpenGL, but it first clears the current camera settings.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ * @see PGraphics#beginCamera()
+ * @see PGraphics#endCamera()
+ * @see PGraphics#frustum(float, float, float, float, float, float)
+ */
public void camera() {
g.camera();
}
+ /**
+ * @param eyeX x-coordinate for the eye
+ * @param eyeY y-coordinate for the eye
+ * @param eyeZ z-coordinate for the eye
+ * @param centerX x-coordinate for the center of the scene
+ * @param centerY y-coordinate for the center of the scene
+ * @param centerZ z-coordinate for the center of the scene
+ * @param upX usually 0.0, 1.0, or -1.0
+ * @param upY usually 0.0, 1.0, or -1.0
+ * @param upZ usually 0.0, 1.0, or -1.0
+ */
public void camera(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ) {
@@ -8943,22 +10809,58 @@ public void camera(float eyeX, float eyeY, float eyeZ,
}
+ /**
+ * ( begin auto-generated from printCamera.xml )
+ *
+ * Prints the current camera matrix to the Console (the text window at the
+ * bottom of Processing).
+ *
+ * ( end auto-generated )
+ * @webref lights_camera:camera
+ * @see PGraphics#camera(float, float, float, float, float, float, float, float, float)
+ */
public void printCamera() {
g.printCamera();
}
+ /**
+ * ( begin auto-generated from ortho.xml )
+ *
+ * Sets an orthographic projection and defines a parallel clipping volume.
+ * All objects with the same dimension appear the same size, regardless of
+ * whether they are near or far from the camera. The parameters to this
+ * function specify the clipping volume where left and right are the
+ * minimum and maximum x values, top and bottom are the minimum and maximum
+ * y values, and near and far are the minimum and maximum z values. If no
+ * parameters are given, the default is used: ortho(0, width, 0, height,
+ * -10, 10).
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ */
public void ortho() {
g.ortho();
}
+ /**
+ * @param left left plane of the clipping volume
+ * @param right right plane of the clipping volume
+ * @param bottom bottom plane of the clipping volume
+ * @param top top plane of the clipping volume
+ */
public void ortho(float left, float right,
float bottom, float top) {
g.ortho(left, right, bottom, top);
}
+ /**
+ * @param near maximum distance from the origin to the viewer
+ * @param far maximum distance from the origin away from the viewer
+ */
public void ortho(float left, float right,
float bottom, float top,
float near, float far) {
@@ -8966,16 +10868,61 @@ public void ortho(float left, float right,
}
+ /**
+ * ( begin auto-generated from perspective.xml )
+ *
+ * Sets a perspective projection applying foreshortening, making distant
+ * objects appear smaller than closer ones. The parameters define a viewing
+ * volume with the shape of truncated pyramid. Objects near to the front of
+ * the volume appear their actual size, while farther objects appear
+ * smaller. This projection simulates the perspective of the world more
+ * accurately than orthographic projection. The version of perspective
+ * without parameters sets the default perspective and the version with
+ * four parameters allows the programmer to set the area precisely. The
+ * default values are: perspective(PI/3.0, width/height, cameraZ/10.0,
+ * cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ */
public void perspective() {
g.perspective();
}
+ /**
+ * @param fovy field-of-view angle (in radians) for vertical direction
+ * @param aspect ratio of width to height
+ * @param zNear z-position of nearest clipping plane
+ * @param zFar z-position of farthest clipping plane
+ */
public void perspective(float fovy, float aspect, float zNear, float zFar) {
g.perspective(fovy, aspect, zNear, zFar);
}
+ /**
+ * ( begin auto-generated from frustum.xml )
+ *
+ * Sets a perspective matrix defined through the parameters. Works like
+ * glFrustum, except it wipes out the current perspective matrix rather
+ * than muliplying itself with it.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ * @param left left coordinate of the clipping plane
+ * @param right right coordinate of the clipping plane
+ * @param bottom bottom coordinate of the clipping plane
+ * @param top top coordinate of the clipping plane
+ * @param near near component of the clipping plane; must be greater than zero
+ * @param far far component of the clipping plane; must be greater than the near value
+ * @see PGraphics#camera(float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#beginCamera()
+ * @see PGraphics#endCamera()
+ * @see PGraphics#perspective(float, float, float, float)
+ */
public void frustum(float left, float right,
float bottom, float top,
float near, float far) {
@@ -8983,15 +10930,35 @@ public void frustum(float left, float right,
}
+ /**
+ * ( begin auto-generated from printProjection.xml )
+ *
+ * Prints the current projection matrix to the Console (the text window at
+ * the bottom of Processing).
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:camera
+ * @see PGraphics#camera(float, float, float, float, float, float, float, float, float)
+ */
public void printProjection() {
g.printProjection();
}
/**
- * Given an x and y coordinate, returns the x position of where
- * that point would be placed on screen, once affected by translate(),
- * scale(), or any other transformations.
+ * ( begin auto-generated from screenX.xml )
+ *
+ * Takes a three-dimensional X, Y, Z position and returns the X value for
+ * where it will appear on a (two-dimensional) screen.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:coordinates
+ * @param x 3D x-coordinate to be mapped
+ * @param y 3D y-coordinate to be mapped
+ * @see PGraphics#screenY(float, float, float)
+ * @see PGraphics#screenZ(float, float, float)
*/
public float screenX(float x, float y) {
return g.screenX(x, y);
@@ -8999,9 +10966,18 @@ public float screenX(float x, float y) {
/**
- * Given an x and y coordinate, returns the y position of where
- * that point would be placed on screen, once affected by translate(),
- * scale(), or any other transformations.
+ * ( begin auto-generated from screenY.xml )
+ *
+ * Takes a three-dimensional X, Y, Z position and returns the Y value for
+ * where it will appear on a (two-dimensional) screen.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:coordinates
+ * @param x 3D x-coordinate to be mapped
+ * @param y 3D y-coordinate to be mapped
+ * @see PGraphics#screenX(float, float, float)
+ * @see PGraphics#screenZ(float, float, float)
*/
public float screenY(float x, float y) {
return g.screenY(x, y);
@@ -9009,11 +10985,7 @@ public float screenY(float x, float y) {
/**
- * Maps a three dimensional point to its placement on-screen.
- *
- * Given an (x, y, z) coordinate, returns the x position of where
- * that point would be placed on screen, once affected by translate(),
- * scale(), or any other transformations.
+ * @param z 3D z-coordinate to be mapped
*/
public float screenX(float x, float y, float z) {
return g.screenX(x, y, z);
@@ -9021,11 +10993,7 @@ public float screenX(float x, float y, float z) {
/**
- * Maps a three dimensional point to its placement on-screen.
- *
- * Given an (x, y, z) coordinate, returns the y position of where
- * that point would be placed on screen, once affected by translate(),
- * scale(), or any other transformations.
+ * @param z 3D z-coordinate to be mapped
*/
public float screenY(float x, float y, float z) {
return g.screenY(x, y, z);
@@ -9033,15 +11001,19 @@ public float screenY(float x, float y, float z) {
/**
- * Maps a three dimensional point to its placement on-screen.
- *
- * Given an (x, y, z) coordinate, returns its z value.
- * This value can be used to determine if an (x, y, z) coordinate
- * is in front or in back of another (x, y, z) coordinate.
- * The units are based on how the zbuffer is set up, and don't
- * relate to anything "real". They're only useful for in
- * comparison to another value obtained from screenZ(),
- * or directly out of the zbuffer[].
+ * ( begin auto-generated from screenZ.xml )
+ *
+ * Takes a three-dimensional X, Y, Z position and returns the Z value for
+ * where it will appear on a (two-dimensional) screen.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:coordinates
+ * @param x 3D x-coordinate to be mapped
+ * @param y 3D y-coordinate to be mapped
+ * @param z 3D z-coordinate to be mapped
+ * @see PGraphics#screenX(float, float, float)
+ * @see PGraphics#screenY(float, float, float)
*/
public float screenZ(float x, float y, float z) {
return g.screenZ(x, y, z);
@@ -9049,13 +11021,29 @@ public float screenZ(float x, float y, float z) {
/**
- * Returns the model space x value for an x, y, z coordinate.
- *
- * This will give you a coordinate after it has been transformed
- * by translate(), rotate(), and camera(), but not yet transformed
- * by the projection matrix. For instance, his can be useful for
- * figuring out how points in 3D space relate to the edge
- * coordinates of a shape.
+ * ( begin auto-generated from modelX.xml )
+ *
+ * Returns the three-dimensional X, Y, Z position in model space. This
+ * returns the X value for a given coordinate based on the current set of
+ * transformations (scale, rotate, translate, etc.) The X value can be used
+ * to place an object in space relative to the location of the original
+ * point once the transformations are no longer in use.
+ *
+ * In the example, the modelX() , modelY() , and
+ * modelZ() functions record the location of a box in space after
+ * being placed using a series of translate and rotate commands. After
+ * popMatrix() is called, those transformations no longer apply, but the
+ * (x, y, z) coordinate returned by the model functions is used to place
+ * another box in the same location.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:coordinates
+ * @param x 3D x-coordinate to be mapped
+ * @param y 3D y-coordinate to be mapped
+ * @param z 3D z-coordinate to be mapped
+ * @see PGraphics#modelY(float, float, float)
+ * @see PGraphics#modelZ(float, float, float)
*/
public float modelX(float x, float y, float z) {
return g.modelX(x, y, z);
@@ -9063,7 +11051,29 @@ public float modelX(float x, float y, float z) {
/**
- * Returns the model space y value for an x, y, z coordinate.
+ * ( begin auto-generated from modelY.xml )
+ *
+ * Returns the three-dimensional X, Y, Z position in model space. This
+ * returns the Y value for a given coordinate based on the current set of
+ * transformations (scale, rotate, translate, etc.) The Y value can be used
+ * to place an object in space relative to the location of the original
+ * point once the transformations are no longer in use.
+ *
+ * In the example, the modelX() , modelY() , and
+ * modelZ() functions record the location of a box in space after
+ * being placed using a series of translate and rotate commands. After
+ * popMatrix() is called, those transformations no longer apply, but the
+ * (x, y, z) coordinate returned by the model functions is used to place
+ * another box in the same location.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:coordinates
+ * @param x 3D x-coordinate to be mapped
+ * @param y 3D y-coordinate to be mapped
+ * @param z 3D z-coordinate to be mapped
+ * @see PGraphics#modelX(float, float, float)
+ * @see PGraphics#modelZ(float, float, float)
*/
public float modelY(float x, float y, float z) {
return g.modelY(x, y, z);
@@ -9071,18 +11081,79 @@ public float modelY(float x, float y, float z) {
/**
- * Returns the model space z value for an x, y, z coordinate.
+ * ( begin auto-generated from modelZ.xml )
+ *
+ * Returns the three-dimensional X, Y, Z position in model space. This
+ * returns the Z value for a given coordinate based on the current set of
+ * transformations (scale, rotate, translate, etc.) The Z value can be used
+ * to place an object in space relative to the location of the original
+ * point once the transformations are no longer in use.
+ *
+ * In the example, the modelX() , modelY() , and
+ * modelZ() functions record the location of a box in space after
+ * being placed using a series of translate and rotate commands. After
+ * popMatrix() is called, those transformations no longer apply, but the
+ * (x, y, z) coordinate returned by the model functions is used to place
+ * another box in the same location.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:coordinates
+ * @param x 3D x-coordinate to be mapped
+ * @param y 3D y-coordinate to be mapped
+ * @param z 3D z-coordinate to be mapped
+ * @see PGraphics#modelX(float, float, float)
+ * @see PGraphics#modelY(float, float, float)
*/
public float modelZ(float x, float y, float z) {
return g.modelZ(x, y, z);
}
+ /**
+ * ( begin auto-generated from pushStyle.xml )
+ *
+ * The pushStyle() function saves the current style settings and
+ * popStyle() restores the prior settings. Note that these functions
+ * are always used together. They allow you to change the style settings
+ * and later return to what you had. When a new style is started with
+ * pushStyle() , it builds on the current style information. The
+ * pushStyle() and popStyle() functions can be embedded to
+ * provide more control (see the second example above for a demonstration.)
+ *
+ * The style information controlled by the following functions are included
+ * in the style:
+ * fill(), stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(),
+ * imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
+ * textAlign(), textFont(), textMode(), textSize(), textLeading(),
+ * emissive(), specular(), shininess(), ambient()
+ *
+ * ( end auto-generated )
+ *
+ * @webref structure
+ * @see PGraphics#popStyle()
+ */
public void pushStyle() {
g.pushStyle();
}
+ /**
+ * ( begin auto-generated from popStyle.xml )
+ *
+ * The pushStyle() function saves the current style settings and
+ * popStyle() restores the prior settings; these functions are
+ * always used together. They allow you to change the style settings and
+ * later return to what you had. When a new style is started with
+ * pushStyle() , it builds on the current style information. The
+ * pushStyle() and popStyle() functions can be embedded to
+ * provide more control (see the second example above for a demonstration.)
+ *
+ * ( end auto-generated )
+ *
+ * @webref structure
+ * @see PGraphics#pushStyle()
+ */
public void popStyle() {
g.popStyle();
}
@@ -9093,40 +11164,153 @@ public void style(PStyle s) {
}
+ /**
+ * ( begin auto-generated from strokeWeight.xml )
+ *
+ * Sets the width of the stroke used for lines, points, and the border
+ * around shapes. All widths are set in units of pixels.
+ *
+ * When drawing with P3D, series of connected lines (such as the stroke
+ * around a polygon, triangle, or ellipse) produce unattractive results
+ * when a thick stroke weight is set (see
+ * Issue 123 ). With P3D, the minimum and maximum values for
+ * strokeWeight() are controlled by the graphics card and the
+ * operating system's OpenGL implementation. For instance, the thickness
+ * may not go higher than 10 pixels.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:attributes
+ * @param weight the weight (in pixels) of the stroke
+ * @see PGraphics#stroke(int, float)
+ * @see PGraphics#strokeJoin(int)
+ * @see PGraphics#strokeCap(int)
+ */
public void strokeWeight(float weight) {
g.strokeWeight(weight);
}
+ /**
+ * ( begin auto-generated from strokeJoin.xml )
+ *
+ * Sets the style of the joints which connect line segments. These joints
+ * are either mitered, beveled, or rounded and specified with the
+ * corresponding parameters MITER, BEVEL, and ROUND. The default joint is
+ * MITER.
+ *
+ * This function is not available with the P3D renderer, (see
+ * Issue 123 ). More information about the renderers can be found in the
+ * size() reference.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:attributes
+ * @param join either MITER, BEVEL, ROUND
+ * @see PGraphics#stroke(int, float)
+ * @see PGraphics#strokeWeight(float)
+ * @see PGraphics#strokeCap(int)
+ */
public void strokeJoin(int join) {
g.strokeJoin(join);
}
+ /**
+ * ( begin auto-generated from strokeCap.xml )
+ *
+ * Sets the style for rendering line endings. These ends are either
+ * squared, extended, or rounded and specified with the corresponding
+ * parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
+ *
+ * This function is not available with the P3D renderer (see
+ * Issue 123 ). More information about the renderers can be found in the
+ * size() reference.
+ *
+ * ( end auto-generated )
+ *
+ * @webref shape:attributes
+ * @param cap either SQUARE, PROJECT, or ROUND
+ * @see PGraphics#stroke(int, float)
+ * @see PGraphics#strokeWeight(float)
+ * @see PGraphics#strokeJoin(int)
+ * @see PApplet#size(int, int, String, String)
+ */
public void strokeCap(int cap) {
g.strokeCap(cap);
}
+ /**
+ * ( begin auto-generated from noStroke.xml )
+ *
+ * Disables drawing the stroke (outline). If both noStroke() and
+ * noFill() are called, nothing will be drawn to the screen.
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:setting
+ * @see PGraphics#stroke(int, float)
+ * @see PGraphics#fill(float, float, float, float)
+ * @see PGraphics#noFill()
+ */
public void noStroke() {
g.noStroke();
}
/**
- * Set the tint to either a grayscale or ARGB value.
- * See notes attached to the fill() function.
+ * ( begin auto-generated from stroke.xml )
+ *
+ * Sets the color used to draw lines and borders around shapes. This color
+ * is either specified in terms of the RGB or HSB color depending on the
+ * current colorMode() (the default color space is RGB, with each
+ * value in the range from 0 to 255).
+ *
+ * When using hexadecimal notation to specify a color, use "#" or "0x"
+ * before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
+ * digits to specify a color (the way colors are specified in HTML and
+ * CSS). When using the hexadecimal notation starting with "0x", the
+ * hexadecimal value must be specified with eight characters; the first two
+ * characters define the alpha component and the remainder the red, green,
+ * and blue components.
+ *
+ * The value for the parameter "gray" must be less than or equal to the
+ * current maximum value as specified by colorMode() . The default
+ * maximum value is 255.
+ *
+ * ( end auto-generated )
+ *
+ * @param rgb color value in hexadecimal notation
+ * @see PGraphics#noStroke()
+ * @see PGraphics#strokeWeight(float)
+ * @see PGraphics#strokeJoin(int)
+ * @see PGraphics#strokeCap(int)
+ * @see PGraphics#fill(int, float)
+ * @see PGraphics#noFill()
+ * @see PGraphics#tint(int, float)
+ * @see PGraphics#background(float, float, float, float)
+ * @see PGraphics#colorMode(int, float, float, float, float)
*/
public void stroke(int rgb) {
g.stroke(rgb);
}
+ /**
+ * @param alpha opacity of the stroke
+ */
public void stroke(int rgb, float alpha) {
g.stroke(rgb, alpha);
}
+ /**
+ * @param gray specifies a value between white and black
+ */
public void stroke(float gray) {
g.stroke(gray);
}
@@ -9137,34 +11321,90 @@ public void stroke(float gray, float alpha) {
}
- public void stroke(float x, float y, float z) {
- g.stroke(x, y, z);
+ /**
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ * @webref color:setting
+ */
+ public void stroke(float v1, float v2, float v3) {
+ g.stroke(v1, v2, v3);
}
- public void stroke(float x, float y, float z, float a) {
- g.stroke(x, y, z, a);
+ public void stroke(float v1, float v2, float v3, float alpha) {
+ g.stroke(v1, v2, v3, alpha);
}
+ /**
+ * ( begin auto-generated from noTint.xml )
+ *
+ * Removes the current fill value for displaying images and reverts to
+ * displaying images with their original hues.
+ *
+ * ( end auto-generated )
+ *
+ * @webref image:loading_displaying
+ * @usage web_application
+ * @see PGraphics#tint(float, float, float, float)
+ * @see PGraphics#image(PImage, float, float, float, float)
+ */
public void noTint() {
g.noTint();
}
/**
- * Set the tint to either a grayscale or ARGB value.
+ * ( begin auto-generated from tint.xml )
+ *
+ * Sets the fill value for displaying images. Images can be tinted to
+ * specified colors or made transparent by setting the alpha.
+ *
+ * To make an image transparent, but not change it's color, use white as
+ * the tint color and specify an alpha value. For instance, tint(255, 128)
+ * will make an image 50% transparent (unless colorMode() has been
+ * used).
+ *
+ * When using hexadecimal notation to specify a color, use "#" or "0x"
+ * before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
+ * digits to specify a color (the way colors are specified in HTML and
+ * CSS). When using the hexadecimal notation starting with "0x", the
+ * hexadecimal value must be specified with eight characters; the first two
+ * characters define the alpha component and the remainder the red, green,
+ * and blue components.
+ *
+ * The value for the parameter "gray" must be less than or equal to the
+ * current maximum value as specified by colorMode() . The default
+ * maximum value is 255.
+ *
+ * The tint() function is also used to control the coloring of
+ * textures in 3D.
+ *
+ * ( end auto-generated )
+ *
+ * @webref image:loading_displaying
+ * @usage web_application
+ * @param rgb color value in hexadecimal notation
+ * @see PGraphics#noTint()
+ * @see PGraphics#image(PImage, float, float, float, float)
*/
public void tint(int rgb) {
g.tint(rgb);
}
+ /**
+ * @param alpha opacity of the image
+ */
public void tint(int rgb, float alpha) {
g.tint(rgb, alpha);
}
+ /**
+ * @param gray specifies a value between white and black
+ */
public void tint(float gray) {
g.tint(gray);
}
@@ -9175,34 +11415,91 @@ public void tint(float gray, float alpha) {
}
- public void tint(float x, float y, float z) {
- g.tint(x, y, z);
+ /**
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ */
+ public void tint(float v1, float v2, float v3) {
+ g.tint(v1, v2, v3);
}
- public void tint(float x, float y, float z, float a) {
- g.tint(x, y, z, a);
+ public void tint(float v1, float v2, float v3, float alpha) {
+ g.tint(v1, v2, v3, alpha);
}
+ /**
+ * ( begin auto-generated from noFill.xml )
+ *
+ * Disables filling geometry. If both noStroke() and noFill()
+ * are called, nothing will be drawn to the screen.
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:setting
+ * @usage web_application
+ * @see PGraphics#fill(float, float, float, float)
+ * @see PGraphics#stroke(int, float)
+ * @see PGraphics#noStroke()
+ */
public void noFill() {
g.noFill();
}
/**
- * Set the fill to either a grayscale value or an ARGB int.
+ * ( begin auto-generated from fill.xml )
+ *
+ * Sets the color used to fill shapes. For example, if you run fill(204,
+ * 102, 0) , all subsequent shapes will be filled with orange. This
+ * color is either specified in terms of the RGB or HSB color depending on
+ * the current colorMode() (the default color space is RGB, with
+ * each value in the range from 0 to 255).
+ *
+ * When using hexadecimal notation to specify a color, use "#" or "0x"
+ * before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
+ * digits to specify a color (the way colors are specified in HTML and
+ * CSS). When using the hexadecimal notation starting with "0x", the
+ * hexadecimal value must be specified with eight characters; the first two
+ * characters define the alpha component and the remainder the red, green,
+ * and blue components.
+ *
+ * The value for the parameter "gray" must be less than or equal to the
+ * current maximum value as specified by colorMode() . The default
+ * maximum value is 255.
+ *
+ * To change the color of an image (or a texture), use tint().
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:setting
+ * @usage web_application
+ * @param rgb color variable or hex value
+ * @see PGraphics#noFill()
+ * @see PGraphics#stroke(int, float)
+ * @see PGraphics#noStroke()
+ * @see PGraphics#tint(int, float)
+ * @see PGraphics#background(float, float, float, float)
+ * @see PGraphics#colorMode(int, float, float, float, float)
*/
public void fill(int rgb) {
g.fill(rgb);
}
+ /**
+ * @param alpha opacity of the fill
+ */
public void fill(int rgb, float alpha) {
g.fill(rgb, alpha);
}
+ /**
+ * @param gray number specifying value between white and black
+ */
public void fill(float gray) {
g.fill(gray);
}
@@ -9213,127 +11510,465 @@ public void fill(float gray, float alpha) {
}
- public void fill(float x, float y, float z) {
- g.fill(x, y, z);
+ /**
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ */
+ public void fill(float v1, float v2, float v3) {
+ g.fill(v1, v2, v3);
}
- public void fill(float x, float y, float z, float a) {
- g.fill(x, y, z, a);
+ public void fill(float v1, float v2, float v3, float alpha) {
+ g.fill(v1, v2, v3, alpha);
}
+ /**
+ * ( begin auto-generated from ambient.xml )
+ *
+ * Sets the ambient reflectance for shapes drawn to the screen. This is
+ * combined with the ambient light component of environment. The color
+ * components set through the parameters define the reflectance. For
+ * example in the default color mode, setting v1=255, v2=126, v3=0, would
+ * cause all the red light to reflect and half of the green light to
+ * reflect. Used in combination with emissive() , specular() ,
+ * and shininess() in setting the material properties of shapes.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:material_properties
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#emissive(float, float, float)
+ * @see PGraphics#specular(float, float, float)
+ * @see PGraphics#shininess(float)
+ */
public void ambient(int rgb) {
g.ambient(rgb);
}
+ /**
+ * @param gray number specifying value between white and black
+ */
public void ambient(float gray) {
g.ambient(gray);
}
- public void ambient(float x, float y, float z) {
- g.ambient(x, y, z);
+ /**
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ */
+ public void ambient(float v1, float v2, float v3) {
+ g.ambient(v1, v2, v3);
}
+ /**
+ * ( begin auto-generated from specular.xml )
+ *
+ * Sets the specular color of the materials used for shapes drawn to the
+ * screen, which sets the color of hightlights. Specular refers to light
+ * which bounces off a surface in a perferred direction (rather than
+ * bouncing in all directions like a diffuse light). Used in combination
+ * with emissive() , ambient() , and shininess() in
+ * setting the material properties of shapes.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:material_properties
+ * @usage web_application
+ * @param rgb color to set
+ * @see PGraphics#lightSpecular(float, float, float)
+ * @see PGraphics#ambient(float, float, float)
+ * @see PGraphics#emissive(float, float, float)
+ * @see PGraphics#shininess(float)
+ */
public void specular(int rgb) {
g.specular(rgb);
}
+ /**
+ * gray number specifying value between white and black
+ *
+ * @param gray value between black and white, by default 0 to 255
+ */
public void specular(float gray) {
g.specular(gray);
}
- public void specular(float x, float y, float z) {
- g.specular(x, y, z);
+ /**
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ */
+ public void specular(float v1, float v2, float v3) {
+ g.specular(v1, v2, v3);
}
+ /**
+ * ( begin auto-generated from shininess.xml )
+ *
+ * Sets the amount of gloss in the surface of shapes. Used in combination
+ * with ambient() , specular() , and emissive() in
+ * setting the material properties of shapes.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:material_properties
+ * @usage web_application
+ * @param shine degree of shininess
+ * @see PGraphics#emissive(float, float, float)
+ * @see PGraphics#ambient(float, float, float)
+ * @see PGraphics#specular(float, float, float)
+ */
public void shininess(float shine) {
g.shininess(shine);
}
+ /**
+ * ( begin auto-generated from emissive.xml )
+ *
+ * Sets the emissive color of the material used for drawing shapes drawn to
+ * the screen. Used in combination with ambient() ,
+ * specular() , and shininess() in setting the material
+ * properties of shapes.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:material_properties
+ * @usage web_application
+ * @param rgb color to set
+ * @see PGraphics#ambient(float, float, float)
+ * @see PGraphics#specular(float, float, float)
+ * @see PGraphics#shininess(float)
+ */
public void emissive(int rgb) {
g.emissive(rgb);
}
+ /**
+ * gray number specifying value between white and black
+ *
+ * @param gray value between black and white, by default 0 to 255
+ */
public void emissive(float gray) {
g.emissive(gray);
}
- public void emissive(float x, float y, float z) {
- g.emissive(x, y, z);
+ /**
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ */
+ public void emissive(float v1, float v2, float v3) {
+ g.emissive(v1, v2, v3);
}
+ /**
+ * ( begin auto-generated from lights.xml )
+ *
+ * Sets the default ambient light, directional light, falloff, and specular
+ * values. The defaults are ambientLight(128, 128, 128) and
+ * directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
+ * lightSpecular(0, 0, 0). Lights need to be included in the draw() to
+ * remain persistent in a looping program. Placing them in the setup() of a
+ * looping program will cause them to only have an effect the first time
+ * through the loop.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @see PGraphics#ambientLight(float, float, float, float, float, float)
+ * @see PGraphics#directionalLight(float, float, float, float, float, float)
+ * @see PGraphics#pointLight(float, float, float, float, float, float)
+ * @see PGraphics#spotLight(float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#noLights()
+ */
public void lights() {
g.lights();
}
+ /**
+ * ( begin auto-generated from noLights.xml )
+ *
+ * Disable all lighting. Lighting is turned off by default and enabled with
+ * the lights() function. This function can be used to disable
+ * lighting so that 2D geometry (which does not require lighting) can be
+ * drawn after a set of lighted 3D geometry.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @see PGraphics#lights()
+ */
public void noLights() {
g.noLights();
}
- public void ambientLight(float red, float green, float blue) {
- g.ambientLight(red, green, blue);
+ /**
+ * ( begin auto-generated from ambientLight.xml )
+ *
+ * Adds an ambient light. Ambient light doesn't come from a specific
+ * direction, the rays have light have bounced around so much that objects
+ * are evenly lit from all sides. Ambient lights are almost always used in
+ * combination with other types of lights. Lights need to be included in
+ * the draw() to remain persistent in a looping program. Placing
+ * them in the setup() of a looping program will cause them to only
+ * have an effect the first time through the loop. The effect of the
+ * parameters is determined by the current color mode.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ * @see PGraphics#lights()
+ * @see PGraphics#directionalLight(float, float, float, float, float, float)
+ * @see PGraphics#pointLight(float, float, float, float, float, float)
+ * @see PGraphics#spotLight(float, float, float, float, float, float, float, float, float, float, float)
+ */
+ public void ambientLight(float v1, float v2, float v3) {
+ g.ambientLight(v1, v2, v3);
}
- public void ambientLight(float red, float green, float blue,
+ /**
+ * @param x x-coordinate of the light
+ * @param y y-coordinate of the light
+ * @param z z-coordinate of the light
+ */
+ public void ambientLight(float v1, float v2, float v3,
float x, float y, float z) {
- g.ambientLight(red, green, blue, x, y, z);
+ g.ambientLight(v1, v2, v3, x, y, z);
}
- public void directionalLight(float red, float green, float blue,
+ /**
+ * ( begin auto-generated from directionalLight.xml )
+ *
+ * Adds a directional light. Directional light comes from one direction and
+ * is stronger when hitting a surface squarely and weaker if it hits at a a
+ * gentle angle. After hitting a surface, a directional lights scatters in
+ * all directions. Lights need to be included in the draw() to
+ * remain persistent in a looping program. Placing them in the
+ * setup() of a looping program will cause them to only have an
+ * effect the first time through the loop. The affect of the v1 ,
+ * v2 , and v3 parameters is determined by the current color
+ * mode. The nx , ny , and nz parameters specify the
+ * direction the light is facing. For example, setting ny to -1 will
+ * cause the geometry to be lit from below (the light is facing directly upward).
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ * @param nx direction along the x-axis
+ * @param ny direction along the y-axis
+ * @param nz direction along the z-axis
+ * @see PGraphics#lights()
+ * @see PGraphics#ambientLight(float, float, float, float, float, float)
+ * @see PGraphics#pointLight(float, float, float, float, float, float)
+ * @see PGraphics#spotLight(float, float, float, float, float, float, float, float, float, float, float)
+ */
+ public void directionalLight(float v1, float v2, float v3,
float nx, float ny, float nz) {
- g.directionalLight(red, green, blue, nx, ny, nz);
+ g.directionalLight(v1, v2, v3, nx, ny, nz);
}
- public void pointLight(float red, float green, float blue,
+ /**
+ * ( begin auto-generated from pointLight.xml )
+ *
+ * Adds a point light. Lights need to be included in the draw() to
+ * remain persistent in a looping program. Placing them in the
+ * setup() of a looping program will cause them to only have an
+ * effect the first time through the loop. The affect of the v1 ,
+ * v2 , and v3 parameters is determined by the current color
+ * mode. The x , y , and z parameters set the position
+ * of the light.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ * @param x x-coordinate of the light
+ * @param y y-coordinate of the light
+ * @param z z-coordinate of the light
+ * @see PGraphics#lights()
+ * @see PGraphics#directionalLight(float, float, float, float, float, float)
+ * @see PGraphics#ambientLight(float, float, float, float, float, float)
+ * @see PGraphics#spotLight(float, float, float, float, float, float, float, float, float, float, float)
+ */
+ public void pointLight(float v1, float v2, float v3,
float x, float y, float z) {
- g.pointLight(red, green, blue, x, y, z);
+ g.pointLight(v1, v2, v3, x, y, z);
}
- public void spotLight(float red, float green, float blue,
+ /**
+ * ( begin auto-generated from spotLight.xml )
+ *
+ * Adds a spot light. Lights need to be included in the draw() to
+ * remain persistent in a looping program. Placing them in the
+ * setup() of a looping program will cause them to only have an
+ * effect the first time through the loop. The affect of the v1 ,
+ * v2 , and v3 parameters is determined by the current color
+ * mode. The x , y , and z parameters specify the
+ * position of the light and nx , ny , nz specify the
+ * direction or light. The angle parameter affects angle of the
+ * spotlight cone.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ * @param x x-coordinate of the light
+ * @param y y-coordinate of the light
+ * @param z z-coordinate of the light
+ * @param nx direction along the x axis
+ * @param ny direction along the y axis
+ * @param nz direction along the z axis
+ * @param angle angle of the spotlight cone
+ * @param concentration exponent determining the center bias of the cone
+ * @see PGraphics#lights()
+ * @see PGraphics#directionalLight(float, float, float, float, float, float)
+ * @see PGraphics#pointLight(float, float, float, float, float, float)
+ * @see PGraphics#ambientLight(float, float, float, float, float, float)
+ */
+ public void spotLight(float v1, float v2, float v3,
float x, float y, float z,
float nx, float ny, float nz,
float angle, float concentration) {
- g.spotLight(red, green, blue, x, y, z, nx, ny, nz, angle, concentration);
+ g.spotLight(v1, v2, v3, x, y, z, nx, ny, nz, angle, concentration);
}
+ /**
+ * ( begin auto-generated from lightFalloff.xml )
+ *
+ * Sets the falloff rates for point lights, spot lights, and ambient
+ * lights. The parameters are used to determine the falloff with the
+ * following equation: d = distance from light position to
+ * vertex position falloff = 1 / (CONSTANT + d * LINEAR + (d*d) *
+ * QUADRATIC) Like fill() , it affects only the elements
+ * which are created after it in the code. The default value if
+ * LightFalloff(1.0, 0.0, 0.0) . Thinking about an ambient light with
+ * a falloff can be tricky. It is used, for example, if you wanted a region
+ * of your scene to be lit ambiently one color and another region to be lit
+ * ambiently by another color, you would use an ambient light with location
+ * and falloff. You can think of it as a point light that doesn't care
+ * which direction a surface is facing.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @param constant constant value or determining falloff
+ * @param linear linear value for determining falloff
+ * @param quadratic quadratic value for determining falloff
+ * @see PGraphics#lights()
+ * @see PGraphics#ambientLight(float, float, float, float, float, float)
+ * @see PGraphics#pointLight(float, float, float, float, float, float)
+ * @see PGraphics#spotLight(float, float, float, float, float, float, float, float, float, float, float)
+ * @see PGraphics#lightSpecular(float, float, float)
+ */
public void lightFalloff(float constant, float linear, float quadratic) {
g.lightFalloff(constant, linear, quadratic);
}
- public void lightSpecular(float x, float y, float z) {
- g.lightSpecular(x, y, z);
+ /**
+ * ( begin auto-generated from lightSpecular.xml )
+ *
+ * Sets the specular color for lights. Like fill() , it affects only
+ * the elements which are created after it in the code. Specular refers to
+ * light which bounces off a surface in a perferred direction (rather than
+ * bouncing in all directions like a diffuse light) and is used for
+ * creating highlights. The specular quality of a light interacts with the
+ * specular material qualities set through the specular() and
+ * shininess() functions.
+ *
+ * ( end auto-generated )
+ *
+ * @webref lights_camera:lights
+ * @usage web_application
+ * @param v1 red or hue value (depending on current color mode)
+ * @param v2 green or saturation value (depending on current color mode)
+ * @param v3 blue or brightness value (depending on current color mode)
+ * @see PGraphics#specular(float, float, float)
+ * @see PGraphics#lights()
+ * @see PGraphics#ambientLight(float, float, float, float, float, float)
+ * @see PGraphics#pointLight(float, float, float, float, float, float)
+ * @see PGraphics#spotLight(float, float, float, float, float, float, float, float, float, float, float)
+ */
+ public void lightSpecular(float v1, float v2, float v3) {
+ g.lightSpecular(v1, v2, v3);
}
/**
- * Set the background to a gray or ARGB color.
- *
- * For the main drawing surface, the alpha value will be ignored. However,
- * alpha can be used on PGraphics objects from createGraphics(). This is
- * the only way to set all the pixels partially transparent, for instance.
- *
- * Note that background() should be called before any transformations occur,
- * because some implementations may require the current transformation matrix
- * to be identity before drawing.
+ * ( begin auto-generated from background.xml )
+ *
+ * The background() function sets the color used for the background
+ * of the Processing window. The default background is light gray. In the
+ * draw() function, the background color is used to clear the
+ * display window at the beginning of each frame.
+ *
+ * An image can also be used as the background for a sketch, however its
+ * width and height must be the same size as the sketch window. To resize
+ * an image 'b' to the size of the sketch window, use b.resize(width, height).
+ *
+ * Images used as background will ignore the current tint() setting.
+ *
+ * It is not possible to use transparency (alpha) in background colors with
+ * the main drawing surface, however they will work properly with createGraphics() .
+ *
+ * ( end auto-generated )
+ *
+ *
Advanced
+ * Clear the background with a color that includes an alpha value. This can
+ * only be used with objects created by createGraphics(), because the main
+ * drawing surface cannot be set transparent.
+ * It might be tempting to use this function to partially clear the screen
+ * on each frame, however that's not how this function works. When calling
+ * background(), the pixels will be replaced with pixels that have that level
+ * of transparency. To do a semi-transparent overlay, use fill() with alpha
+ * and draw a rectangle.
+ *
+ * @webref color:setting
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#stroke(float)
+ * @see PGraphics#fill(float)
+ * @see PGraphics#tint(float)
+ * @see PGraphics#colorMode(int)
*/
public void background(int rgb) {
g.background(rgb);
@@ -9341,7 +11976,7 @@ public void background(int rgb) {
/**
- * See notes about alpha in background(x, y, z, a).
+ * @param alpha opacity of the background
*/
public void background(int rgb, float alpha) {
g.background(rgb, alpha);
@@ -9349,47 +11984,36 @@ public void background(int rgb, float alpha) {
/**
- * Set the background to a grayscale value, based on the
- * current colorMode.
+ * @param gray specifies a value between white and black
*/
public void background(float gray) {
g.background(gray);
}
- /**
- * See notes about alpha in background(x, y, z, a).
- */
public void background(float gray, float alpha) {
g.background(gray, alpha);
}
/**
- * Set the background to an r, g, b or h, s, b value,
- * based on the current colorMode.
+ * @param v1 red or hue value (depending on the current color mode)
+ * @param v2 green or saturation value (depending on the current color mode)
+ * @param v3 blue or brightness value (depending on the current color mode)
*/
- public void background(float x, float y, float z) {
- g.background(x, y, z);
+ public void background(float v1, float v2, float v3) {
+ g.background(v1, v2, v3);
}
- /**
- * Clear the background with a color that includes an alpha value. This can
- * only be used with objects created by createGraphics(), because the main
- * drawing surface cannot be set transparent.
- *
- * It might be tempting to use this function to partially clear the screen
- * on each frame, however that's not how this function works. When calling
- * background(), the pixels will be replaced with pixels that have that level
- * of transparency. To do a semi-transparent overlay, use fill() with alpha
- * and draw a rectangle.
- */
- public void background(float x, float y, float z, float a) {
- g.background(x, y, z, a);
+ public void background(float v1, float v2, float v3, float alpha) {
+ g.background(v1, v2, v3, alpha);
}
+ /**
+ * @webref color:setting
+ */
public void clear() {
g.clear();
}
@@ -9398,94 +12022,255 @@ public void clear() {
/**
* Takes an RGB or ARGB image and sets it as the background.
* The width and height of the image must be the same size as the sketch.
- * Use image.resize(width, height) to make short work of such a task.
- *
+ * Use image.resize(width, height) to make short work of such a task.
+ *
* Note that even if the image is set as RGB, the high 8 bits of each pixel
- * should be set opaque (0xFF000000), because the image data will be copied
+ * should be set opaque (0xFF000000) because the image data will be copied
* directly to the screen, and non-opaque background images may have strange
- * behavior. Using image.filter(OPAQUE) will handle this easily.
- *
+ * behavior. Use image.filter(OPAQUE) to handle this easily.
+ *
* When using 3D, this will also clear the zbuffer (if it exists).
+ *
+ * @param image PImage to set as background (must be same size as the sketch window)
*/
public void background(PImage image) {
g.background(image);
}
+ /**
+ * ( begin auto-generated from colorMode.xml )
+ *
+ * Changes the way Processing interprets color data. By default, the
+ * parameters for fill() , stroke() , background() , and
+ * color() are defined by values between 0 and 255 using the RGB
+ * color model. The colorMode() function is used to change the
+ * numerical range used for specifying colors and to switch color systems.
+ * For example, calling colorMode(RGB, 1.0) will specify that values
+ * are specified between 0 and 1. The limits for defining colors are
+ * altered by setting the parameters range1, range2, range3, and range 4.
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:setting
+ * @usage web_application
+ * @param mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
+ * @see PGraphics#background(float)
+ * @see PGraphics#fill(float)
+ * @see PGraphics#stroke(float)
+ */
public void colorMode(int mode) {
g.colorMode(mode);
}
+ /**
+ * @param max range for all color elements
+ */
public void colorMode(int mode, float max) {
g.colorMode(mode, max);
}
/**
- * Set the colorMode and the maximum values for (r, g, b)
- * or (h, s, b).
- *
- * Note that this doesn't set the maximum for the alpha value,
- * which might be confusing if for instance you switched to
- *
colorMode(HSB, 360, 100, 100);
- * because the alpha values were still between 0 and 255.
+ * @param max1 range for the red or hue depending on the current color mode
+ * @param max2 range for the green or saturation depending on the current color mode
+ * @param max3 range for the blue or brightness depending on the current color mode
*/
- public void colorMode(int mode, float maxX, float maxY, float maxZ) {
- g.colorMode(mode, maxX, maxY, maxZ);
+ public void colorMode(int mode, float max1, float max2, float max3) {
+ g.colorMode(mode, max1, max2, max3);
}
+ /**
+ * @param maxA range for the alpha
+ */
public void colorMode(int mode,
- float maxX, float maxY, float maxZ, float maxA) {
- g.colorMode(mode, maxX, maxY, maxZ, maxA);
- }
-
-
- public final float alpha(int what) {
- return g.alpha(what);
+ float max1, float max2, float max3, float maxA) {
+ g.colorMode(mode, max1, max2, max3, maxA);
}
- public final float red(int what) {
- return g.red(what);
+ /**
+ * ( begin auto-generated from alpha.xml )
+ *
+ * Extracts the alpha value from a color.
+ *
+ * ( end auto-generated )
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#red(int)
+ * @see PGraphics#green(int)
+ * @see PGraphics#blue(int)
+ * @see PGraphics#hue(int)
+ * @see PGraphics#saturation(int)
+ * @see PGraphics#brightness(int)
+ */
+ public final float alpha(int rgb) {
+ return g.alpha(rgb);
}
- public final float green(int what) {
- return g.green(what);
+ /**
+ * ( begin auto-generated from red.xml )
+ *
+ * Extracts the red value from a color, scaled to match current
+ * colorMode() . This value is always returned as a float so be
+ * careful not to assign it to an int value. The red() function
+ * is easy to use and undestand, but is slower than another technique. To
+ * achieve the same results when working in colorMode(RGB, 255) , but
+ * with greater speed, use the >> (right shift) operator with a bit
+ * mask. For example, the following two lines of code are equivalent:float r1 = red(myColor); float r2 = myColor >> 16
+ * & 0xFF;
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#green(int)
+ * @see PGraphics#blue(int)
+ * @see PGraphics#alpha(int)
+ * @see PGraphics#hue(int)
+ * @see PGraphics#saturation(int)
+ * @see PGraphics#brightness(int)
+ * @see_external rightshift
+ */
+ public final float red(int rgb) {
+ return g.red(rgb);
}
- public final float blue(int what) {
- return g.blue(what);
+ /**
+ * ( begin auto-generated from green.xml )
+ *
+ * Extracts the green value from a color, scaled to match current
+ * colorMode() . This value is always returned as a float so be
+ * careful not to assign it to an int value. The green()
+ * function is easy to use and undestand, but is slower than another
+ * technique. To achieve the same results when working in colorMode(RGB,
+ * 255) , but with greater speed, use the >> (right shift)
+ * operator with a bit mask. For example, the following two lines of code
+ * are equivalent:float r1 = green(myColor); float r2 =
+ * myColor >> 8 & 0xFF;
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#red(int)
+ * @see PGraphics#blue(int)
+ * @see PGraphics#alpha(int)
+ * @see PGraphics#hue(int)
+ * @see PGraphics#saturation(int)
+ * @see PGraphics#brightness(int)
+ * @see_external rightshift
+ */
+ public final float green(int rgb) {
+ return g.green(rgb);
}
- public final float hue(int what) {
- return g.hue(what);
+ /**
+ * ( begin auto-generated from blue.xml )
+ *
+ * Extracts the blue value from a color, scaled to match current
+ * colorMode() . This value is always returned as a float so be
+ * careful not to assign it to an int value. The blue()
+ * function is easy to use and undestand, but is slower than another
+ * technique. To achieve the same results when working in colorMode(RGB,
+ * 255) , but with greater speed, use a bit mask to remove the other
+ * color components. For example, the following two lines of code are
+ * equivalent:float r1 = blue(myColor); float r2 = myColor
+ * & 0xFF;
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#red(int)
+ * @see PGraphics#green(int)
+ * @see PGraphics#alpha(int)
+ * @see PGraphics#hue(int)
+ * @see PGraphics#saturation(int)
+ * @see PGraphics#brightness(int)
+ * @see_external rightshift
+ */
+ public final float blue(int rgb) {
+ return g.blue(rgb);
}
- public final float saturation(int what) {
- return g.saturation(what);
+ /**
+ * ( begin auto-generated from hue.xml )
+ *
+ * Extracts the hue value from a color.
+ *
+ * ( end auto-generated )
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#red(int)
+ * @see PGraphics#green(int)
+ * @see PGraphics#blue(int)
+ * @see PGraphics#alpha(int)
+ * @see PGraphics#saturation(int)
+ * @see PGraphics#brightness(int)
+ */
+ public final float hue(int rgb) {
+ return g.hue(rgb);
}
- public final float brightness(int what) {
- return g.brightness(what);
+ /**
+ * ( begin auto-generated from saturation.xml )
+ *
+ * Extracts the saturation value from a color.
+ *
+ * ( end auto-generated )
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#red(int)
+ * @see PGraphics#green(int)
+ * @see PGraphics#blue(int)
+ * @see PGraphics#alpha(int)
+ * @see PGraphics#hue(int)
+ * @see PGraphics#brightness(int)
+ */
+ public final float saturation(int rgb) {
+ return g.saturation(rgb);
}
/**
- * Interpolate between two colors, using the current color mode.
+ * ( begin auto-generated from brightness.xml )
+ *
+ * Extracts the brightness value from a color.
+ *
+ * ( end auto-generated )
+ *
+ * @webref color:creating_reading
+ * @usage web_application
+ * @param rgb any value of the color datatype
+ * @see PGraphics#red(int)
+ * @see PGraphics#green(int)
+ * @see PGraphics#blue(int)
+ * @see PGraphics#alpha(int)
+ * @see PGraphics#hue(int)
+ * @see PGraphics#saturation(int)
*/
- public int lerpColor(int c1, int c2, float amt) {
- return g.lerpColor(c1, c2, amt);
+ public final float brightness(int rgb) {
+ return g.brightness(rgb);
}
/**
+ * @nowebref
* Interpolate between two colors. Like lerp(), but for the
* individual color components of a color supplied as an int value.
*/
@@ -9542,35 +12327,30 @@ static public void showMissingWarning(String method) {
/**
- * Return true if this renderer should be drawn to the screen. Defaults to
- * returning true, since nearly all renderers are on-screen beasts. But can
- * be overridden for subclasses like PDF so that a window doesn't open up.
- *
- * A better name? showFrame, displayable, isVisible, visible, shouldDisplay,
- * what to call this?
- */
- public boolean displayable() {
- return g.displayable();
- }
-
-
- /**
- * Return true if this renderer does rendering through OpenGL. Defaults to false.
- */
- public boolean isGL() {
- return g.isGL();
- }
-
-
- /**
- * Returns the native Bitmap object for this PImage.
- */
- public Object getNative() {
- return g.getNative();
- }
-
-
- /**
+ * ( begin auto-generated from PImage_get.xml )
+ *
+ * Reads the color of any pixel or grabs a section of an image. If no
+ * parameters are specified, the entire image is returned. Use the x
+ * and y parameters to get the value of one pixel. Get a section of
+ * the display window by specifying an additional width and
+ * height parameter. When getting an image, the x and
+ * y parameters define the coordinates for the upper-left corner of
+ * the image, regardless of the current imageMode() .
+ *
+ * If the pixel requested is outside of the image window, black is
+ * returned. The numbers returned are scaled according to the current color
+ * ranges, but only RGB values are returned by this function. For example,
+ * even though you may have drawn a shape with colorMode(HSB) , the
+ * numbers returned will be in RGB format.
+ *
+ * Getting the color of a single pixel with get(x, y) is easy, but
+ * not as fast as grabbing the data directly from pixels[] . The
+ * equivalent statement to get(x, y) using pixels[] is
+ * pixels[y*width+x] . See the reference for pixels[] for more information.
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
* Returns an ARGB "color" type (a packed 32 bit int with the color.
* If the coordinate is outside the image, zero is returned
* (black, but completely transparent).
@@ -9587,16 +12367,21 @@ public Object getNative() {
* is inside the bounds, and then has to check to see what image
* type it is. If you want things to be more efficient, access the
* pixels[] array directly.
+ *
+ * @webref image:pixels
+ * @brief Reads the color of any pixel or grabs a rectangle of pixels
+ * @usage web_application
+ * @param x x-coordinate of the pixel
+ * @param y y-coordinate of the pixel
+ * @see PApplet#set(int, int, int)
+ * @see PApplet#pixels
+ * @see PApplet#copy(PImage, int, int, int, int, int, int, int, int)
*/
public int get(int x, int y) {
return g.get(x, y);
}
- /**
- * Grab a subsection of a PImage, and copy it into a fresh PImage.
- * As of release 0149, no longer honors imageMode() for the coordinates.
- */
/**
* @param w width of pixel rectangle to get
* @param h height of pixel rectangle to get
@@ -9608,14 +12393,48 @@ public PImage get(int x, int y, int w, int h) {
/**
* Returns a copy of this PImage. Equivalent to get(0, 0, width, height).
+ * Deprecated, just use copy() instead.
*/
public PImage get() {
return g.get();
}
+ public PImage copy() {
+ return g.copy();
+ }
+
+
/**
- * Set a single pixel to the specified color.
+ * ( begin auto-generated from PImage_set.xml )
+ *
+ * Changes the color of any pixel or writes an image directly into the
+ * display window.
+ *
+ * The x and y parameters specify the pixel to change and the
+ * color parameter specifies the color value. The color parameter is
+ * affected by the current color mode (the default is RGB values from 0 to
+ * 255). When setting an image, the x and y parameters define
+ * the coordinates for the upper-left corner of the image, regardless of
+ * the current imageMode() .
+ *
+ * Setting the color of a single pixel with set(x, y) is easy, but
+ * not as fast as putting the data directly into pixels[] . The
+ * equivalent statement to set(x, y, #000000) using pixels[]
+ * is pixels[y*width+x] = #000000 . See the reference for
+ * pixels[] for more information.
+ *
+ * ( end auto-generated )
+ *
+ * @webref image:pixels
+ * @brief writes a color to any pixel or writes an image into another
+ * @usage web_application
+ * @param x x-coordinate of the pixel
+ * @param y y-coordinate of the pixel
+ * @param c any value of the color datatype
+ * @see PImage#get(int, int, int, int)
+ * @see PImage#pixels
+ * @see PImage#copy(PImage, int, int, int, int, int, int, int, int)
*/
public void set(int x, int y, int c) {
g.set(x, y, c);
@@ -9623,9 +12442,12 @@ public void set(int x, int y, int c) {
/**
+ * Advanced
* Efficient method of drawing an image's pixels directly to this surface.
* No variations are employed, meaning that any scale, tint, or imageMode
* settings will be ignored.
+ *
+ * @param img image to copy into the original image
*/
public void set(int x, int y, PImage img) {
g.set(x, y, img);
@@ -9633,6 +12455,23 @@ public void set(int x, int y, PImage img) {
/**
+ * ( begin auto-generated from PImage_mask.xml )
+ *
+ * Masks part of an image from displaying by loading another image and
+ * using it as an alpha channel. This mask image should only contain
+ * grayscale data, but only the blue color channel is used. The mask image
+ * needs to be the same size as the image to which it is applied.
+ *
+ * In addition to using a mask image, an integer array containing the alpha
+ * channel data can be specified directly. This method is useful for
+ * creating dynamically generated alpha masks. This array must be of the
+ * same length as the target image's pixels array and should contain only
+ * grayscale data of values between 0-255.
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
+ *
* Set alpha channel for an image. Black colors in the source
* image will make the destination image completely transparent,
* and white will make things fully opaque. Gray values will
@@ -9642,23 +12481,54 @@ public void set(int x, int y, PImage img) {
* used as the alpha color. For a fully grayscale image, this
* is correct, but for a color image it's not 100% accurate.
* For a more accurate conversion, first use filter(GRAY)
- * which will make the image into a "correct" grayscake by
+ * which will make the image into a "correct" grayscale by
* performing a proper luminance-based conversion.
+ *
+ * @webref pimage:method
+ * @usage web_application
+ * @param img image to use as the mask
+ * @brief Masks part of an image with another image as an alpha channel
*/
- public void mask(int alpha[]) {
- g.mask(alpha);
+ public void mask(PImage img) {
+ g.mask(img);
}
- /**
- * Set alpha channel for an image using another image as the source.
- */
- public void mask(PImage alpha) {
- g.mask(alpha);
+ public void filter(int kind) {
+ g.filter(kind);
}
/**
+ * ( begin auto-generated from PImage_filter.xml )
+ *
+ * Filters an image as defined by one of the following modes: THRESHOLD - converts the image to black and white pixels depending if
+ * they are above or below the threshold defined by the level parameter.
+ * The level must be between 0.0 (black) and 1.0(white). If no level is
+ * specified, 0.5 is used.
+ *
+ * GRAY - converts any colors in the image to grayscale equivalents
+ *
+ * INVERT - sets each pixel to its inverse value
+ *
+ * POSTERIZE - limits each channel of the image to the number of colors
+ * specified as the level parameter
+ *
+ * BLUR - executes a Guassian blur with the level parameter specifying the
+ * extent of the blurring. If no level parameter is used, the blur is
+ * equivalent to Guassian blur of radius 1
+ *
+ * OPAQUE - sets the alpha channel to entirely opaque
+ *
+ * ERODE - reduces the light areas with the amount defined by the level
+ * parameter
+ *
+ * DILATE - increases the light areas with the amount defined by the level parameter
+ *
+ * ( end auto-generated )
+ *
+ * Advanced
* Method to apply a variety of basic filters to this image.
*
*
@@ -9675,27 +12545,12 @@ public void mask(PImage alpha) {
*
* Gaussian blur code contributed by
* Mario Klingemann
- */
- public void filter(int kind) {
- g.filter(kind);
- }
-
-
- /**
- * Method to apply a variety of basic filters to this image.
- * These filters all take a parameter.
- *
- *
- * filter(BLUR, int radius) performs a gaussian blur of the
- * specified radius.
- * filter(POSTERIZE, int levels) will posterize the image to
- * between 2 and 255 levels.
- * filter(THRESHOLD, float center) allows you to set the
- * center point for the threshold. It takes a value from 0 to 1.0.
- *
- * Gaussian blur code contributed by
- * Mario Klingemann
- * and later updated by toxi for better speed.
+ *
+ * @webref image:pixels
+ * @brief Converts the image to grayscale or black and white
+ * @usage web_application
+ * @param kind Either THRESHOLD, GRAY, OPAQUE, INVERT, POSTERIZE, BLUR, ERODE, or DILATE
+ * @param param unique for each, see above
*/
public void filter(int kind, float param) {
g.filter(kind, param);
@@ -9703,8 +12558,31 @@ public void filter(int kind, float param) {
/**
- * Copy things from one area of this image
- * to another area in the same image.
+ * ( begin auto-generated from PImage_copy.xml )
+ *
+ * Copies a region of pixels from one image into another. If the source and
+ * destination regions aren't the same size, it will automatically resize
+ * source pixels to fit the specified target region. No alpha information
+ * is used in the process, however if the source image has an alpha channel
+ * set, it will be copied as well.
+ *
+ * As of release 0149, this function ignores imageMode() .
+ *
+ * ( end auto-generated )
+ *
+ * @webref image:pixels
+ * @brief Copies the entire image
+ * @usage web_application
+ * @param sx X coordinate of the source's upper left corner
+ * @param sy Y coordinate of the source's upper left corner
+ * @param sw source image width
+ * @param sh source image height
+ * @param dx X coordinate of the destination's upper left corner
+ * @param dy Y coordinate of the destination's upper left corner
+ * @param dw destination image width
+ * @param dh destination image height
+ * @see PGraphics#alpha(int)
+ * @see PImage#blend(PImage, int, int, int, int, int, int, int, int, int)
*/
public void copy(int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh) {
@@ -9713,7 +12591,7 @@ public void copy(int sx, int sy, int sw, int sh,
/**
- * Copies area of one image into another PImage object.
+ * @param src an image variable referring to the source image.
*/
public void copy(PImage src,
int sx, int sy, int sw, int sh,
@@ -9722,10 +12600,6 @@ public void copy(PImage src,
}
- /**
- * Blends one area of this image to another area.
- * @see processing.core.PImage#blendColor(int,int,int)
- */
public void blend(int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh, int mode) {
g.blend(sx, sy, sw, sh, dx, dy, dw, dh, mode);
@@ -9733,8 +12607,72 @@ public void blend(int sx, int sy, int sw, int sh,
/**
- * Copies area of one image into another PImage object.
- * @see processing.core.PImage#blendColor(int,int,int)
+ * ( begin auto-generated from PImage_blend.xml )
+ *
+ * Blends a region of pixels into the image specified by the img
+ * parameter. These copies utilize full alpha channel support and a choice
+ * of the following modes to blend the colors of source pixels (A) with the
+ * ones of pixels in the destination image (B):
+ *
+ * BLEND - linear interpolation of colours: C = A*factor + B
+ *
+ * ADD - additive blending with white clip: C = min(A*factor + B, 255)
+ *
+ * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor,
+ * 0)
+ *
+ * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
+ *
+ * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
+ *
+ * DIFFERENCE - subtract colors from underlying image.
+ *
+ * EXCLUSION - similar to DIFFERENCE, but less extreme.
+ *
+ * MULTIPLY - Multiply the colors, result will always be darker.
+ *
+ * SCREEN - Opposite multiply, uses inverse values of the colors.
+ *
+ * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values,
+ * and screens light values.
+ *
+ * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
+ *
+ * SOFT_LIGHT - Mix of DARKEST and LIGHTEST.
+ * Works like OVERLAY, but not as harsh.
+ *
+ * DODGE - Lightens light tones and increases contrast, ignores darks.
+ * Called "Color Dodge" in Illustrator and Photoshop.
+ *
+ * BURN - Darker areas are applied, increasing contrast, ignores lights.
+ * Called "Color Burn" in Illustrator and Photoshop.
+ *
+ * All modes use the alpha information (highest byte) of source image
+ * pixels as the blending factor. If the source and destination regions are
+ * different sizes, the image will be automatically resized to match the
+ * destination size. If the srcImg parameter is not used, the
+ * display window is used as the source image.
+ *
+ * As of release 0149, this function ignores imageMode() .
+ *
+ * ( end auto-generated )
+ *
+ * @webref image:pixels
+ * @brief Copies a pixel or rectangle of pixels using different blending modes
+ * @param src an image variable referring to the source image
+ * @param sx X coordinate of the source's upper left corner
+ * @param sy Y coordinate of the source's upper left corner
+ * @param sw source image width
+ * @param sh source image height
+ * @param dx X coordinate of the destinations's upper left corner
+ * @param dy Y coordinate of the destinations's upper left corner
+ * @param dw destination image width
+ * @param dh destination image height
+ * @param mode Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
+ *
+ * @see PApplet#alpha(int)
+ * @see PImage#copy(PImage, int, int, int, int, int, int, int, int)
+ * @see PImage#blendColor(int,int,int)
*/
public void blend(PImage src,
int sx, int sy, int sw, int sh,
diff --git a/core/src/processing/core/PConstants.java b/libs/processing-core/src/main/java/processing/core/PConstants.java
similarity index 92%
rename from core/src/processing/core/PConstants.java
rename to libs/processing-core/src/main/java/processing/core/PConstants.java
index 582509acc..14753a133 100644
--- a/core/src/processing/core/PConstants.java
+++ b/libs/processing-core/src/main/java/processing/core/PConstants.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2004-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
@@ -45,8 +46,14 @@ public interface PConstants {
// built-in rendering options
static final String JAVA2D = "processing.core.PGraphicsAndroid2D";
static final String P2D = "processing.opengl.PGraphics2D";
+ static final String P2DX = "processing.opengl.PGraphics2DX";
static final String P3D = "processing.opengl.PGraphics3D";
static final String OPENGL = P3D;
+ static final String STEREO = "processing.vr.VRGraphicsStereo";
+ static final String MONO = "processing.vr.VRGraphicsMono";
+ static final String VR = STEREO;
+ static final String AR = "processing.ar.ARGraphics";
+ static final String ARCORE = AR;
// The PDF and DXF renderers are not available for Android.
@@ -346,12 +353,12 @@ public interface PConstants {
// for 0125, these were changed to 'char' values, because they
// can be upgraded to ints automatically by Java, but having them
// as ints prevented split(blah, TAB) from working
- static final char BACKSPACE = 8;
- static final char TAB = 9;
- static final char ENTER = 10;
- static final char RETURN = 13;
- static final char ESC = 27;
- static final char DELETE = 127;
+ static final char BACKSPACE = KeyEvent.KEYCODE_DEL;
+ static final char TAB = KeyEvent.KEYCODE_TAB;
+ static final char ENTER = KeyEvent.KEYCODE_ENTER;
+ static final char RETURN = KeyEvent.KEYCODE_ENTER;
+ static final char ESC = KeyEvent.KEYCODE_ESCAPE;
+ static final char DELETE = KeyEvent.KEYCODE_DEL;
// i.e. if ((key == CODED) && (keyCode == UP))
static final int CODED = 0xffff;
@@ -373,8 +380,7 @@ public interface PConstants {
// key will be CODED and keyCode will be this value
// static final int ALT = KeyEvent.VK_ALT;
// static final int CONTROL = KeyEvent.VK_CONTROL;
-// static final int SHIFT = KeyEvent.VK_SHIFT;
-
+ static final int SHIFT = KeyEvent.KEYCODE_SHIFT_LEFT;
// cursor types
@@ -395,7 +401,9 @@ public interface PConstants {
// hints - hint values are positive for the alternate version,
// negative of the same value returns to the normal/default state
+ @Deprecated
static final int ENABLE_NATIVE_FONTS = 1;
+ @Deprecated
static final int DISABLE_NATIVE_FONTS = -1;
static final int DISABLE_DEPTH_TEST = 2;
@@ -422,7 +430,16 @@ public interface PConstants {
static final int ENABLE_STROKE_PURE = 9;
static final int DISABLE_STROKE_PURE = -9;
- static final int HINT_COUNT = 10;
+ static final int ENABLE_BUFFER_READING = 10;
+ static final int DISABLE_BUFFER_READING = -10;
+
+ static final int DISABLE_KEY_REPEAT = 11;
+ static final int ENABLE_KEY_REPEAT = -11;
+
+ static final int DISABLE_ASYNC_SAVEFRAME = 12;
+ static final int ENABLE_ASYNC_SAVEFRAME = -12;
+
+ static final int HINT_COUNT = 13;
// error messages
diff --git a/core/src/processing/core/PFont.java b/libs/processing-core/src/main/java/processing/core/PFont.java
similarity index 98%
rename from core/src/processing/core/PFont.java
rename to libs/processing-core/src/main/java/processing/core/PFont.java
index 7e71bed25..48a01b3cc 100644
--- a/core/src/processing/core/PFont.java
+++ b/libs/processing-core/src/main/java/processing/core/PFont.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2004-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
@@ -417,6 +418,19 @@ public int getSize() {
}
+ /**
+ * Returns the size that will be used when textFont(font) is called.
+ */
+ public int getDefaultSize() {
+ return size;
+ }
+
+
+ public boolean isSmooth() {
+ return smooth;
+ }
+
+
public void setSubsetting() {
subsetting = true;
}
diff --git a/core/src/processing/core/PGraphics.java b/libs/processing-core/src/main/java/processing/core/PGraphics.java
similarity index 86%
rename from core/src/processing/core/PGraphics.java
rename to libs/processing-core/src/main/java/processing/core/PGraphics.java
index a7e922343..dc1657cb2 100644
--- a/core/src/processing/core/PGraphics.java
+++ b/libs/processing-core/src/main/java/processing/core/PGraphics.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2004-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
@@ -25,12 +26,18 @@
import java.util.HashMap;
import java.util.WeakHashMap;
-
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import processing.android.AppComponent;
import processing.opengl.PGL;
import processing.opengl.PShader;
-
-import android.graphics.Bitmap;
import android.graphics.Color;
+import android.view.SurfaceHolder;
/**
@@ -118,26 +125,26 @@ public class PGraphics extends PImage implements PConstants {
// width and height are already inherited from PImage
- /// width minus one (useful for many calculations)
- protected int width1;
-
- /// height minus one (useful for many calculations)
- protected int height1;
+// /// width minus one (useful for many calculations)
+// protected int width1;
+//
+// /// height minus one (useful for many calculations)
+// protected int height1;
/// width * height (useful for many calculations)
public int pixelCount;
/// true if smoothing is enabled (read-only)
- public boolean smooth = false;
-
- /// the anti-aliasing level for renderers that support it
- protected int quality;
+ public int smooth;
// ........................................................
/// true if defaults() has been called a first time
protected boolean settingsInited;
+ /// true if settings should be re-applied on next beginDraw()
+ protected boolean reapplySettings;
+
/// set to a PGraphics object being used inside a beginRaw/endRaw() block
protected PGraphics raw;
@@ -152,7 +159,7 @@ public class PGraphics extends PImage implements PConstants {
* created any other way than size(). When this is set, the listeners
* are also added to the sketch.
*/
- protected boolean primarySurface;
+ protected boolean primaryGraphics;
// ........................................................
@@ -473,16 +480,6 @@ public class PGraphics extends PImage implements PConstants {
// ........................................................
- /**
- * Java AWT Image object associated with this renderer. For P2D and P3D,
- * this will be associated with their MemoryImageSource. For PGraphicsJava2D,
- * it will be the offscreen drawing buffer.
- */
- //public Image image;
- public Bitmap bitmap;
-
- // ........................................................
-
// internal color for setting/calculating
protected float calcR, calcG, calcB, calcA;
protected int calcRi, calcGi, calcBi, calcAi;
@@ -643,6 +640,22 @@ public class PGraphics extends PImage implements PConstants {
/// Number of V steps (aka "phi") along latitudinally top-to-bottom spanning pi
public int sphereDetailV = 0;
+ // ........................................................
+
+ // Variables used to save the surface contents before the activity is taken to the background.
+ protected String restoreFilename;
+ protected int restoreWidth, restoreHeight;
+ protected int restoreCount;
+ protected boolean restartedLoopingAfterResume = false;
+ protected boolean restoredSurface = true;
+
+ // This auxiliary variable is used to implement a little hack that fixes
+ // https://github.com/processing/processing-android/issues/147
+ // on older devices where the last frame cannot be maintained after ending
+ // the rendering in GL. The trick consists in running one more frame after the
+ // noLoop() call, which ensures that the FBO layer is properly initialized
+ // and drawn with the contents of the previous frame.
+ protected boolean requestedNoLoop = false;
//////////////////////////////////////////////////////////////
@@ -670,12 +683,12 @@ public void setParent(PApplet parent) { // ignore
* else that goes along with that.
*/
public void setPrimary(boolean primary) { // ignore
- this.primarySurface = primary;
+ this.primaryGraphics = primary;
// base images must be opaque (for performance and general
// headache reasons.. argh, a semi-transparent opengl surface?)
// use createGraphics() if you want a transparent surface.
- if (primarySurface) {
+ if (primaryGraphics) {
format = RGB;
}
}
@@ -690,6 +703,14 @@ public void setFrameRate(float framerate) { // ignore
}
+ public void surfaceChanged() { // ignore
+ }
+
+
+ public void reset() { // ignore
+ }
+
+
/**
* The final step in setting up a renderer, set its size of this renderer.
* This was formerly handled by the constructor, but instead it's been broken
@@ -704,19 +725,12 @@ public void setFrameRate(float framerate) { // ignore
public void setSize(int w, int h) { // ignore
width = w;
height = h;
- width1 = width - 1;
- height1 = height - 1;
-
- allocate();
- reapplySettings();
- }
+ pixelWidth = width * pixelDensity;
+ pixelHeight = height * pixelDensity;
- /**
- * Allocate memory for this renderer. Generally will need to be implemented
- * for all renderers.
- */
- protected void allocate() { }
+ reapplySettings = true;
+ }
/**
@@ -727,9 +741,14 @@ protected void allocate() { }
* endRaw(), in order to shut things off.
*/
public void dispose() { // ignore
+ parent = null;
}
+ public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore
+ return null;
+ }
+
//////////////////////////////////////////////////////////////
@@ -741,10 +760,10 @@ public void dispose() { // ignore
* image data, for instance a BufferedImage with tint() settings applied for
* PGraphicsJava2D, or resized image data and OpenGL texture indices for
* PGraphicsOpenGL.
- * @param renderer The PGraphics renderer associated to the image
+ * @param image The image to be stored
* @param storage The metadata required by the renderer
*/
- public void setCache(PImage image, Object storage) {
+ public void setCache(PImage image, Object storage) { // ignore
cacheMap.put(image, storage);
}
@@ -754,24 +773,22 @@ public void setCache(PImage image, Object storage) {
* will cache data in different formats, it's necessary to store cache data
* keyed by the renderer object. Otherwise, attempting to draw the same
* image to both a PGraphicsJava2D and a PGraphicsOpenGL will cause errors.
- * @param renderer The PGraphics renderer associated to the image
* @return metadata stored for the specified renderer
*/
- public Object getCache(PImage image) {
+ public Object getCache(PImage image) { // ignore
return cacheMap.get(image);
}
/**
* Remove information associated with this renderer from the cache, if any.
- * @param renderer The PGraphics renderer whose cache data should be removed
+ * @param image The image whose cache data should be removed
*/
- public void removeCache(PImage image) {
+ public void removeCache(PImage image) { // ignore
cacheMap.remove(image);
}
-
//////////////////////////////////////////////////////////////
// FRAME
@@ -788,16 +805,16 @@ public void requestFocus() { // ignore
/**
* Some renderers have requirements re: when they are ready to draw.
*/
- public boolean canDraw() { // ignore
- return true;
- }
+// public boolean canDraw() { // ignore
+// return true;
+// }
/**
* Try to draw, or put a draw request on the queue.
*/
- public void requestDraw() { // ignore
- }
+// public void requestDraw() { // ignore
+// }
/**
@@ -838,6 +855,7 @@ public void endPGL() {
protected void checkSettings() {
if (!settingsInited) defaultSettings();
+ if (reapplySettings) reapplySettings();
}
@@ -851,8 +869,6 @@ protected void checkSettings() {
* This is currently called by checkSettings(), during beginDraw().
*/
protected void defaultSettings() { // ignore
- smooth(); // 2.0a5
-
colorMode(RGB, 255);
fill(255);
stroke(0);
@@ -886,7 +902,7 @@ protected void defaultSettings() { // ignore
// a gray background (when just a transparent surface or an empty pdf
// is what's desired).
// this background() call is for the Java 2D and OpenGL renderers.
- if (primarySurface) {
+ if (primaryGraphics) {
//System.out.println("main drawing surface bg " + getClass().getName());
background(backgroundColor);
}
@@ -895,7 +911,7 @@ protected void defaultSettings() { // ignore
settingsInited = true;
// defaultSettings() overlaps reapplySettings(), don't do both
- //reapplySettings = false;
+ reapplySettings = false;
}
@@ -909,7 +925,7 @@ protected void defaultSettings() { // ignore
* size(), which is safely called from inside beginDraw(). And it cannot be
* called before defaultSettings(), so we should be safe.
*/
- protected void reapplySettings() {
+ protected void reapplySettings() { // ignore
// System.out.println("attempting reapplySettings()");
if (!settingsInited) return; // if this is the initial setup, no need to reapply
@@ -945,12 +961,12 @@ protected void reapplySettings() {
} else {
noTint();
}
- if (smooth) {
- smooth();
- } else {
- // Don't bother setting this, cuz it'll anger P3D.
- noSmooth();
- }
+// if (smooth) {
+// smooth();
+// } else {
+// // Don't bother setting this, cuz it'll anger P3D.
+// noSmooth();
+// }
if (textFont != null) {
// System.out.println(" textFont in reapply is " + textFont);
// textFont() resets the leading, so save it in case it's changed
@@ -964,7 +980,65 @@ protected void reapplySettings() {
blendMode(blendMode);
- //reapplySettings = false;
+ reapplySettings = false;
+ }
+
+ //////////////////////////////////////////////////////////////
+
+ // RENDERER STATE
+
+
+ protected void clearState() { // ignore
+ // Nothing to do here, it depends on the renderer's implementation.
+ }
+
+
+ protected void saveState() { // ignore
+ // Nothing to do here, it depends on the renderer's implementation.
+ }
+
+
+ protected void restoreState() { // ignore
+ // This method probably does not need to be re-implemented in the subclasses. All we need to
+ // do is to check for the resume in no-loop state situation:
+ restoredSurface = false;
+ if (!parent.looping) {
+ // The sketch needs to draw a few frames after resuming so it has the chance to restore the
+ // screen contents:
+ // https://github.com/processing/processing-android/issues/492
+ // so we restart looping:
+ parent.loop();
+ // and flag this situation when the surface has been restored:
+ restartedLoopingAfterResume = true;
+ }
+ }
+
+
+ protected boolean restoringState() { // ignore
+ return !restoredSurface && restartedLoopingAfterResume;
+ }
+
+
+ protected void restoreSurface() { // ignore
+ // When implementing this method in a subclass of PGraphics, it should add a call to the super
+ // implementation, to make sure that the looping is stopped in the case where the sketch was
+ // resumed in no-loop state (see comment in restoreState() method above).
+ if (restoredSurface && restartedLoopingAfterResume) {
+ restartedLoopingAfterResume = false;
+ parent.noLoop();
+ }
+ }
+
+
+ protected boolean requestNoLoop() { // ignore
+ // Some renderers (OpenGL) cannot be set to no-loop right away, it has to be requested so
+ // any pending frames are properly rendered. Override as needed.
+ return false;
+ }
+
+
+ protected boolean isLooping() { // ignore
+ return parent.isLooping() && (!requestNoLoop() || !requestedNoLoop);
}
@@ -990,7 +1064,18 @@ protected void reapplySettings() {
* turns off the z-buffer in the P3D or OPENGL renderers.
*
*/
+ @SuppressWarnings("deprecation")
public void hint(int which) {
+ if (which == ENABLE_NATIVE_FONTS ||
+ which == DISABLE_NATIVE_FONTS) {
+ showWarning("hint(ENABLE_NATIVE_FONTS) no longer supported. " +
+ "Use createFont() instead.");
+ }
+ if (which == ENABLE_KEY_REPEAT) {
+ parent.keyRepeatEnabled = true;
+ } else if (which == DISABLE_KEY_REPEAT) {
+ parent.keyRepeatEnabled = false;
+ }
if (which > 0) {
hints[which] = true;
} else {
@@ -1076,6 +1161,37 @@ public void normal(float nx, float ny, float nz) {
}
}
+
+ public void attribPosition(String name, float x, float y, float z) {
+ showMissingWarning("attrib");
+ }
+
+
+ public void attribNormal(String name, float nx, float ny, float nz) {
+ showMissingWarning("attrib");
+ }
+
+
+ public void attribColor(String name, int color) {
+ showMissingWarning("attrib");
+ }
+
+
+ public void attrib(String name, float... values) {
+ showMissingWarning("attrib");
+ }
+
+
+ public void attrib(String name, int... values) {
+ showMissingWarning("attrib");
+ }
+
+
+ public void attrib(String name, boolean... values) {
+ showMissingWarning("attrib");
+ }
+
+
/**
* Set texture mode to either to use coordinates based on the IMAGE
* (more intuitive for new users) or NORMALIZED (better for advanced chaps)
@@ -1466,35 +1582,132 @@ public PShape loadShape(String filename) {
}
+ public PShape loadShape(String filename, String options) {
+ showMissingWarning("loadShape");
+ return null;
+ }
+
+
//////////////////////////////////////////////////////////////
// SHAPE CREATION
- public PShape createShape(PShape source) {
- showMissingWarning("createShape");
- return null;
+ /**
+ * @webref shape
+ * @see PShape
+ * @see PShape#endShape()
+ * @see PApplet#loadShape(String)
+ */
+ public PShape createShape() {
+ // Defaults to GEOMETRY (rather than GROUP like the default constructor)
+ // because that's how people will use it within a sketch.
+ return createShape(PShape.GEOMETRY);
}
- public PShape createShape() {
- showMissingWarning("createShape");
- return null;
+ // POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, QUAD_STRIP
+ public PShape createShape(int type) {
+ // If it's a PRIMITIVE, it needs the 'params' field anyway
+ if (type == PConstants.GROUP ||
+ type == PShape.PATH ||
+ type == PShape.GEOMETRY) {
+ return createShapeFamily(type);
+ }
+ final String msg =
+ "Only GROUP, PShape.PATH, and PShape.GEOMETRY work with createShape()";
+ throw new IllegalArgumentException(msg);
}
- public PShape createShape(int type) {
- showMissingWarning("createShape");
- return null;
+ /** Override this method to return an appropriate shape for your renderer */
+ protected PShape createShapeFamily(int type) {
+ return new PShape(this, type);
+// showMethodWarning("createShape()");
+// return null;
}
+ /**
+ * @param kind either POINT, LINE, TRIANGLE, QUAD, RECT, ELLIPSE, ARC, BOX, SPHERE
+ * @param p parameters that match the kind of shape
+ */
public PShape createShape(int kind, float... p) {
- showMissingWarning("createShape");
- return null;
+ int len = p.length;
+
+ if (kind == POINT) {
+ if (is3D() && len != 2 && len != 3) {
+ throw new IllegalArgumentException("Use createShape(POINT, x, y) or createShape(POINT, x, y, z)");
+ } else if (len != 2) {
+ throw new IllegalArgumentException("Use createShape(POINT, x, y)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == LINE) {
+ if (is3D() && len != 4 && len != 6) {
+ throw new IllegalArgumentException("Use createShape(LINE, x1, y1, x2, y2) or createShape(LINE, x1, y1, z1, x2, y2, z1)");
+ } else if (len != 4) {
+ throw new IllegalArgumentException("Use createShape(LINE, x1, y1, x2, y2)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == TRIANGLE) {
+ if (len != 6) {
+ throw new IllegalArgumentException("Use createShape(TRIANGLE, x1, y1, x2, y2, x3, y3)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == QUAD) {
+ if (len != 8) {
+ throw new IllegalArgumentException("Use createShape(QUAD, x1, y1, x2, y2, x3, y3, x4, y4)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == RECT) {
+ if (len != 4 && len != 5 && len != 8 && len != 9) {
+ throw new IllegalArgumentException("Wrong number of parameters for createShape(RECT), see the reference");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == ELLIPSE) {
+ if (len != 4 && len != 5) {
+ throw new IllegalArgumentException("Use createShape(ELLIPSE, x, y, w, h) or createShape(ELLIPSE, x, y, w, h, mode)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == ARC) {
+ if (len != 6 && len != 7) {
+ throw new IllegalArgumentException("Use createShape(ARC, x, y, w, h, start, stop)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == BOX) {
+ if (!is3D()) {
+ throw new IllegalArgumentException("createShape(BOX) is not supported in 2D");
+ } else if (len != 1 && len != 3) {
+ throw new IllegalArgumentException("Use createShape(BOX, size) or createShape(BOX, width, height, depth)");
+ }
+ return createShapePrimitive(kind, p);
+
+ } else if (kind == SPHERE) {
+ if (!is3D()) {
+ throw new IllegalArgumentException("createShape(SPHERE) is not supported in 2D");
+ } else if (len != 1) {
+ throw new IllegalArgumentException("Use createShape(SPHERE, radius)");
+ }
+ return createShapePrimitive(kind, p);
+ }
+ throw new IllegalArgumentException("Unknown shape type passed to createShape()");
}
+ /** Override this to have a custom shape object used by your renderer. */
+ protected PShape createShapePrimitive(int kind, float... p) {
+// showMethodWarning("createShape()");
+// return null;
+ return new PShape(this, kind, p);
+ }
+
//////////////////////////////////////////////////////////////
@@ -1997,6 +2210,10 @@ protected void rectImpl(float x1, float y1, float x2, float y2,
}
+ public void square(float x, float y, float extent) {
+ rect(x, y, extent, extent);
+ }
+
//////////////////////////////////////////////////////////////
@@ -2116,6 +2333,10 @@ protected void arcImpl(float x, float y, float w, float h,
}
+ public void circle(float x, float y, float extent) {
+ ellipse(x, y, extent, extent);
+ }
+
//////////////////////////////////////////////////////////////
@@ -2363,11 +2584,10 @@ public void sphere(float r) {
* endShape();
*/
public float bezierPoint(float a, float b, float c, float d, float t) {
- float t1 = 1.0f - t;
- return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t;
+ float t1 = t-1.0f;
+ return t * ( 3*t1*(b*t1-c*t) + d*t*t ) - a*t1*t1*t1;
}
-
/**
* Provide the tangent at the given point on the bezier curve.
* Fix from davbol for 0136.
@@ -2643,26 +2863,37 @@ protected void splineForward(int segments, PMatrix3D matrix) {
// SMOOTHING
- /**
- * If true in PImage, use bilinear interpolation for copy()
- * operations. When inherited by PGraphics, also controls shapes.
- */
- public void smooth() {
- smooth = true;
+ public void smooth() { // ignore
+ smooth(1);
}
- public void smooth(int level) {
- smooth = true;
+
+ public void smooth(int quality) { // ignore
+ if (primaryGraphics) {
+ parent.smooth(quality);
+ } else {
+ // for createGraphics(), make sure beginDraw() not called yet
+ if (settingsInited) {
+ // ignore if it's just a repeat of the current state
+ if (this.smooth != quality) {
+ smoothWarning("smooth");
+ }
+ } else {
+ this.smooth = quality;
+ }
+ }
}
- /**
- * Disable smoothing. See smooth().
- */
- public void noSmooth() {
- smooth = false;
+
+ public void noSmooth() { // ignore
+ smooth(0);
}
+ private void smoothWarning(String method) {
+ PGraphics.showWarning("%s() can only be used before beginDraw()", method);
+ }
+
//////////////////////////////////////////////////////////////
@@ -2965,10 +3196,47 @@ public float textDescent() {
* The leading will also be reset.
*/
public void textFont(PFont which) {
- if (which != null) {
- textFont = which;
+ if (which == null) {
+ throw new RuntimeException(ERROR_TEXTFONT_NULL_PFONT);
+ }
+ textFontImpl(which, which.getDefaultSize());
+ }
+
+
+ /**
+ * Useful function to set the font and size at the same time.
+ */
+ public void textFont(PFont which, float size) {
+ if (which == null) {
+ throw new RuntimeException(ERROR_TEXTFONT_NULL_PFONT);
+ }
+ // https://github.com/processing/processing/issues/3110
+ if (size <= 0) {
+ // Using System.err instead of showWarning to avoid running out of
+ // memory with a bunch of textSize() variants (cause of this bug is
+ // usually something done with map() or in a loop).
+ System.err.println("textFont: ignoring size " + size + " px:" +
+ "the text size must be larger than zero");
+ size = textSize;
+ }
+ textFontImpl(which, size);
+ }
+
+
+ /**
+ * Called from textFont. Check the validity of args and
+ * print possible errors to the user before calling this.
+ * Subclasses will want to override this one.
+ *
+ * @param which font to set, not null
+ * @param size size to set, greater than zero
+ */
+ protected void textFontImpl(PFont which, float size) {
+ textFont = which;
// if (hints[ENABLE_NATIVE_FONTS]) {
-// which.findTypeface(name);
+// //if (which.font == null) {
+// which.findNative();
+// //}
// }
/*
textFontNative = which.font;
@@ -2993,20 +3261,8 @@ public void textFont(PFont which) {
// float w = font.getStringBounds(text, g2.getFontRenderContext()).getWidth();
}
*/
- textSize(which.size);
- } else {
- throw new RuntimeException(ERROR_TEXTFONT_NULL_PFONT);
- }
- }
-
-
- /**
- * Useful function to set the font and size at the same time.
- */
- public void textFont(PFont which, float size) {
- textFont(which);
- textSize(size);
+ handleTextSize(size);
}
@@ -3059,15 +3315,40 @@ protected boolean textModeCheck(int mode) {
* Sets the text size, also resets the value for the leading.
*/
public void textSize(float size) {
+ // https://github.com/processing/processing/issues/3110
+ if (size <= 0) {
+ // Using System.err instead of showWarning to avoid running out of
+ // memory with a bunch of textSize() variants (cause of this bug is
+ // usually something done with map() or in a loop).
+ System.err.println("textSize(" + size + ") ignored: " +
+ "the text size must be larger than zero");
+ return;
+ }
if (textFont == null) {
defaultFontOrDeath("textSize", size);
}
+ textSizeImpl(size);
+ }
+
+
+ /**
+ * Called from textSize() after validating size. Subclasses
+ * will want to override this one.
+ * @param size size of the text, greater than zero
+ */
+ protected void textSizeImpl(float size) {
+ handleTextSize(size);
+ }
+
+ /**
+ * Sets the actual size. Called from textSizeImpl and
+ * from textFontImpl after setting the font.
+ * @param size size of the text, greater than zero
+ */
+ protected void handleTextSize(float size) {
textSize = size;
-// PApplet.println("textSize textAscent -> " + textAscent());
-// PApplet.println("textSize textDescent -> " + textDescent());
textLeading = (textAscent() + textDescent()) * 1.275f;
-// PApplet.println("textSize textLeading = " + textLeading);
}
@@ -3113,6 +3394,11 @@ public float textWidth(String str) {
}
+ public float textWidth(char[] chars, int start, int length) {
+ return textWidthImpl(chars, start, start + length);
+ }
+
+
/**
* Implementation of returning the text width of
* the chars [start, stop) in the buffer.
@@ -3183,14 +3469,6 @@ public void text(char c, float x, float y, float z) {
}
-// /**
-// * Write text where we just left off.
-// */
-// public void text(String str) {
-// text(str, textX, textY, textZ);
-// }
-
-
/**
* Draw a chunk of text.
* Newlines that are \n (Unix newline or linefeed char, ascii 10)
@@ -3248,6 +3526,52 @@ public void text(String str, float x, float y) {
}
+ /**
+ * Method to draw text from an array of chars. This method will usually be
+ * more efficient than drawing from a String object, because the String will
+ * not be converted to a char array before drawing.
+ */
+ public void text(char[] chars, int start, int stop, float x, float y) {
+ // If multiple lines, sum the height of the additional lines
+ float high = 0; //-textAscent();
+ for (int i = start; i < stop; i++) {
+ if (chars[i] == '\n') {
+ high += textLeading;
+ }
+ }
+ if (textAlignY == CENTER) {
+ // for a single line, this adds half the textAscent to y
+ // for multiple lines, subtract half the additional height
+ //y += (textAscent() - textDescent() - high)/2;
+ y += (textAscent() - high)/2;
+ } else if (textAlignY == TOP) {
+ // for a single line, need to add textAscent to y
+ // for multiple lines, no different
+ y += textAscent();
+ } else if (textAlignY == BOTTOM) {
+ // for a single line, this is just offset by the descent
+ // for multiple lines, subtract leading for each line
+ y -= textDescent() + high;
+ //} else if (textAlignY == BASELINE) {
+ // do nothing
+ }
+
+// int start = 0;
+ int index = 0;
+ while (index < stop) { //length) {
+ if (chars[index] == '\n') {
+ textLineAlignImpl(chars, start, index, x, y);
+ start = index + 1;
+ y += textLeading;
+ }
+ index++;
+ }
+ if (start < stop) { //length) {
+ textLineAlignImpl(chars, start, index, x, y);
+ }
+ }
+
+
/**
* Same as above but with a z coordinate.
*/
@@ -3266,6 +3590,17 @@ public void text(String str, float x, float y, float z) {
}
+ public void text(char[] chars, int start, int stop,
+ float x, float y, float z) {
+ if (z != 0) translate(0, 0, z); // slow!
+
+ text(chars, start, stop, x, y);
+// textZ = z;
+
+ if (z != 0) translate(0, 0, -z); // inaccurate!
+ }
+
+
/**
* Draw text in a box that is constrained to a particular size.
* The current rectMode() determines what the coordinates mean
@@ -3397,8 +3732,8 @@ public void text(String str, float x1, float y1, float x2, float y2) {
* Emit a sentence of text, defined as a chunk of text without any newlines.
* @param stop non-inclusive, the end of the text in question
*/
- private boolean textSentence(char[] buffer, int start, int stop,
- float boxWidth, float spaceWidth) {
+ protected boolean textSentence(char[] buffer, int start, int stop,
+ float boxWidth, float spaceWidth) {
float runningX = 0;
// Keep track of this separately from index, since we'll need to back up
@@ -3463,7 +3798,7 @@ private boolean textSentence(char[] buffer, int start, int stop,
}
- private void textSentenceBreak(int start, int stop) {
+ protected void textSentenceBreak(int start, int stop) {
if (textBreakCount == textBreakStart.length) {
textBreakStart = PApplet.expand(textBreakStart);
textBreakStop = PApplet.expand(textBreakStop);
@@ -3592,6 +3927,22 @@ protected void textCharModelImpl(PImage glyph,
}
+ //////////////////////////////////////////////////////////////
+
+ // PARITY WITH P5.JS
+
+
+ public void push() {
+ pushStyle();
+ pushMatrix();
+ }
+
+
+ public void pop() {
+ popStyle();
+ popMatrix();
+ }
+
//////////////////////////////////////////////////////////////
@@ -3818,6 +4169,46 @@ public PMatrix3D getMatrix(PMatrix3D target) {
}
+ /**
+ * Returns a copy of the current object matrix.
+ * Pass in null to create a new matrix.
+ */
+ public PMatrix3D getObjectMatrix() {
+ showMissingWarning("getObjectMatrix");
+ return null;
+ }
+
+
+ /**
+ * Copy the current object matrix into the specified target.
+ * Pass in null to create a new matrix.
+ */
+ public PMatrix3D getObjectMatrix(PMatrix3D target) {
+ showMissingWarning("getObjectMatrix");
+ return null;
+ }
+
+
+ /**
+ * Returns a copy of the current eye matrix.
+ * Pass in null to create a new matrix.
+ */
+ public PMatrix3D getEyeMatrix() {
+ showMissingWarning("getEyeMatrix");
+ return null;
+ }
+
+
+ /**
+ * Copy the current eye matrix into the specified target.
+ * Pass in null to create a new matrix.
+ */
+ public PMatrix3D getEyeMatrix(PMatrix3D target) {
+ showMissingWarning("getEyeMatrix");
+ return null;
+ }
+
+
/**
* Set the current transformation matrix to the contents of another.
*/
@@ -3859,6 +4250,11 @@ public void printMatrix() {
// CAMERA
+ public void cameraUp() {
+ showMethodWarning("cameraUp");
+ }
+
+
public void beginCamera() {
showMethodWarning("beginCamera");
}
@@ -3886,6 +4282,10 @@ public void printCamera() {
}
+ public void eye() {
+ showMethodWarning("eye");
+ }
+
//////////////////////////////////////////////////////////////
@@ -4034,6 +4434,69 @@ public float modelZ(float x, float y, float z) {
}
+ //////////////////////////////////////////////////////////////
+
+ // RAY CASTING
+
+
+ public PVector[] getRayFromScreen(float screenX, float screenY, PVector[] ray) {
+ showMissingWarning("getRayFromScreen");
+ return null;
+ }
+
+
+ public void getRayFromScreen(float screenX, float screenY, PVector origin, PVector direction) {
+ showMissingWarning("getRayFromScreen");
+ }
+
+
+ public boolean intersectsSphere(float r, float screenX, float screenY) {
+ showMissingWarning("intersectsSphere");
+ return false;
+ }
+
+
+ public boolean intersectsSphere(float r, PVector origin, PVector direction) {
+ showMissingWarning("intersectsSphere");
+ return false;
+ }
+
+
+ public boolean intersectsBox(float size, float screenX, float screenY) {
+ showMissingWarning("intersectsBox");
+ return false;
+ }
+
+
+ public boolean intersectsBox(float w, float h, float d, float screenX, float screenY) {
+ showMissingWarning("intersectsBox");
+ return false;
+ }
+
+
+ public boolean intersectsBox(float size, PVector origin, PVector direction) {
+ showMissingWarning("intersectsBox");
+ return false;
+ }
+
+
+ public boolean intersectsBox(float w, float h, float d, PVector origin, PVector direction) {
+ showMissingWarning("intersectsBox");
+ return false;
+ }
+
+
+ public PVector intersectsPlane(float screenX, float screenY) {
+ showMissingWarning("intersectsPlane");
+ return null;
+ }
+
+
+ public PVector intersectsPlane(PVector origin, PVector direction) {
+ showMissingWarning("intersectsPlane");
+ return null;
+ }
+
//////////////////////////////////////////////////////////////
@@ -4073,6 +4536,8 @@ public void style(PStyle s) {
ellipseMode(s.ellipseMode);
shapeMode(s.shapeMode);
+ blendMode(s.blendMode);
+
if (s.tint) {
tint(s.tintColor);
} else {
@@ -4151,6 +4616,8 @@ public PStyle getStyle(PStyle s) { // ignore
s.ellipseMode = ellipseMode;
s.shapeMode = shapeMode;
+ s.blendMode = blendMode;
+
s.colorMode = colorMode;
s.colorModeX = colorModeX;
s.colorModeY = colorModeY;
@@ -5235,13 +5702,16 @@ public int lerpColor(int c1, int c2, float amt) {
static float[] lerpColorHSB1;
static float[] lerpColorHSB2;
- static float[] lerpColorHSB3;
+ static float[] lerpColorHSB3;
/**
* Interpolate between two colors. Like lerp(), but for the
* individual color components of a color supplied as an int value.
*/
static public int lerpColor(int c1, int c2, float amt, int mode) {
+ if (amt < 0) amt = 0;
+ if (amt > 1) amt = 1;
+
if (mode == RGB) {
float a1 = ((c1 >> 24) & 0xff);
float r1 = (c1 >> 16) & 0xff;
@@ -5252,10 +5722,10 @@ static public int lerpColor(int c1, int c2, float amt, int mode) {
float g2 = (c2 >> 8) & 0xff;
float b2 = c2 & 0xff;
- return (((int) (a1 + (a2-a1)*amt) << 24) |
- ((int) (r1 + (r2-r1)*amt) << 16) |
- ((int) (g1 + (g2-g1)*amt) << 8) |
- ((int) (b1 + (b2-b1)*amt)));
+ return ((PApplet.round(a1 + (a2-a1)*amt) << 24) |
+ (PApplet.round(r1 + (r2-r1)*amt) << 16) |
+ (PApplet.round(g1 + (g2-g1)*amt) << 8) |
+ (PApplet.round(b1 + (b2-b1)*amt)));
} else if (mode == HSB) {
if (lerpColorHSB1 == null) {
@@ -5266,7 +5736,7 @@ static public int lerpColor(int c1, int c2, float amt, int mode) {
float a1 = (c1 >> 24) & 0xff;
float a2 = (c2 >> 24) & 0xff;
- int alfa = ((int) (a1 + (a2-a1)*amt)) << 24;
+ int alfa = (PApplet.round(a1 + (a2-a1)*amt)) << 24;
Color.RGBToHSV((c1 >> 16) & 0xff, (c1 >> 8) & 0xff, c1 & 0xff,
lerpColorHSB1);
@@ -5298,23 +5768,21 @@ static public int lerpColor(int c1, int c2, float amt, int mode) {
}
float ho = (PApplet.lerp(lerpColorHSB1[0], lerpColorHSB2[0], amt)) % 1.0f;
*/
+ // float ho = PApplet.lerp(lerpColorHSB1[0], lerpColorHSB2[0], amt);
+ // float so = PApplet.lerp(lerpColorHSB1[1], lerpColorHSB2[1], amt);
+ // float bo = PApplet.lerp(lerpColorHSB1[2], lerpColorHSB2[2], amt);
-// float ho = PActivity.lerp(lerpColorHSB1[0], lerpColorHSB2[0], amt);
-// float so = PActivity.lerp(lerpColorHSB1[1], lerpColorHSB2[1], amt);
-// float bo = PActivity.lerp(lerpColorHSB1[2], lerpColorHSB2[2], amt);
-// return alfa | (Color.HSVtoRGB(ho, so, bo) & 0xFFFFFF);
-// return Color.HSVToColor(alfa, new float[] { ho, so, bo });
+ // return alfa | (Color.RGBToHSV(ho, so, bo) & 0xFFFFFF);
lerpColorHSB3[0] = PApplet.lerp(lerpColorHSB1[0], lerpColorHSB2[0], amt);
lerpColorHSB3[1] = PApplet.lerp(lerpColorHSB1[1], lerpColorHSB2[1], amt);
lerpColorHSB3[2] = PApplet.lerp(lerpColorHSB1[2], lerpColorHSB2[2], amt);
- return Color.HSVToColor(alfa, lerpColorHSB3);
+ return Color.HSVToColor(alfa, lerpColorHSB3);
}
return 0;
}
-
//////////////////////////////////////////////////////////////
// BEGINRAW/ENDRAW
@@ -5510,4 +5978,158 @@ public boolean is3D() {
public boolean isGL() {
return false;
}
+
+
+ //////////////////////////////////////////////////////////////
+
+ // ASYNC IMAGE SAVING
+
+
+ @Override
+ public boolean save(String filename) { // ignore
+
+ if (hints[DISABLE_ASYNC_SAVEFRAME]) {
+ return super.save(filename);
+ }
+
+ if (asyncImageSaver == null) {
+ asyncImageSaver = new AsyncImageSaver();
+ }
+
+ if (!loaded) loadPixels();
+ PImage target = asyncImageSaver.getAvailableTarget(pixelWidth, pixelHeight,
+ format);
+ if (target == null) return false;
+ int count = PApplet.min(pixels.length, target.pixels.length);
+ System.arraycopy(pixels, 0, target.pixels, 0, count);
+ asyncImageSaver.saveTargetAsync(this, target, filename);
+
+ return true;
+ }
+
+ protected void processImageBeforeAsyncSave(PImage image) { }
+
+
+ protected static AsyncImageSaver asyncImageSaver;
+
+ protected static class AsyncImageSaver {
+
+ static final int TARGET_COUNT =
+ Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
+
+ BlockingQueue targetPool = new ArrayBlockingQueue<>(TARGET_COUNT);
+ ExecutorService saveExecutor = Executors.newFixedThreadPool(TARGET_COUNT);
+
+ int targetsCreated = 0;
+
+
+ static final int TIME_AVG_FACTOR = 32;
+
+ volatile long avgNanos = 0;
+ long lastTime = 0;
+ int lastFrameCount = 0;
+
+
+ public AsyncImageSaver() { } // ignore
+
+
+ public void dispose() { // ignore
+ saveExecutor.shutdown();
+ try {
+ saveExecutor.awaitTermination(5000, TimeUnit.SECONDS);
+ } catch (InterruptedException e) { }
+ }
+
+
+ public boolean hasAvailableTarget() { // ignore
+ return targetsCreated < TARGET_COUNT || targetPool.isEmpty();
+ }
+
+
+ /**
+ * After taking a target, you must call saveTargetAsync() or
+ * returnUnusedTarget(), otherwise one thread won't be able to run
+ */
+ public PImage getAvailableTarget(int requestedWidth, int requestedHeight, // ignore
+ int format) {
+ try {
+ PImage target;
+ if (targetsCreated < TARGET_COUNT && targetPool.isEmpty()) {
+ target = new PImage(requestedWidth, requestedHeight);
+ targetsCreated++;
+ } else {
+ target = targetPool.take();
+ if (target.width != requestedWidth ||
+ target.height != requestedHeight) {
+ target.width = requestedWidth;
+ target.height = requestedHeight;
+ // TODO: this kills performance when saving different sizes
+ target.pixels = new int[requestedWidth * requestedHeight];
+ }
+ }
+ target.format = format;
+ return target;
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+
+
+ public void returnUnusedTarget(PImage target) { // ignore
+ targetPool.offer(target);
+ }
+
+
+ public void saveTargetAsync(final PGraphics renderer, final PImage target, // ignore
+ final String filename) {
+ target.parent = renderer.parent;
+
+ // if running every frame, smooth the framerate
+ if (target.parent.frameCount - 1 == lastFrameCount && TARGET_COUNT > 1) {
+
+ // count with one less thread to reduce jitter
+ // 2 cores - 1 save thread - no wait
+ // 4 cores - 3 save threads - wait 1/2 of save time
+ // 8 cores - 7 save threads - wait 1/6 of save time
+ long avgTimePerFrame = avgNanos / (Math.max(1, TARGET_COUNT - 1));
+ long now = System.nanoTime();
+ long delay = PApplet.round((lastTime + avgTimePerFrame - now) / 1e6f);
+ try {
+ if (delay > 0) Thread.sleep(delay);
+ } catch (InterruptedException e) { }
+ }
+
+ lastFrameCount = target.parent.frameCount;
+ lastTime = System.nanoTime();
+
+ try {
+ saveExecutor.submit(new Runnable() {
+ @Override
+ public void run() { // ignore
+ try {
+ long startTime = System.nanoTime();
+ renderer.processImageBeforeAsyncSave(target);
+ target.save(filename);
+ long saveNanos = System.nanoTime() - startTime;
+ synchronized (AsyncImageSaver.this) {
+ if (avgNanos == 0) {
+ avgNanos = saveNanos;
+ } else if (saveNanos < avgNanos) {
+ avgNanos = (avgNanos * (TIME_AVG_FACTOR - 1) + saveNanos) /
+ (TIME_AVG_FACTOR);
+ } else {
+ avgNanos = saveNanos;
+ }
+ }
+ } finally {
+ targetPool.offer(target);
+ }
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ // the executor service was probably shut down, no more saving for us
+ }
+ }
+ }
+
}
diff --git a/core/src/processing/core/PImage.java b/libs/processing-core/src/main/java/processing/core/PImage.java
similarity index 79%
rename from core/src/processing/core/PImage.java
rename to libs/processing-core/src/main/java/processing/core/PImage.java
index a75ebb07b..8eceaf2c4 100644
--- a/core/src/processing/core/PImage.java
+++ b/libs/processing-core/src/main/java/processing/core/PImage.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2004-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
@@ -51,6 +52,13 @@ public class PImage implements PConstants, Cloneable {
public int[] pixels;
public int width, height;
+ /**
+ * For the time being, simply to ensure compatibility with Java mode code
+ */
+ public int pixelDensity = 1;
+ public int pixelWidth;
+ public int pixelHeight;
+
/**
* Path to parent object that will be used with save().
* This prevents users from needing savePath() to use PImage.save().
@@ -148,6 +156,10 @@ public void init(int width, int height, int format) { // ignore
this.pixels = new int[width*height];
this.format = format;
// this.cache = null;
+
+ pixelWidth = width * pixelDensity;
+ pixelHeight = height * pixelDensity;
+ this.pixels = new int[pixelWidth * pixelHeight];
}
@@ -182,6 +194,9 @@ public PImage(Object nativeObject) {
this.height = bitmap.getHeight();
this.pixels = null;
this.format = bitmap.hasAlpha() ? ARGB : RGB;
+ this.pixelDensity = 1;
+ this.pixelWidth = width;
+ this.pixelHeight = height;
}
@@ -193,6 +208,13 @@ public Object getNative() {
}
+ public void setNative(Object nativeObject) {
+ Bitmap bitmap = (Bitmap) nativeObject;
+ this.bitmap = bitmap;
+ }
+
+
+
//////////////////////////////////////////////////////////////
// MARKING IMAGE AS MODIFIED / FOR USE w/ GET/SET
@@ -242,9 +264,24 @@ public void loadPixels() { // ignore
if (pixels == null || pixels.length != width*height) {
pixels = new int[width*height];
}
+
if (bitmap != null) {
- bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+ if (modified) {
+ // The pixels array has been used to do color manipulations, so
+ // the bitmap should be updated
+ if (!bitmap.isMutable()) {
+ // create a mutable version of this bitmap
+ bitmap = bitmap.copy(Config.ARGB_8888, true);
+ }
+ bitmap.setPixels(pixels, 0, width, mx1, my1, mx2 - mx1, my2 - my1);
+ modified = false;
+ } else {
+ // Get wherever it is in the bitmap right now, we assume is the most
+ // up-to-date version of the image.
+ bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+ }
}
+
setLoaded();
}
@@ -336,6 +373,10 @@ public Object clone() throws CloneNotSupportedException { // ignore
* Use 0 for wide or high to make that dimension scale proportionally.
*/
public void resize(int w, int h) { // ignore
+ if (bitmap == null) {
+ return; // Cannot resize an image not backed by a bitmap
+ }
+
if (w <= 0 && h <= 0) {
throw new IllegalArgumentException("width or height must be > 0 for resize");
}
@@ -348,11 +389,15 @@ public void resize(int w, int h) { // ignore
h = (int) (height * diff);
}
bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+ if (pixels != null) {
+ // Resize pixels array, if in use.
+ pixels = new int[w * h];
+ bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
+ }
this.width = w;
this.height = h;
-
- // Mark the pixels array as altered
- updatePixels();
+ this.pixelWidth = w * pixelDensity;
+ this.pixelHeight = h * pixelDensity;
}
@@ -476,6 +521,8 @@ public PImage get(int x, int y, int w, int h) {
target.parent = parent; // parent may be null so can't use createImage()
if (w > 0 && h > 0) {
getImpl(x, y, w, h, target, targetX, targetY);
+ Bitmap nat = Bitmap.createBitmap(target.pixels, targetWidth, targetHeight, Config.ARGB_8888);
+ target.setNative(nat);
}
return target;
}
@@ -490,11 +537,11 @@ public PImage get(int x, int y, int w, int h) {
protected void getImpl(int sourceX, int sourceY,
int sourceWidth, int sourceHeight,
PImage target, int targetX, int targetY) {
- if (pixels == null) {
+ if (bitmap != null) {
bitmap.getPixels(target.pixels,
targetY*target.width + targetX, target.width,
sourceX, sourceY, sourceWidth, sourceHeight);
- } else {
+ } else if (pixels != null) {
int sourceIndex = sourceY*width + sourceX;
int targetIndex = targetY*target.width + targetX;
for (int row = 0; row < sourceHeight; row++) {
@@ -516,6 +563,11 @@ public PImage get() {
}
+ public PImage copy() {
+ return get(0, 0, pixelWidth, pixelHeight);
+ }
+
+
/**
* Set a single pixel to the specified color.
*/
@@ -1379,24 +1431,6 @@ public void blend(int sx, int sy, int sw, int sh,
public void blend(PImage src,
int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh, int mode) {
- /*
- if (imageMode == CORNER) { // if CORNERS, do nothing
- sx2 += sx1;
- sy2 += sy1;
- dx2 += dx1;
- dy2 += dy1;
-
- } else if (imageMode == CENTER) {
- sx1 -= sx2 / 2f;
- sy1 -= sy2 / 2f;
- sx2 += sx1;
- sy2 += sy1;
- dx1 -= dx2 / 2f;
- dy1 -= dy2 / 2f;
- dx2 += dx1;
- dy2 += dy1;
- }
- */
int sx2 = sx + sw;
int sy2 = sy + sh;
int dx2 = dx + dw;
@@ -1405,18 +1439,18 @@ public void blend(PImage src,
loadPixels();
if (src == this) {
if (intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
- blit_resize(get(sx, sy, sx2 - sx, sy2 - sy),
- 0, 0, sx2 - sx - 1, sy2 - sy - 1,
- pixels, width, height, dx, dy, dx2, dy2, mode);
+ blit_resize(get(sx, sy, sw, sh),
+ 0, 0, sw, sh,
+ pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
} else {
// same as below, except skip the loadPixels() because it'd be redundant
blit_resize(src, sx, sy, sx2, sy2,
- pixels, width, height, dx, dy, dx2, dy2, mode);
+ pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
}
} else {
src.loadPixels();
blit_resize(src, sx, sy, sx2, sy2,
- pixels, width, height, dx, dy, dx2, dy2, mode);
+ pixels, pixelWidth, pixelHeight, dx, dy, dx2, dy2, mode);
//src.updatePixels();
}
updatePixels();
@@ -1474,8 +1508,8 @@ private void blit_resize(PImage img,
int mode) {
if (srcX1 < 0) srcX1 = 0;
if (srcY1 < 0) srcY1 = 0;
- if (srcX2 > img.width) srcX2 = img.width;
- if (srcY2 > img.height) srcY2 = img.height;
+ if (srcX2 > img.pixelWidth) srcX2 = img.pixelWidth;
+ if (srcY2 > img.pixelHeight) srcY2 = img.pixelHeight;
int srcW = srcX2 - srcX1;
int srcH = srcY2 - srcY1;
@@ -1491,15 +1525,15 @@ private void blit_resize(PImage img,
if (destW <= 0 || destH <= 0 ||
srcW <= 0 || srcH <= 0 ||
destX1 >= screenW || destY1 >= screenH ||
- srcX1 >= img.width || srcY1 >= img.height) {
+ srcX1 >= img.pixelWidth || srcY1 >= img.pixelHeight) {
return;
}
int dx = (int) (srcW / (float) destW * PRECISIONF);
int dy = (int) (srcH / (float) destH * PRECISIONF);
- srcXOffset = (int) (destX1 < 0 ? -destX1 * dx : srcX1 * PRECISIONF);
- srcYOffset = (int) (destY1 < 0 ? -destY1 * dy : srcY1 * PRECISIONF);
+ srcXOffset = destX1 < 0 ? -destX1 * dx : srcX1 * PRECISIONF;
+ srcYOffset = destY1 < 0 ? -destY1 * dy : srcY1 * PRECISIONF;
if (destX1 < 0) {
destW += destX1;
@@ -1510,17 +1544,17 @@ private void blit_resize(PImage img,
destY1 = 0;
}
- destW = low(destW, screenW - destX1);
- destH = low(destH, screenH - destY1);
+ destW = min(destW, screenW - destX1);
+ destH = min(destH, screenH - destY1);
int destOffset = destY1 * screenW + destX1;
srcBuffer = img.pixels;
if (smooth) {
// use bilinear filtering
- iw = img.width;
- iw1 = img.width - 1;
- ih1 = img.height - 1;
+ iw = img.pixelWidth;
+ iw1 = img.pixelWidth - 1;
+ ih1 = img.pixelHeight - 1;
switch (mode) {
@@ -1729,12 +1763,12 @@ private void blit_resize(PImage img,
case BLEND:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
// davbol - renamed old blend_multiply to blend_blend
destPixels[destOffset + x] =
blend_blend(destPixels[destOffset + x],
- srcBuffer[sY + (sX >> PRECISIONB)]);
+ srcBuffer[sY + (sX >> PRECISIONB)]);
sX += dx;
}
destOffset += screenW;
@@ -1745,7 +1779,7 @@ private void blit_resize(PImage img,
case ADD:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_add_pin(destPixels[destOffset + x],
@@ -1760,7 +1794,7 @@ private void blit_resize(PImage img,
case SUBTRACT:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_sub_pin(destPixels[destOffset + x],
@@ -1775,7 +1809,7 @@ private void blit_resize(PImage img,
case LIGHTEST:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_lightest(destPixels[destOffset + x],
@@ -1790,7 +1824,7 @@ private void blit_resize(PImage img,
case DARKEST:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_darkest(destPixels[destOffset + x],
@@ -1805,7 +1839,7 @@ private void blit_resize(PImage img,
case REPLACE:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] = srcBuffer[sY + (sX >> PRECISIONB)];
sX += dx;
@@ -1818,7 +1852,7 @@ private void blit_resize(PImage img,
case DIFFERENCE:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_difference(destPixels[destOffset + x],
@@ -1833,7 +1867,7 @@ private void blit_resize(PImage img,
case EXCLUSION:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_exclusion(destPixels[destOffset + x],
@@ -1848,7 +1882,7 @@ private void blit_resize(PImage img,
case MULTIPLY:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_multiply(destPixels[destOffset + x],
@@ -1863,7 +1897,7 @@ private void blit_resize(PImage img,
case SCREEN:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_screen(destPixels[destOffset + x],
@@ -1878,7 +1912,7 @@ private void blit_resize(PImage img,
case OVERLAY:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_overlay(destPixels[destOffset + x],
@@ -1893,7 +1927,7 @@ private void blit_resize(PImage img,
case HARD_LIGHT:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_hard_light(destPixels[destOffset + x],
@@ -1908,7 +1942,7 @@ private void blit_resize(PImage img,
case SOFT_LIGHT:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_soft_light(destPixels[destOffset + x],
@@ -1924,7 +1958,7 @@ private void blit_resize(PImage img,
case DODGE:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_dodge(destPixels[destOffset + x],
@@ -1939,7 +1973,7 @@ private void blit_resize(PImage img,
case BURN:
for (int y = 0; y < destH; y++) {
sX = srcXOffset;
- sY = (srcYOffset >> PRECISIONB) * img.width;
+ sY = (srcYOffset >> PRECISIONB) * img.pixelWidth;
for (int x = 0; x < destW; x++) {
destPixels[destOffset + x] =
blend_burn(destPixels[destOffset + x],
@@ -1959,21 +1993,21 @@ private void blit_resize(PImage img,
private void filter_new_scanline() {
sX = srcXOffset;
fracV = srcYOffset & PREC_MAXVAL;
- ifV = PREC_MAXVAL - fracV;
+ ifV = PREC_MAXVAL - fracV + 1;
v1 = (srcYOffset >> PRECISIONB) * iw;
- v2 = low((srcYOffset >> PRECISIONB) + 1, ih1) * iw;
+ v2 = min((srcYOffset >> PRECISIONB) + 1, ih1) * iw;
}
private int filter_bilinear() {
fracU = sX & PREC_MAXVAL;
- ifU = PREC_MAXVAL - fracU;
+ ifU = PREC_MAXVAL - fracU + 1;
ul = (ifU * ifV) >> PRECISIONB;
- ll = (ifU * fracV) >> PRECISIONB;
- ur = (fracU * ifV) >> PRECISIONB;
- lr = (fracU * fracV) >> PRECISIONB;
+ ll = ifU - ul;
+ ur = ifV - ul;
+ lr = PREC_MAXVAL + 1 - ul - ll - ur;
u1 = (sX >> PRECISIONB);
- u2 = low(u1 + 1, iw1);
+ u2 = min(u1 + 1, iw1);
// get color values of the 4 neighbouring texels
cUL = srcBuffer[v1 + u1];
@@ -2007,333 +2041,479 @@ private int filter_bilinear() {
// internal blending methods
- private static int low(int a, int b) {
+ private static int min(int a, int b) {
return (a < b) ? a : b;
}
- private static int high(int a, int b) {
+ private static int max(int a, int b) {
return (a > b) ? a : b;
}
- // davbol - added peg helper, equiv to constrain(n,0,255)
- private static int peg(int n) {
- return (n < 0) ? 0 : ((n > 255) ? 255 : n);
- }
-
- private static int mix(int a, int b, int f) {
- return a + (((b - a) * f) >> 8);
- }
+ /////////////////////////////////////////////////////////////
+ // BLEND MODE IMPLEMENTATIONS
- /////////////////////////////////////////////////////////////
+ /*
+ * Jakub Valtar
+ *
+ * All modes use SRC alpha to interpolate between DST and the result of
+ * the operation:
+ *
+ * R = (1 - SRC_ALPHA) * DST + SRC_ALPHA *
+ *
+ * Comments above each mode only specify the formula of its operation.
+ *
+ * These implementations treat alpha 127 (=255/2) as a perfect 50 % mix.
+ *
+ * One alpha value between 126 and 127 is intentionally left out,
+ * so the step 126 -> 127 is twice as big compared to other steps.
+ * This is because our colors are in 0..255 range, but we divide
+ * by right shifting 8 places (=256) which is much faster than
+ * (correct) float division by 255.0f. The missing value was placed
+ * between 126 and 127, because limits of the range (near 0 and 255) and
+ * the middle value (127) have to blend correctly.
+ *
+ * Below you will often see RED and BLUE channels (RB) manipulated together
+ * and GREEN channel (GN) manipulated separately. It is sometimes possible
+ * because the operation won't use more than 16 bits, so we process the RED
+ * channel in the upper 16 bits and BLUE channel in the lower 16 bits. This
+ * decreases the number of operations per pixel and thus makes things faster.
+ *
+ * Some of the modes are hand tweaked (various +1s etc.) to be more accurate
+ * and to produce correct values in extremes. Below is a sketch you can use
+ * to check any blending function for
+ *
+ * 1) Discrepancies between color channels:
+ * - highlighted by the offending color
+ * 2) Behavior at extremes (set colorCount to 256):
+ * - values of all corners are printed to the console
+ * 3) Rounding errors:
+ * - set colorCount to lower value to better see color bands
+ *
- // BLEND MODE IMPLEMENTIONS
+// use powers of 2 in range 2..256
+// to better see color bands
+final int colorCount = 256;
+final int blockSize = 3;
- private static int blend_blend(int a, int b) {
- int f = (b & ALPHA_MASK) >>> 24;
+void settings() {
+ size(blockSize * 256, blockSize * 256);
+}
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- mix(a & RED_MASK, b & RED_MASK, f) & RED_MASK |
- mix(a & GREEN_MASK, b & GREEN_MASK, f) & GREEN_MASK |
- mix(a & BLUE_MASK, b & BLUE_MASK, f));
+void setup() { }
+
+void draw() {
+ noStroke();
+ colorMode(RGB, colorCount-1);
+ int alpha = (mouseX / blockSize) << 24;
+ int r, g, b, r2, g2, b2 = 0;
+ for (int x = 0; x <= 0xFF; x++) {
+ for (int y = 0; y <= 0xFF; y++) {
+ int dst = (x << 16) | (x << 8) | x;
+ int src = (y << 16) | (y << 8) | y | alpha;
+ int result = testFunction(dst, src);
+ r = r2 = (result >> 16 & 0xFF);
+ g = g2 = (result >> 8 & 0xFF);
+ b = b2 = (result >> 0 & 0xFF);
+ if (r != g && r != b) r2 = (128 + r2) % 255;
+ if (g != r && g != b) g2 = (128 + g2) % 255;
+ if (b != r && b != g) b2 = (128 + b2) % 255;
+ fill(r2 % colorCount, g2 % colorCount, b2 % colorCount);
+ rect(x * blockSize, y * blockSize, blockSize, blockSize);
+ }
}
+ println(
+ "alpha:", mouseX/blockSize,
+ "TL:", hex(get(0, 0)),
+ "TR:", hex(get(width-1, 0)),
+ "BR:", hex(get(width-1, height-1)),
+ "BL:", hex(get(0, height-1)));
+}
+
+int testFunction(int dst, int src) {
+ // your function here
+ return dst;
+}
+
+ *
+ *
+ */
+ private static final int RB_MASK = 0x00FF00FF;
+ private static final int GN_MASK = 0x0000FF00;
/**
- * additive blend with clipping
+ * Blend
+ * O = S
*/
- private static int blend_add_pin(int a, int b) {
- int f = (b & ALPHA_MASK) >>> 24;
+ private static int blend_blend(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- low(((a & RED_MASK) +
- ((b & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
- low(((a & GREEN_MASK) +
- ((b & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
- low((a & BLUE_MASK) +
- (((b & BLUE_MASK) * f) >> 8), BLUE_MASK));
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + (src & RB_MASK) * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + (src & GN_MASK) * s_a) >>> 8 & GN_MASK;
}
/**
- * subtractive blend with clipping
+ * Add
+ * O = MIN(D + S, 1)
*/
- private static int blend_sub_pin(int a, int b) {
- int f = (b & ALPHA_MASK) >>> 24;
+ private static int blend_add_pin(int dst, int src) {
+ int a = src >>> 24;
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- high(((a & RED_MASK) - ((b & RED_MASK) >> 8) * f),
- GREEN_MASK) & RED_MASK |
- high(((a & GREEN_MASK) - ((b & GREEN_MASK) >> 8) * f),
- BLUE_MASK) & GREEN_MASK |
- high((a & BLUE_MASK) - (((b & BLUE_MASK) * f) >> 8), 0));
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+
+ int rb = (dst & RB_MASK) + ((src & RB_MASK) * s_a >>> 8 & RB_MASK);
+ int gn = (dst & GN_MASK) + ((src & GN_MASK) * s_a >>> 8);
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ min(rb & 0xFFFF0000, RED_MASK) |
+ min(gn & 0x00FFFF00, GREEN_MASK) |
+ min(rb & 0x0000FFFF, BLUE_MASK);
}
/**
- * only returns the blended lightest colour
+ * Subtract
+ * O = MAX(0, D - S)
*/
- private static int blend_lightest(int a, int b) {
- int f = (b & ALPHA_MASK) >>> 24;
+ private static int blend_sub_pin(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- high(a & RED_MASK, ((b & RED_MASK) >> 8) * f) & RED_MASK |
- high(a & GREEN_MASK, ((b & GREEN_MASK) >> 8) * f) & GREEN_MASK |
- high(a & BLUE_MASK, ((b & BLUE_MASK) * f) >> 8));
+ int rb = ((src & RB_MASK) * s_a >>> 8);
+ int gn = ((src & GREEN_MASK) * s_a >>> 8);
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ max((dst & RED_MASK) - (rb & RED_MASK), 0) |
+ max((dst & GREEN_MASK) - (gn & GREEN_MASK), 0) |
+ max((dst & BLUE_MASK) - (rb & BLUE_MASK), 0);
}
/**
- * only returns the blended darkest colour
+ * Lightest
+ * O = MAX(D, S)
*/
- private static int blend_darkest(int a, int b) {
- int f = (b & ALPHA_MASK) >>> 24;
+ private static int blend_lightest(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- mix(a & RED_MASK,
- low(a & RED_MASK,
- ((b & RED_MASK) >> 8) * f), f) & RED_MASK |
- mix(a & GREEN_MASK,
- low(a & GREEN_MASK,
- ((b & GREEN_MASK) >> 8) * f), f) & GREEN_MASK |
- mix(a & BLUE_MASK,
- low(a & BLUE_MASK,
- ((b & BLUE_MASK) * f) >> 8), f));
+ int rb = max(src & RED_MASK, dst & RED_MASK) |
+ max(src & BLUE_MASK, dst & BLUE_MASK);
+ int gn = max(src & GREEN_MASK, dst & GREEN_MASK);
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
- * returns the absolute value of the difference of the input colors
- * C = |A - B|
+ * Darkest
+ * O = MIN(D, S)
*/
- private static int blend_difference(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = (ar > br) ? (ar-br) : (br-ar);
- int cg = (ag > bg) ? (ag-bg) : (bg-ag);
- int cb = (ab > bb) ? (ab-bb) : (bb-ab);
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_darkest(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int rb = min(src & RED_MASK, dst & RED_MASK) |
+ min(src & BLUE_MASK, dst & BLUE_MASK);
+ int gn = min(src & GREEN_MASK, dst & GREEN_MASK);
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
- * Cousin of difference, algorithm used here is based on a Lingo version
- * found here: http://www.mediamacros.com/item/item-1006687616/
- * (Not yet verified to be correct).
+ * Difference
+ * O = ABS(D - S)
*/
- private static int blend_exclusion(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = ar + br - ((ar * br) >> 7);
- int cg = ag + bg - ((ag * bg) >> 7);
- int cb = ab + bb - ((ab * bb) >> 7);
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_difference(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int r = (dst & RED_MASK) - (src & RED_MASK);
+ int b = (dst & BLUE_MASK) - (src & BLUE_MASK);
+ int g = (dst & GREEN_MASK) - (src & GREEN_MASK);
+
+ int rb = (r < 0 ? -r : r) |
+ (b < 0 ? -b : b);
+ int gn = (g < 0 ? -g : g);
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
- * returns the product of the input colors
- * C = A * B
+ * Exclusion
+ * O = (1 - S)D + S(1 - D)
+ * O = D + S - 2DS
+ */
+ private static int blend_exclusion(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int d_rb = dst & RB_MASK;
+ int d_gn = dst & GN_MASK;
+
+ int s_gn = src & GN_MASK;
+
+ int f_r = (dst & RED_MASK) >> 16;
+ int f_b = (dst & BLUE_MASK);
+
+ int rb_sub =
+ ((src & RED_MASK) * (f_r + (f_r >= 0x7F ? 1 : 0)) |
+ (src & BLUE_MASK) * (f_b + (f_b >= 0x7F ? 1 : 0)))
+ >>> 7 & 0x01FF01FF;
+ int gn_sub = s_gn * (d_gn + (d_gn >= 0x7F00 ? 0x100 : 0))
+ >>> 15 & 0x0001FF00;
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ (d_rb * d_a + (d_rb + (src & RB_MASK) - rb_sub) * s_a) >>> 8 & RB_MASK |
+ (d_gn * d_a + (d_gn + s_gn - gn_sub) * s_a) >>> 8 & GN_MASK;
+ }
+
+
+ /*
+ * Multiply
+ * O = DS
*/
- private static int blend_multiply(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = (ar * br) >> 8;
- int cg = (ag * bg) >> 8;
- int cb = (ab * bb) >> 8;
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_multiply(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int d_gn = dst & GN_MASK;
+
+ int f_r = (dst & RED_MASK) >> 16;
+ int f_b = (dst & BLUE_MASK);
+
+ int rb =
+ ((src & RED_MASK) * (f_r + 1) |
+ (src & BLUE_MASK) * (f_b + 1))
+ >>> 8 & RB_MASK;
+ int gn =
+ (src & GREEN_MASK) * (d_gn + 0x100)
+ >>> 16 & GN_MASK;
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
+ (d_gn * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
- * returns the inverse of the product of the inverses of the input colors
- * (the inverse of multiply). C = 1 - (1-A) * (1-B)
+ * Screen
+ * O = 1 - (1 - D)(1 - S)
+ * O = D + S - DS
*/
- private static int blend_screen(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = 255 - (((255 - ar) * (255 - br)) >> 8);
- int cg = 255 - (((255 - ag) * (255 - bg)) >> 8);
- int cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_screen(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int d_rb = dst & RB_MASK;
+ int d_gn = dst & GN_MASK;
+
+ int s_gn = src & GN_MASK;
+
+ int f_r = (dst & RED_MASK) >> 16;
+ int f_b = (dst & BLUE_MASK);
+
+ int rb_sub =
+ ((src & RED_MASK) * (f_r + 1) |
+ (src & BLUE_MASK) * (f_b + 1))
+ >>> 8 & RB_MASK;
+ int gn_sub = s_gn * (d_gn + 0x100)
+ >>> 16 & GN_MASK;
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ (d_rb * d_a + (d_rb + (src & RB_MASK) - rb_sub) * s_a) >>> 8 & RB_MASK |
+ (d_gn * d_a + (d_gn + s_gn - gn_sub) * s_a) >>> 8 & GN_MASK;
}
/**
- * returns either multiply or screen for darker or lighter values of A
- * (the inverse of hard light)
- * C =
- * A < 0.5 : 2 * A * B
- * A >=0.5 : 1 - (2 * (255-A) * (255-B))
+ * Overlay
+ * O = 2 * MULTIPLY(D, S) = 2DS for D < 0.5
+ * O = 2 * SCREEN(D, S) - 1 = 2(S + D - DS) - 1 otherwise
*/
- private static int blend_overlay(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = (ar < 128) ? ((ar*br)>>7) : (255-(((255-ar)*(255-br))>>7));
- int cg = (ag < 128) ? ((ag*bg)>>7) : (255-(((255-ag)*(255-bg))>>7));
- int cb = (ab < 128) ? ((ab*bb)>>7) : (255-(((255-ab)*(255-bb))>>7));
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_overlay(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int d_r = dst & RED_MASK;
+ int d_g = dst & GREEN_MASK;
+ int d_b = dst & BLUE_MASK;
+
+ int s_r = src & RED_MASK;
+ int s_g = src & GREEN_MASK;
+ int s_b = src & BLUE_MASK;
+
+ int r = (d_r < 0x800000) ?
+ d_r * ((s_r >>> 16) + 1) >>> 7 :
+ 0xFF0000 - ((0x100 - (s_r >>> 16)) * (RED_MASK - d_r) >>> 7);
+ int g = (d_g < 0x8000) ?
+ d_g * (s_g + 0x100) >>> 15 :
+ (0xFF00 - ((0x10000 - s_g) * (GREEN_MASK - d_g) >>> 15));
+ int b = (d_b < 0x80) ?
+ d_b * (s_b + 1) >>> 7 :
+ (0xFF00 - ((0x100 - s_b) * (BLUE_MASK - d_b) << 1)) >>> 8;
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + ((r | b) & RB_MASK) * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + (g & GN_MASK) * s_a) >>> 8 & GN_MASK;
}
/**
- * returns either multiply or screen for darker or lighter values of B
- * (the inverse of overlay)
- * C =
- * B < 0.5 : 2 * A * B
- * B >=0.5 : 1 - (2 * (255-A) * (255-B))
+ * Hard Light
+ * O = OVERLAY(S, D)
+ *
+ * O = 2 * MULTIPLY(D, S) = 2DS for S < 0.5
+ * O = 2 * SCREEN(D, S) - 1 = 2(S + D - DS) - 1 otherwise
*/
- private static int blend_hard_light(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = (br < 128) ? ((ar*br)>>7) : (255-(((255-ar)*(255-br))>>7));
- int cg = (bg < 128) ? ((ag*bg)>>7) : (255-(((255-ag)*(255-bg))>>7));
- int cb = (bb < 128) ? ((ab*bb)>>7) : (255-(((255-ab)*(255-bb))>>7));
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_hard_light(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int d_r = dst & RED_MASK;
+ int d_g = dst & GREEN_MASK;
+ int d_b = dst & BLUE_MASK;
+
+ int s_r = src & RED_MASK;
+ int s_g = src & GREEN_MASK;
+ int s_b = src & BLUE_MASK;
+
+ int r = (s_r < 0x800000) ?
+ s_r * ((d_r >>> 16) + 1) >>> 7 :
+ 0xFF0000 - ((0x100 - (d_r >>> 16)) * (RED_MASK - s_r) >>> 7);
+ int g = (s_g < 0x8000) ?
+ s_g * (d_g + 0x100) >>> 15 :
+ (0xFF00 - ((0x10000 - d_g) * (GREEN_MASK - s_g) >>> 15));
+ int b = (s_b < 0x80) ?
+ s_b * (d_b + 1) >>> 7 :
+ (0xFF00 - ((0x100 - d_b) * (BLUE_MASK - s_b) << 1)) >>> 8;
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + ((r | b) & RB_MASK) * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + (g & GN_MASK) * s_a) >>> 8 & GN_MASK;
}
/**
- * returns the inverse multiply plus screen, which simplifies to
- * C = 2AB + A^2 - 2A^2B
+ * Soft Light (Pegtop)
+ * O = (1 - D) * MULTIPLY(D, S) + D * SCREEN(D, S)
+ * O = (1 - D) * DS + D * (1 - (1 - D)(1 - S))
+ * O = 2DS + DD - 2DDS
*/
- private static int blend_soft_light(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = ((ar*br)>>7) + ((ar*ar)>>8) - ((ar*ar*br)>>15);
- int cg = ((ag*bg)>>7) + ((ag*ag)>>8) - ((ag*ag*bg)>>15);
- int cb = ((ab*bb)>>7) + ((ab*ab)>>8) - ((ab*ab*bb)>>15);
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_soft_light(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int d_r = dst & RED_MASK;
+ int d_g = dst & GREEN_MASK;
+ int d_b = dst & BLUE_MASK;
+
+ int s_r1 = src & RED_MASK >> 16;
+ int s_g1 = src & GREEN_MASK >> 8;
+ int s_b1 = src & BLUE_MASK;
+
+ int d_r1 = (d_r >> 16) + (s_r1 < 7F ? 1 : 0);
+ int d_g1 = (d_g >> 8) + (s_g1 < 7F ? 1 : 0);
+ int d_b1 = d_b + (s_b1 < 7F ? 1 : 0);
+
+ int r = (s_r1 * d_r >> 7) + 0xFF * d_r1 * (d_r1 + 1) -
+ ((s_r1 * d_r1 * d_r1) << 1) & RED_MASK;
+ int g = (s_g1 * d_g << 1) + 0xFF * d_g1 * (d_g1 + 1) -
+ ((s_g1 * d_g1 * d_g1) << 1) >>> 8 & GREEN_MASK;
+ int b = (s_b1 * d_b << 9) + 0xFF * d_b1 * (d_b1 + 1) -
+ ((s_b1 * d_b1 * d_b1) << 1) >>> 16;
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + (r | b) * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + g * s_a) >>> 8 & GN_MASK;
}
/**
- * Returns the first (underlay) color divided by the inverse of
- * the second (overlay) color. C = A / (255-B)
+ * Dodge
+ * O = D / (1 - S)
*/
- private static int blend_dodge(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = (br==255) ? 255 : peg((ar << 8) / (255 - br)); // division requires pre-peg()-ing
- int cg = (bg==255) ? 255 : peg((ag << 8) / (255 - bg)); // "
- int cb = (bb==255) ? 255 : peg((ab << 8) / (255 - bb)); // "
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_dodge(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int r = (dst & RED_MASK) / (256 - ((src & RED_MASK) >> 16));
+ int g = ((dst & GREEN_MASK) << 8) / (256 - ((src & GREEN_MASK) >> 8));
+ int b = ((dst & BLUE_MASK) << 8) / (256 - (src & BLUE_MASK));
+
+ int rb =
+ (r > 0xFF00 ? 0xFF0000 : ((r << 8) & RED_MASK)) |
+ (b > 0x00FF ? 0x0000FF : b);
+ int gn =
+ (g > 0xFF00 ? 0x00FF00 : (g & GREEN_MASK));
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
/**
- * returns the inverse of the inverse of the first (underlay) color
- * divided by the second (overlay) color. C = 255 - (255-A) / B
+ * Burn
+ * O = 1 - (1 - A) / B
*/
- private static int blend_burn(int a, int b) {
- // setup (this portion will always be the same)
- int f = (b & ALPHA_MASK) >>> 24;
- int ar = (a & RED_MASK) >> 16;
- int ag = (a & GREEN_MASK) >> 8;
- int ab = (a & BLUE_MASK);
- int br = (b & RED_MASK) >> 16;
- int bg = (b & GREEN_MASK) >> 8;
- int bb = (b & BLUE_MASK);
- // formula:
- int cr = (br==0) ? 0 : 255 - peg(((255 - ar) << 8) / br); // division requires pre-peg()-ing
- int cg = (bg==0) ? 0 : 255 - peg(((255 - ag) << 8) / bg); // "
- int cb = (bb==0) ? 0 : 255 - peg(((255 - ab) << 8) / bb); // "
- // alpha blend (this portion will always be the same)
- return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
- (peg(ar + (((cr - ar) * f) >> 8)) << 16) |
- (peg(ag + (((cg - ag) * f) >> 8)) << 8) |
- (peg(ab + (((cb - ab) * f) >> 8)) ) );
+ private static int blend_burn(int dst, int src) {
+ int a = src >>> 24;
+
+ int s_a = a + (a >= 0x7F ? 1 : 0);
+ int d_a = 0x100 - s_a;
+
+ int r = ((0xFF0000 - (dst & RED_MASK))) / (1 + (src & RED_MASK >> 16));
+ int g = ((0x00FF00 - (dst & GREEN_MASK)) << 8) / (1 + (src & GREEN_MASK >> 8));
+ int b = ((0x0000FF - (dst & BLUE_MASK)) << 8) / (1 + (src & BLUE_MASK));
+
+ int rb = RB_MASK -
+ (r > 0xFF00 ? 0xFF0000 : ((r << 8) & RED_MASK)) -
+ (b > 0x00FF ? 0x0000FF : b);
+ int gn = GN_MASK -
+ (g > 0xFF00 ? 0x00FF00 : (g & GREEN_MASK));
+
+ return min((dst >>> 24) + a, 0xFF) << 24 |
+ ((dst & RB_MASK) * d_a + rb * s_a) >>> 8 & RB_MASK |
+ ((dst & GN_MASK) * d_a + gn * s_a) >>> 8 & GN_MASK;
}
diff --git a/core/src/processing/core/PMatrix.java b/libs/processing-core/src/main/java/processing/core/PMatrix.java
similarity index 71%
rename from core/src/processing/core/PMatrix.java
rename to libs/processing-core/src/main/java/processing/core/PMatrix.java
index 283039629..1c6fabc3f 100644
--- a/core/src/processing/core/PMatrix.java
+++ b/libs/processing-core/src/main/java/processing/core/PMatrix.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2005-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2005-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -24,26 +25,26 @@
public interface PMatrix {
-
+
public void reset();
-
+
/**
* Returns a copy of this PMatrix.
*/
- public PMatrix get();
+ public PMatrix get();
/**
* Copies the matrix contents into a float array.
* If target is null (or not the correct size), a new array will be created.
*/
public float[] get(float[] target);
-
-
+
+
public void set(PMatrix src);
public void set(float[] source);
- public void set(float m00, float m01, float m02,
+ public void set(float m00, float m01, float m02,
float m10, float m11, float m12);
public void set(float m00, float m01, float m02, float m03,
@@ -51,9 +52,9 @@ public void set(float m00, float m01, float m02, float m03,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33);
-
+
public void translate(float tx, float ty);
-
+
public void translate(float tx, float ty, float tz);
public void rotate(float angle);
@@ -71,12 +72,12 @@ public void set(float m00, float m01, float m02, float m03,
public void scale(float sx, float sy);
public void scale(float x, float y, float z);
-
+
public void shearX(float angle);
-
+
public void shearY(float angle);
- /**
+ /**
* Multiply this matrix by another.
*/
public void apply(PMatrix source);
@@ -85,7 +86,7 @@ public void set(float m00, float m01, float m02, float m03,
public void apply(PMatrix3D source);
- public void apply(float n00, float n01, float n02,
+ public void apply(float n00, float n01, float n02,
float n10, float n11, float n12);
public void apply(float n00, float n01, float n02, float n03,
@@ -93,55 +94,75 @@ public void apply(float n00, float n01, float n02, float n03,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33);
+ /**
+ * Apply another matrix to the left of this one.
+ */
+ public void preApply(PMatrix left);
+
/**
* Apply another matrix to the left of this one.
*/
public void preApply(PMatrix2D left);
+ /**
+ * Apply another matrix to the left of this one. 3D only.
+ */
public void preApply(PMatrix3D left);
- public void preApply(float n00, float n01, float n02,
+ /**
+ * Apply another matrix to the left of this one.
+ */
+ public void preApply(float n00, float n01, float n02,
float n10, float n11, float n12);
+ /**
+ * Apply another matrix to the left of this one. 3D only.
+ */
public void preApply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33);
-
- /**
- * Multiply a PVector by this matrix.
+
+ /**
+ * Multiply source by this matrix, and return the result.
+ * The result will be stored in target if target is non-null, and target
+ * will then be the matrix returned. This improves performance if you reuse
+ * target, so it's recommended if you call this many times in draw().
*/
public PVector mult(PVector source, PVector target);
-
-
- /**
- * Multiply a multi-element vector against this matrix.
+
+
+ /**
+ * Multiply a multi-element vector against this matrix.
+ * Supplying and recycling a target array improves performance, so it's
+ * recommended if you call this many times in draw().
*/
public float[] mult(float[] source, float[] target);
-
-
+
+
// public float multX(float x, float y);
// public float multY(float x, float y);
-
+
// public float multX(float x, float y, float z);
// public float multY(float x, float y, float z);
-// public float multZ(float x, float y, float z);
-
-
+// public float multZ(float x, float y, float z);
+
+
/**
- * Transpose this matrix.
+ * Transpose this matrix; rows become columns and columns rows.
*/
public void transpose();
-
+
/**
- * Invert this matrix.
+ * Invert this matrix. Will not necessarily succeed, because some matrices
+ * map more than one point to the same image point, and so are irreversible.
* @return true if successful
*/
public boolean invert();
-
-
+
+
/**
* @return the determinant of the matrix
*/
diff --git a/core/src/processing/core/PMatrix2D.java b/libs/processing-core/src/main/java/processing/core/PMatrix2D.java
similarity index 96%
rename from core/src/processing/core/PMatrix2D.java
rename to libs/processing-core/src/main/java/processing/core/PMatrix2D.java
index 585a9cf84..8d13979b7 100644
--- a/core/src/processing/core/PMatrix2D.java
+++ b/libs/processing-core/src/main/java/processing/core/PMatrix2D.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2005-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2005-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -243,6 +244,18 @@ public void apply(float n00, float n01, float n02, float n03,
}
+ /**
+ * Apply another matrix to the left of this one.
+ */
+ public void preApply(PMatrix source) {
+ if (source instanceof PMatrix2D) {
+ preApply((PMatrix2D) source);
+ } else if (source instanceof PMatrix3D) {
+ preApply((PMatrix3D) source);
+ }
+ }
+
+
/**
* Apply another matrix to the left of this one.
*/
@@ -410,11 +423,11 @@ public void print() {
//////////////////////////////////////////////////////////////
- // TODO these need to be added as regular API, but the naming and
+ // TODO these need to be added as regular API, but the naming and
// implementation needs to be improved first. (e.g. actually keeping track
// of whether the matrix is in fact identity internally.)
-
+
protected boolean isIdentity() {
return ((m00 == 1) && (m01 == 0) && (m02 == 0) &&
(m10 == 0) && (m11 == 1) && (m12 == 0));
@@ -424,14 +437,14 @@ protected boolean isIdentity() {
// TODO make this more efficient, or move into PMatrix2D
protected boolean isWarped() {
// was &&, but changed so shearX and shearY will work
- return ((m00 != 1) || (m01 != 0) ||
+ return ((m00 != 1) || (m01 != 0) ||
(m10 != 0) || (m11 != 1));
}
//////////////////////////////////////////////////////////////
-
+
private final float max(float a, float b) {
return (a > b) ? a : b;
}
@@ -447,7 +460,7 @@ private final float sin(float angle) {
private final float cos(float angle) {
return (float)Math.cos(angle);
}
-
+
private final float tan(float angle) {
return (float)Math.tan(angle);
}
diff --git a/core/src/processing/core/PMatrix3D.java b/libs/processing-core/src/main/java/processing/core/PMatrix3D.java
similarity index 98%
rename from core/src/processing/core/PMatrix3D.java
rename to libs/processing-core/src/main/java/processing/core/PMatrix3D.java
index deeb23b45..9167af1f0 100644
--- a/core/src/processing/core/PMatrix3D.java
+++ b/libs/processing-core/src/main/java/processing/core/PMatrix3D.java
@@ -3,6 +3,7 @@
/*
Part of the Processing project - http://processing.org
+ Copyright (c) 2012-21 The Processing Foundation
Copyright (c) 2005-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
@@ -358,6 +359,18 @@ public void apply(float n00, float n01, float n02, float n03,
}
+ /**
+ * Apply another matrix to the left of this one.
+ */
+ public void preApply(PMatrix source) {
+ if (source instanceof PMatrix2D) {
+ preApply((PMatrix2D) source);
+ } else if (source instanceof PMatrix3D) {
+ preApply((PMatrix3D) source);
+ }
+ }
+
+
public void preApply(PMatrix2D left) {
preApply(left.m00, left.m01, 0, left.m02,
left.m10, left.m11, 0, left.m12,
diff --git a/core/src/processing/core/PShape.java b/libs/processing-core/src/main/java/processing/core/PShape.java
similarity index 80%
rename from core/src/processing/core/PShape.java
rename to libs/processing-core/src/main/java/processing/core/PShape.java
index 85e626e9c..a609a9e4e 100644
--- a/core/src/processing/core/PShape.java
+++ b/libs/processing-core/src/main/java/processing/core/PShape.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2006-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2006-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -22,9 +23,11 @@
package processing.core;
-import java.util.HashMap;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
-import processing.core.PApplet;
+import java.util.HashMap;
+import java.util.Map;
/**
@@ -78,17 +81,21 @@
*/
public class PShape implements PConstants {
protected String name;
- protected HashMap nameTable;
+ protected Map nameTable;
// /** Generic, only draws its child objects. */
// static public final int GROUP = 0;
- // GROUP now inherited from PConstants
+ // GROUP now inherited from PConstants, and is still zero
+
+ // These constants were updated in 3.0b6 so that they could be distinguished
+ // from others in PConstants and improve how some typos were handled.
+ // https://github.com/processing/processing/issues/3776
/** A line, ellipse, arc, image, etc. */
- static public final int PRIMITIVE = 1;
+ static public final int PRIMITIVE = 101;
/** A series of vertex, curveVertex, and bezierVertex calls. */
- static public final int PATH = 2;
+ static public final int PATH = 102;
/** Collections of vertices created with beginShape(). */
- static public final int GEOMETRY = 3;
+ static public final int GEOMETRY = 103;
/** The shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY. */
protected int family;
@@ -101,6 +108,7 @@ public class PShape implements PConstants {
/** Texture or image data associated with this shape. */
protected PImage image;
+ protected String imagePath = null;
public static final String OUTSIDE_BEGIN_END_ERROR =
"%1$s can only be called between beginShape() and endShape()";
@@ -108,11 +116,18 @@ public class PShape implements PConstants {
public static final String INSIDE_BEGIN_END_ERROR =
"%1$s can only be called outside beginShape() and endShape()";
- // boundary box of this shape
- //protected float x;
- //protected float y;
- //protected float width;
- //protected float height;
+ public static final String NO_SUCH_VERTEX_ERROR =
+ "%1$s vertex index does not exist";
+
+ static public final String NO_VERTICES_ERROR =
+ "getVertexCount() only works with PATH or GEOMETRY shapes";
+
+ public static final String NOT_A_SIMPLE_VERTEX =
+ "%1$s can not be called on quadratic or bezier vertices";
+
+ static public final String PER_VERTEX_UNSUPPORTED =
+ "This renderer does not support %1$s for individual vertices";
+
/**
* ( begin auto-generated from PShape_width.xml )
*
@@ -140,6 +155,8 @@ public class PShape implements PConstants {
public float depth;
+ PGraphics g;
+
// set to false if the object is hidden in the layers palette
protected boolean visible = true;
@@ -227,6 +244,9 @@ public class PShape implements PConstants {
/** True if contains 3D data */
protected boolean is3D = false;
+ protected boolean perVertexStyles = false;
+
+
// should this be called vertices (consistent with PGraphics internals)
// or does that hurt flexibility?
@@ -262,19 +282,98 @@ public class PShape implements PConstants {
// public float px;
// public float py;
+
+ /**
+ * @nowebref
+ */
public PShape() {
this.family = GROUP;
}
-/**
- * @nowebref
- */
+ /**
+ * @nowebref
+ */
public PShape(int family) {
this.family = family;
}
+ /**
+ * @nowebref
+ */
+ public PShape(PGraphics g, int family) {
+ this.g = g;
+ this.family = family;
+
+ // Style parameters are retrieved from the current values in the renderer.
+ textureMode = g.textureMode;
+
+ colorMode(g.colorMode,
+ g.colorModeX, g.colorModeY, g.colorModeZ, g.colorModeA);
+
+ // Initial values for fill, stroke and tint colors are also imported from
+ // the renderer. This is particular relevant for primitive shapes, since is
+ // not possible to set their color separately when creating them, and their
+ // input vertices are actually generated at rendering time, by which the
+ // color configuration of the renderer might have changed.
+ fill = g.fill;
+ fillColor = g.fillColor;
+
+ stroke = g.stroke;
+ strokeColor = g.strokeColor;
+ strokeWeight = g.strokeWeight;
+ strokeCap = g.strokeCap;
+ strokeJoin = g.strokeJoin;
+
+ tint = g.tint;
+ tintColor = g.tintColor;
+
+ setAmbient = g.setAmbient;
+ ambientColor = g.ambientColor;
+ specularColor = g.specularColor;
+ emissiveColor = g.emissiveColor;
+ shininess = g.shininess;
+
+ sphereDetailU = g.sphereDetailU;
+ sphereDetailV = g.sphereDetailV;
+
+// bezierDetail = pg.bezierDetail;
+// curveDetail = pg.curveDetail;
+// curveTightness = pg.curveTightness;
+
+ rectMode = g.rectMode;
+ ellipseMode = g.ellipseMode;
+
+// normalX = normalY = 0;
+// normalZ = 1;
+//
+// normalMode = NORMAL_MODE_AUTO;
+
+ // To make sure that the first vertex is marked as a break.
+ // Same behavior as in the immediate mode.
+// breakShape = false;
+
+ if (family == GROUP) {
+ // GROUP shapes are always marked as ended.
+// shapeCreated = true;
+ // TODO why was this commented out?
+ }
+ }
+
+
+ public PShape(PGraphics g, int kind, float... params) {
+ this(g, PRIMITIVE);
+ setKind(kind);
+ setParams(params);
+ }
+
+
+ public void setFamily(int family) {
+ this.family = family;
+ }
+
+
public void setKind(int kind) {
this.kind = kind;
}
@@ -467,7 +566,7 @@ public boolean is3D() {
}
- public void is3D(boolean val) {
+ public void set3D(boolean val) {
is3D = val;
}
@@ -514,10 +613,12 @@ public void noTexture() {
image = null;
}
+
// TODO unapproved
protected void solid(boolean solid) {
}
+
/**
* @webref shape:vertex
* @brief Starts a new contour
@@ -544,7 +645,9 @@ public void beginContour() {
protected void beginContourImpl() {
- if (vertexCodes.length == vertexCodeCount) {
+ if (vertexCodes == null) {
+ vertexCodes = new int[10];
+ } else if (vertexCodes.length == vertexCodeCount) {
vertexCodes = PApplet.expand(vertexCodes);
}
vertexCodes[vertexCodeCount++] = BREAK;
@@ -609,6 +712,7 @@ public void vertex(float x, float y, float u, float v) {
public void vertex(float x, float y, float z) {
+ vertex(x, y); // maybe? maybe not?
}
@@ -620,6 +724,34 @@ public void normal(float nx, float ny, float nz) {
}
+ public void attribPosition(String name, float x, float y, float z) {
+ }
+
+ public void attribNormal(String name, float nx, float ny, float nz) {
+ }
+
+
+ public void attribColor(String name, int color) {
+ }
+
+
+ public void attrib(String name, float... values) {
+ }
+
+
+ public void attrib(String name, int... values) {
+ }
+
+
+ public void attrib(String name, boolean... values) {
+ }
+
+
+ /**
+ * @webref pshape:method
+ * @brief Starts the creation of a new PShape
+ * @see PApplet#endShape()
+ */
public void beginShape() {
beginShape(POLYGON);
}
@@ -630,11 +762,10 @@ public void beginShape(int kind) {
openShape = true;
}
-
/**
* @webref pshape:method
* @brief Finishes the creation of a new PShape
- * @see PApplet#createShape()
+ * @see PApplet#beginShape()
*/
public void endShape() {
endShape(OPEN);
@@ -652,6 +783,9 @@ public void endShape(int mode) {
return;
}
+ close = (mode==CLOSE);
+
+ // this is the state of the shape
openShape = false;
}
@@ -767,6 +901,11 @@ public void fill(float gray, float alpha) {
colorCalc(gray, alpha);
fillColor = calcColor;
+ if (!setAmbient) {
+ ambient(fillColor);
+ setAmbient = false;
+ }
+
if (!setAmbient) {
ambientColor = fillColor;
}
@@ -1152,11 +1291,6 @@ public void bezierVertex(float x2, float y2, float z2,
public void quadraticVertex(float cx, float cy,
float x3, float y3) {
- }
-
-
- public void quadraticVertex(float cx, float cy, float cz,
- float x3, float y3, float z3) {
if (vertices == null) {
vertices = new float[10][];
} else if (vertexCount + 1 >= vertices.length) {
@@ -1180,29 +1314,31 @@ public void quadraticVertex(float cx, float cy, float cz,
}
+ public void quadraticVertex(float cx, float cy, float cz,
+ float x3, float y3, float z3) {
+ }
+
+
///////////////////////////////////////////////////////////
//
// Catmull-Rom curves
-
public void curveDetail(int detail) {
}
-
public void curveTightness(float tightness) {
}
-
public void curveVertex(float x, float y) {
}
-
public void curveVertex(float x, float y, float z) {
}
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -1358,12 +1494,10 @@ static protected void copyGeometry(PShape src, PShape dest) {
for (int i = 0; i < src.vertexCount; i++) {
float[] vert = src.vertices[i];
-
-
- dest.fill((int)(vert[PGraphics.R] * 255) << 24 |
- (int)(vert[PGraphics.G] * 255) << 16 |
- (int)(vert[PGraphics.B] * 255) << 8 |
- (int)(vert[PGraphics.A] * 255));
+ dest.fill((int)(vert[PGraphics.A] * 255) << 24 |
+ (int)(vert[PGraphics.R] * 255) << 16 |
+ (int)(vert[PGraphics.G] * 255) << 8 |
+ (int)(vert[PGraphics.B] * 255));
// Do we need to copy these as well?
// dest.ambient(vert[PGraphics.AR] * 255, vert[PGraphics.AG] * 255, vert[PGraphics.AB] * 255);
@@ -1417,6 +1551,9 @@ static protected void copyMatrix(PShape src, PShape dest) {
// TODO unapproved
static protected void copyStyles(PShape src, PShape dest) {
+ dest.ellipseMode = src.ellipseMode;
+ dest.rectMode = src.rectMode;
+
if (src.stroke) {
dest.stroke = true;
dest.strokeColor = src.strokeColor;
@@ -1465,13 +1602,14 @@ public void draw(PGraphics g) {
/**
* Draws the SVG document.
*/
- public void drawImpl(PGraphics g) {
- //System.out.println("drawing " + family);
+ protected void drawImpl(PGraphics g) {
if (family == GROUP) {
drawGroup(g);
} else if (family == PRIMITIVE) {
drawPrimitive(g);
} else if (family == GEOMETRY) {
+ // Not same as path: `kind` matters.
+// drawPath(g);
drawGeometry(g);
} else if (family == PATH) {
drawPath(g);
@@ -1511,21 +1649,54 @@ protected void drawPrimitive(PGraphics g) {
params[6], params[7]);
} else if (kind == RECT) {
+
+ if (imagePath != null){
+ loadImage(g);
+ }
if (image != null) {
+ int oldMode = g.imageMode;
g.imageMode(CORNER);
g.image(image, params[0], params[1], params[2], params[3]);
+ g.imageMode(oldMode);
} else {
- g.rectMode(CORNER);
- g.rect(params[0], params[1], params[2], params[3]);
+ int oldMode = g.rectMode;
+ g.rectMode(rectMode);
+ if (params.length == 4) {
+ g.rect(params[0], params[1],
+ params[2], params[3]);
+ } else if (params.length == 5) {
+ g.rect(params[0], params[1],
+ params[2], params[3],
+ params[4]);
+ } else if (params.length == 8) {
+ g.rect(params[0], params[1],
+ params[2], params[3],
+ params[4], params[5],
+ params[6], params[7]);
+ }
+ g.rectMode(oldMode);
}
-
} else if (kind == ELLIPSE) {
- g.ellipseMode(CORNER);
- g.ellipse(params[0], params[1], params[2], params[3]);
+ int oldMode = g.ellipseMode;
+ g.ellipseMode(ellipseMode);
+ g.ellipse(params[0], params[1],
+ params[2], params[3]);
+ g.ellipseMode(oldMode);
} else if (kind == ARC) {
- g.ellipseMode(CORNER);
- g.arc(params[0], params[1], params[2], params[3], params[4], params[5]);
+ int oldMode = g.ellipseMode;
+ g.ellipseMode(ellipseMode);
+ if (params.length == 6) {
+ g.arc(params[0], params[1],
+ params[2], params[3],
+ params[4], params[5]);
+ } else if (params.length == 7) {
+ g.arc(params[0], params[1],
+ params[2], params[3],
+ params[4], params[5],
+ (int) params[6]);
+ }
+ g.ellipseMode(oldMode);
} else if (kind == BOX) {
if (params.length == 1) {
@@ -1557,7 +1728,7 @@ protected void drawGeometry(PGraphics g) {
}
}
}
- g.endShape();
+ g.endShape(close ? CLOSE : OPEN);
}
@@ -1615,7 +1786,6 @@ protected void drawPath(PGraphics g) {
}
*/
-
protected void drawPath(PGraphics g) {
// Paths might be empty (go figure)
// http://dev.processing.org/bugs/show_bug.cgi?id=982
@@ -1718,6 +1888,90 @@ protected void drawPath(PGraphics g) {
}
+ private void loadImage(PGraphics g){
+ if (this.imagePath.startsWith("data:image")){
+ loadBase64Image();
+ }
+
+ if (this.imagePath.startsWith("file://")){
+ loadFileSystemImage(g);
+ }
+ this.imagePath = null;
+ }
+
+ private void loadFileSystemImage(PGraphics g){
+ imagePath = imagePath.substring(7);
+ PImage loadedImage = g.parent.loadImage(imagePath);
+ if (loadedImage == null){
+ System.err.println("Error loading image file: " + imagePath);
+ } else{
+ setTexture(loadedImage);
+ }
+ }
+
+ private void loadBase64Image(){
+ String[] parts = this.imagePath.split(";base64,");
+ String extension = parts[0].substring(11);
+ String encodedData = parts[1];
+
+// byte[] decodedBytes = DatatypeConverter.parseBase64Binary(encodedData);
+ byte[] decodedBytes = parseHexBinary(encodedData);
+
+ if(decodedBytes == null){
+ System.err.println("Decode Error on image: " + imagePath.substring(0, 20));
+ return;
+ }
+
+// Image awtImage = new ImageIcon(decodedBytes).getImage();
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inMutable = true;
+ Bitmap bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length, options);
+
+// if (awtImage instanceof BufferedImage) {
+// BufferedImage buffImage = (BufferedImage) awtImage;
+// int space = buffImage.getColorModel().getColorSpace().getType();
+// if (space == ColorSpace.TYPE_CMYK) {
+// return;
+// }
+// }
+
+ PImage loadedImage = new PImage(bmp);
+ if (loadedImage.width == -1) {
+ // error...
+ }
+
+ // if it's a .gif image, test to see if it has transparency
+ if (extension.equals("gif") || extension.equals("png") ||
+ extension.equals("unknown")) {
+ loadedImage.checkAlpha();
+ }
+
+ setTexture(loadedImage);
+ }
+
+
+ // Replacement for DatatypeConverter
+ // https://github.com/hierynomus/sshj/issues/366#issue-261511648
+ private static byte[] parseHexBinary(String s)
+ throws IllegalArgumentException {
+ if (s == null) {
+ return new byte[0];
+ }
+ s = s.trim();
+ int length = s.length();
+
+ if (length % 2 != 0) {
+ throw new IllegalArgumentException("Invalid hex string length.");
+ }
+
+ byte[] result = new byte[length / 2];
+ for (int i = 0; i < length; i += 2) {
+ result[i/2] = (byte) Integer.parseInt(s.substring(i, i + 2), 16);
+ }
+ return result;
+ }
+
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -1725,12 +1979,26 @@ public PShape getParent() {
return parent;
}
+ /**
+ * @webref
+ * @brief Returns the number of children
+ */
public int getChildCount() {
return childCount;
}
+ /** Resize the children[] array to be in line with childCount */
+ protected void crop() {
+ // https://github.com/processing/processing/issues/3347
+ if (children.length != childCount) {
+ children = (PShape[]) PApplet.subset(children, 0, childCount);
+ }
+ }
+
+
public PShape[] getChildren() {
+ crop();
return children;
}
@@ -1749,6 +2017,7 @@ public PShape[] getChildren() {
* @see PShape#addChild(PShape)
*/
public PShape getChild(int index) {
+ crop();
return children[index];
}
@@ -1863,7 +2132,7 @@ public void addName(String nom, PShape shape) {
parent.addName(nom, shape);
} else {
if (nameTable == null) {
- nameTable = new HashMap();
+ nameTable = new HashMap<>();
}
nameTable.put(nom, shape);
}
@@ -1963,6 +2232,9 @@ protected void setPath(int vcount, float[][] verts, int ccount, int[] codes) {
* @see PShape#setVertex(int, float, float)
*/
public int getVertexCount() {
+ if (family == GROUP || family == PRIMITIVE) {
+ PGraphics.showWarning(NO_VERTICES_ERROR);
+ }
return vertexCount;
}
@@ -1978,6 +2250,7 @@ public PVector getVertex(int index) {
return getVertex(index, null);
}
+
/**
* @param vec PVector to assign the data to
*/
@@ -2011,6 +2284,7 @@ public float getVertexZ(int index) {
return vertices[index][Z];
}
+
/**
* @webref pshape:method
* @brief Sets the vertex at the index position
@@ -2030,6 +2304,7 @@ public void setVertex(int index, float x, float y) {
vertices[index][Y] = y;
}
+
/**
* @param z the z value for the vertex
*/
@@ -2044,6 +2319,7 @@ public void setVertex(int index, float x, float y, float z) {
vertices[index][Z] = z;
}
+
/**
* @param vec the PVector to define the x, y, z coordinates
*/
@@ -2055,7 +2331,12 @@ public void setVertex(int index, PVector vec) {
vertices[index][X] = vec.x;
vertices[index][Y] = vec.y;
- vertices[index][Z] = vec.z;
+
+ if (vertices[index].length > 2) {
+ vertices[index][Z] = vec.z;
+ } else if (vec.z != 0 && vec.z == vec.z) {
+ throw new IllegalArgumentException("Cannot set a z-coordinate on a 2D shape");
+ }
}
@@ -2102,6 +2383,19 @@ public void setNormal(int index, float nx, float ny, float nz) {
}
+
+ public void setAttrib(String name, int index, float... values) {
+ }
+
+
+ public void setAttrib(String name, int index, int... values) {
+ }
+
+
+ public void setAttrib(String name, int index, boolean... values) {
+ }
+
+
public float getTextureU(int index) {
return vertices[index][PGraphics.U];
}
@@ -2118,6 +2412,14 @@ public void setTextureUV(int index, float u, float v) {
return;
}
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setTextureUV()");
+ return;
+ }
+
+
vertices[index][PGraphics.U] = u;
vertices[index][PGraphics.V] = v;
}
@@ -2144,6 +2446,13 @@ public void setTexture(PImage tex) {
public int getFill(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getFill()");
+ return fillColor;
+ }
+
if (image == null) {
int a = (int) (vertices[index][PGraphics.A] * 255);
int r = (int) (vertices[index][PGraphics.R] * 255);
@@ -2155,7 +2464,9 @@ public int getFill(int index) {
}
}
-
+ /**
+ * @nowebref
+ */
public void setFill(boolean fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()");
@@ -2166,24 +2477,59 @@ public void setFill(boolean fill) {
}
+ /**
+ * ( begin auto-generated from PShape_setFill.xml )
+ *
+ * The setFill() method defines the fill color of a PShape .
+ * This method is used after shapes are created or when a shape is defined explicitly
+ * (e.g. createShape(RECT, 20, 20, 80, 80) ) as shown in the above example.
+ * When a shape is created with beginShape() and endShape() , its
+ * attributes may be changed with fill() and stroke() within
+ * beginShape() and endShape() . However, after the shape is
+ * created, only the setFill() method can define a new fill value for
+ * the PShape .
+ *
+ * ( end auto-generated )
+ *
+ * @webref
+ * @param fill
+ * @brief Set the fill value
+ */
public void setFill(int fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()");
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setFill(i, fill);
+ this.fillColor = fill;
+
+ if (vertices != null && perVertexStyles) {
+ for (int i = 0; i < vertexCount; i++) {
+ setFill(i, fill);
+ }
}
}
-
+ /**
+ * @nowebref
+ */
public void setFill(int index, int fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()");
return;
}
+ if (!perVertexStyles) {
+ PGraphics.showWarning(PER_VERTEX_UNSUPPORTED, "setFill()");
+ return;
+ }
+
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getFill()");
+ return;
+ }
+
if (image == null) {
vertices[index][PGraphics.A] = ((fill >> 24) & 0xFF) / 255.0f;
vertices[index][PGraphics.R] = ((fill >> 16) & 0xFF) / 255.0f;
@@ -2194,6 +2540,12 @@ public void setFill(int index, int fill) {
public int getTint(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getTint()");
+ return this.tintColor;
+ }
+
if (image != null) {
int a = (int) (vertices[index][PGraphics.A] * 255);
int r = (int) (vertices[index][PGraphics.R] * 255);
@@ -2222,8 +2574,12 @@ public void setTint(int fill) {
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setFill(i, fill);
+ tintColor = fill;
+
+ if (vertices != null) {
+ for (int i = 0; i < vertices.length; i++) {
+ setFill(i, fill);
+ }
}
}
@@ -2234,6 +2590,13 @@ public void setTint(int index, int tint) {
return;
}
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setTint()");
+ return;
+ }
+
if (image != null) {
vertices[index][PGraphics.A] = ((tint >> 24) & 0xFF) / 255.0f;
vertices[index][PGraphics.R] = ((tint >> 16) & 0xFF) / 255.0f;
@@ -2244,6 +2607,13 @@ public void setTint(int index, int tint) {
public int getStroke(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getStroke()");
+ return strokeColor;
+ }
+
int a = (int) (vertices[index][PGraphics.SA] * 255);
int r = (int) (vertices[index][PGraphics.SR] * 255);
int g = (int) (vertices[index][PGraphics.SG] * 255);
@@ -2252,6 +2622,9 @@ public int getStroke(int index) {
}
+ /**
+ * @nowebref
+ */
public void setStroke(boolean stroke) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()");
@@ -2262,24 +2635,60 @@ public void setStroke(boolean stroke) {
}
+ /**
+ * ( begin auto-generated from PShape_setStroke.xml )
+ *
+ * The setStroke() method defines the outline color of a PShape .
+ * This method is used after shapes are created or when a shape is defined
+ * explicitly (e.g. createShape(RECT, 20, 20, 80, 80) ) as shown in
+ * the above example. When a shape is created with beginShape() and
+ * endShape() , its attributes may be changed with fill() and
+ * stroke() within beginShape() and endShape() .
+ * However, after the shape is created, only the setStroke() method
+ * can define a new stroke value for the PShape .
+ *
+ * ( end auto-generated )
+ *
+ * @webref
+ * @param stroke
+ * @brief Set the stroke value
+ */
public void setStroke(int stroke) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()");
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setStroke(i, stroke);
+ strokeColor = stroke;
+
+ if (vertices != null && perVertexStyles) {
+ for (int i = 0; i < vertices.length; i++) {
+ setStroke(i, stroke);
+ }
}
}
+ /**
+ * @nowebref
+ */
public void setStroke(int index, int stroke) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()");
return;
}
+ if (!perVertexStyles) {
+ PGraphics.showWarning(PER_VERTEX_UNSUPPORTED, "setStroke()");
+ return;
+ }
+
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setStroke()");
+ return;
+ }
+
vertices[index][PGraphics.SA] = ((stroke >> 24) & 0xFF) / 255.0f;
vertices[index][PGraphics.SR] = ((stroke >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.SG] = ((stroke >> 8) & 0xFF) / 255.0f;
@@ -2288,6 +2697,13 @@ public void setStroke(int index, int stroke) {
public float getStrokeWeight(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getStrokeWeight()");
+ return strokeWeight;
+ }
+
+
return vertices[index][PGraphics.SW];
}
@@ -2298,8 +2714,12 @@ public void setStrokeWeight(float weight) {
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setStrokeWeight(i, weight);
+ strokeWeight = weight;
+
+ if (vertices != null && perVertexStyles) {
+ for (int i = 0; i < vertexCount; i++) {
+ setStrokeWeight(i, weight);
+ }
}
}
@@ -2310,6 +2730,17 @@ public void setStrokeWeight(int index, float weight) {
return;
}
+ if (!perVertexStyles) {
+ PGraphics.showWarning(PER_VERTEX_UNSUPPORTED, "setStrokeWeight()");
+ return;
+ }
+
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setStrokeWeight()");
+ return;
+ }
+
vertices[index][PGraphics.SW] = weight;
}
@@ -2335,6 +2766,13 @@ public void setStrokeCap(int cap) {
public int getAmbient(int index) {
+
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getAmbient()");
+ return ambientColor;
+ }
+
int r = (int) (vertices[index][PGraphics.AR] * 255);
int g = (int) (vertices[index][PGraphics.AG] * 255);
int b = (int) (vertices[index][PGraphics.AB] * 255);
@@ -2348,8 +2786,12 @@ public void setAmbient(int ambient) {
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setAmbient(i, ambient);
+ ambientColor = ambient;
+
+ if (vertices != null) {
+ for (int i = 0; i < vertices.length; i++) {
+ setAmbient(i, ambient);
+ }
}
}
@@ -2360,6 +2802,12 @@ public void setAmbient(int index, int ambient) {
return;
}
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setAmbient()");
+ return;
+ }
+
vertices[index][PGraphics.AR] = ((ambient >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.AG] = ((ambient >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.AB] = ((ambient >> 0) & 0xFF) / 255.0f;
@@ -2367,6 +2815,12 @@ public void setAmbient(int index, int ambient) {
public int getSpecular(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getSpecular()");
+ return specularColor;
+ }
+
int r = (int) (vertices[index][PGraphics.SPR] * 255);
int g = (int) (vertices[index][PGraphics.SPG] * 255);
int b = (int) (vertices[index][PGraphics.SPB] * 255);
@@ -2380,8 +2834,12 @@ public void setSpecular(int specular) {
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setSpecular(i, specular);
+ specularColor = specular;
+
+ if (vertices != null) {
+ for (int i = 0; i < vertices.length; i++) {
+ setSpecular(i, specular);
+ }
}
}
@@ -2392,6 +2850,12 @@ public void setSpecular(int index, int specular) {
return;
}
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setSpecular()");
+ return;
+ }
+
vertices[index][PGraphics.SPR] = ((specular >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.SPG] = ((specular >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.SPB] = ((specular >> 0) & 0xFF) / 255.0f;
@@ -2399,6 +2863,12 @@ public void setSpecular(int index, int specular) {
public int getEmissive(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null || index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getEmissive()");
+ return emissiveColor;
+ }
+
int r = (int) (vertices[index][PGraphics.ER] * 255);
int g = (int) (vertices[index][PGraphics.EG] * 255);
int b = (int) (vertices[index][PGraphics.EB] * 255);
@@ -2412,8 +2882,12 @@ public void setEmissive(int emissive) {
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setEmissive(i, emissive);
+ emissiveColor = emissive;
+
+ if (vertices != null) {
+ for (int i = 0; i < vertices.length; i++) {
+ setEmissive(i, emissive);
+ }
}
}
@@ -2424,6 +2898,13 @@ public void setEmissive(int index, int emissive) {
return;
}
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setEmissive()");
+ return;
+ }
+
vertices[index][PGraphics.ER] = ((emissive >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.EG] = ((emissive >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.EB] = ((emissive >> 0) & 0xFF) / 255.0f;
@@ -2431,6 +2912,13 @@ public void setEmissive(int index, int emissive) {
public float getShininess(int index) {
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getShininess()");
+ return shininess;
+ }
+
return vertices[index][PGraphics.SHINE];
}
@@ -2441,8 +2929,12 @@ public void setShininess(float shine) {
return;
}
- for (int i = 0; i < vertices.length; i++) {
- setShininess(i, shine);
+ shininess = shine;
+
+ if (vertices != null) {
+ for (int i = 0; i < vertices.length; i++) {
+ setShininess(i, shine);
+ }
}
}
@@ -2453,6 +2945,14 @@ public void setShininess(int index, float shine) {
return;
}
+ // make sure we allocated the vertices array and that vertex exists
+ if (vertices == null ||
+ index >= vertices.length) {
+ PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setShininess()");
+ return;
+ }
+
+
vertices[index][PGraphics.SHINE] = shine;
}
@@ -2489,13 +2989,28 @@ public boolean isClosed() {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
- // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+ /**
+ * Return true if this x, y coordinate is part of this shape. Only works
+ * with PATH shapes or GROUP shapes that contain other GROUPs or PATHs.
+ */
public boolean contains(float x, float y) {
if (family == PATH) {
+ PVector p = new PVector(x, y);
+ if (matrix != null) {
+ // apply the inverse transformation matrix to the point coordinates
+ PMatrix inverseCoords = matrix.get();
+ // TODO why is this called twice? [fry 190724]
+ // commit was https://github.com/processing/processing/commit/027fc7a4f8e8d0a435366eae754304eea282512a
+ inverseCoords.invert(); // maybe cache this?
+ inverseCoords.invert(); // maybe cache this?
+ inverseCoords.mult(new PVector(x, y), p);
+ }
+
+ // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
boolean c = false;
for (int i = 0, j = vertexCount-1; i < vertexCount; j = i++) {
- if (((vertices[i][Y] > y) != (vertices[j][Y] > y)) &&
- (x <
+ if (((vertices[i][Y] > p.y) != (vertices[j][Y] > p.y)) &&
+ (p.x <
(vertices[j][X]-vertices[i][X]) *
(y-vertices[i][Y]) /
(vertices[j][1]-vertices[i][Y]) +
@@ -2504,7 +3019,18 @@ public boolean contains(float x, float y) {
}
}
return c;
+
+ } else if (family == GROUP) {
+ // If this is a group, loop through children until we find one that
+ // contains the supplied coordinates. If a child does not support
+ // contains() throw a warning and continue.
+ for (int i = 0; i < childCount; i++) {
+ if (children[i].contains(x, y)) return true;
+ }
+ return false;
+
} else {
+ // https://github.com/processing/processing/issues/1280
throw new IllegalArgumentException("The contains() method is only implemented for paths.");
}
}
@@ -2538,8 +3064,8 @@ public boolean contains(float x, float y) {
* @webref pshape:method
* @usage web_application
* @brief Displaces the shape
- * @param tx left/right translation
- * @param ty up/down translation
+ * @param x left/right translation
+ * @param y up/down translation
* @see PShape#rotate(float)
* @see PShape#scale(float)
* @see PShape#resetMatrix()
@@ -2550,7 +3076,7 @@ public void translate(float x, float y) {
}
/**
- * @param tz forward/back translation
+ * @param z forward/back translation
*/
public void translate(float x, float y, float z) {
checkMatrix(3);
@@ -3040,4 +3566,4 @@ protected void colorCalcARGB(int argb, float alpha) {
calcAlpha = (calcAi != 255);
}
-}
\ No newline at end of file
+}
diff --git a/core/src/processing/core/PShapeOBJ.java b/libs/processing-core/src/main/java/processing/core/PShapeOBJ.java
similarity index 82%
rename from core/src/processing/core/PShapeOBJ.java
rename to libs/processing-core/src/main/java/processing/core/PShapeOBJ.java
index 1f4bd50bb..470f3820a 100644
--- a/core/src/processing/core/PShapeOBJ.java
+++ b/libs/processing-core/src/main/java/processing/core/PShapeOBJ.java
@@ -1,10 +1,39 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
package processing.core;
import java.io.BufferedReader;
+import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
+
/**
+ * This class is not part of the Processing API and should not be used
+ * directly. Instead, use loadShape() and methods like it, which will make
+ * use of this class. Using this class directly will cause your code to break
+ * when combined with future versions of Processing.
+ *
* OBJ loading implemented using code from Saito's OBJLoader library:
* http://code.google.com/p/saitoobjloader/
* and OBJReader from Ahmet Kizilay
@@ -17,17 +46,21 @@ public class PShapeOBJ extends PShape {
* Initializes a new OBJ Object with the given filename.
*/
public PShapeOBJ(PApplet parent, String filename) {
- this(parent, parent.createReader(filename));
+ this(parent, parent.createReader(filename), getBasePath(parent, filename));
}
-
public PShapeOBJ(PApplet parent, BufferedReader reader) {
+ this(parent, reader, "");
+ }
+
+ public PShapeOBJ(PApplet parent, BufferedReader reader, String basePath) {
ArrayList faces = new ArrayList();
ArrayList materials = new ArrayList();
ArrayList coords = new ArrayList();
ArrayList normals = new ArrayList();
ArrayList texcoords = new ArrayList();
- parseOBJ(parent, reader, faces, materials, coords, normals, texcoords);
+ parseOBJ(parent, basePath,
+ reader, faces, materials, coords, normals, texcoords);
// The OBJ geometry is stored with each face in a separate child shape.
parent = null;
@@ -142,7 +175,7 @@ protected void addChildren(ArrayList faces,
}
- static protected void parseOBJ(PApplet parent,
+ static protected void parseOBJ(PApplet parent, String path,
BufferedReader reader,
ArrayList faces,
ArrayList materials,
@@ -158,7 +191,7 @@ static protected void parseOBJ(PApplet parent,
String line;
String gname = "object";
while ((line = reader.readLine()) != null) {
- // Parse the line.
+ // Parse the line.
line = line.trim();
if (line.equals("") || line.indexOf('#') == 0) {
// Empty line of comment, ignore line
@@ -205,19 +238,23 @@ static protected void parseOBJ(PApplet parent,
// uv, inverting v to take into account Processing's inverted Y axis
// with respect to OpenGL.
PVector tempv = new PVector(Float.valueOf(parts[1]).floatValue(),
- 1 - Float.valueOf(parts[2]).
- floatValue());
+ 1 - Float.valueOf(parts[2]).floatValue());
texcoords.add(tempv);
readvt = true;
} else if (parts[0].equals("o")) {
// Object name is ignored, for now.
} else if (parts[0].equals("mtllib")) {
if (parts[1] != null) {
- BufferedReader mreader = parent.createReader(parts[1]);
+ String fn = parts[1];
+ if (fn.indexOf(File.separator) == -1 && !path.equals("")) {
+ // Relative file name, adding the base path.
+ fn = path + File.separator + fn;
+ }
+ BufferedReader mreader = parent.createReader(fn);
if (mreader != null) {
- parseMTL(parent, mreader, materials, mtlTable);
+ parseMTL(parent, path,
+ mreader, materials, mtlTable);
}
- mreader.close();
}
} else if (parts[0].equals("g")) {
gname = 1 < parts.length ? parts[1] : "";
@@ -304,7 +341,7 @@ static protected void parseOBJ(PApplet parent,
}
- static protected void parseMTL(PApplet parent,
+ static protected void parseMTL(PApplet parent, String path,
BufferedReader reader,
ArrayList materials,
Hashtable materialsHash) {
@@ -321,11 +358,15 @@ static protected void parseMTL(PApplet parent,
// Starting new material.
String mtlname = parts[1];
currentMtl = new OBJMaterial(mtlname);
- materialsHash.put(mtlname, new Integer(materials.size()));
+ materialsHash.put(mtlname, Integer.valueOf(materials.size()));
materials.add(currentMtl);
} else if (parts[0].equals("map_Kd") && parts.length > 1) {
// Loading texture map.
String texname = parts[1];
+ if (texname.indexOf(File.separator) == -1 && !path.equals("")) {
+ // Relative file name, adding the base path.
+ texname = path + File.separator + texname;
+ }
currentMtl.kdMap = parent.loadImage(texname);
} else if (parts[0].equals("Ka") && parts.length > 3) {
// The ambient color of the material
@@ -343,7 +384,7 @@ static protected void parseMTL(PApplet parent,
currentMtl.ks.y = Float.valueOf(parts[2]).floatValue();
currentMtl.ks.z = Float.valueOf(parts[3]).floatValue();
} else if ((parts[0].equals("d") ||
- parts[0].equals("Tr")) && parts.length > 1) {
+ parts[0].equals("Tr")) && parts.length > 1) {
// Reading the alpha transparency.
currentMtl.d = Float.valueOf(parts[1]).floatValue();
} else if (parts[0].equals("Ns") && parts.length > 1) {
@@ -360,16 +401,16 @@ static protected void parseMTL(PApplet parent,
protected static int rgbaValue(PVector color) {
return 0xFF000000 | ((int)(color.x * 255) << 16) |
- ((int)(color.y * 255) << 8) |
- (int)(color.z * 255);
+ ((int)(color.y * 255) << 8) |
+ (int)(color.z * 255);
}
protected static int rgbaValue(PVector color, float alpha) {
return ((int)(alpha * 255) << 24) |
- ((int)(color.x * 255) << 16) |
- ((int)(color.y * 255) << 8) |
- (int)(color.z * 255);
+ ((int)(color.x * 255) << 16) |
+ ((int)(color.y * 255) << 8) |
+ (int)(color.z * 255);
}
@@ -391,6 +432,14 @@ static protected class OBJFace {
}
+ static protected String getBasePath(PApplet parent, String filename) {
+ if (-1 < filename.indexOf(File.separator)) {
+ return filename.substring(0, filename.lastIndexOf(File.separator));
+ }
+ return "";
+ }
+
+
// Stores a material defined in an MTL file.
static protected class OBJMaterial {
String name;
@@ -416,3 +465,4 @@ static protected class OBJMaterial {
}
}
}
+
diff --git a/libs/processing-core/src/main/java/processing/core/PShapeSVG.java b/libs/processing-core/src/main/java/processing/core/PShapeSVG.java
new file mode 100644
index 000000000..33bd932b8
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/core/PShapeSVG.java
@@ -0,0 +1,2031 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2006-12 Ben Fry and Casey Reas
+ Copyright (c) 2004-06 Michael Chang
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.core;
+
+//import static java.awt.Font.BOLD;
+//import static java.awt.Font.ITALIC;
+//import static java.awt.Font.PLAIN;
+import processing.data.*;
+
+// TODO replace these with PMatrix2D
+import android.graphics.Matrix;
+//import java.awt.geom.AffineTransform;
+//import java.awt.geom.Point2D;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * This class is not part of the Processing API and should not be used
+ * directly. Instead, use loadShape() and methods like it, which will make
+ * use of this class. Using this class directly will cause your code to break
+ * when combined with future versions of Processing.
+ *
+ * SVG stands for Scalable Vector Graphics, a portable graphics format.
+ * It is a vector format so it allows for "infinite" resolution and relatively
+ * small file sizes. Most modern media software can view SVG files, including
+ * Adobe products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
+ * View the SVG specification here .
+ *
+ * We have no intention of turning this into a full-featured SVG library.
+ * The goal of this project is a basic shape importer that originally was small
+ * enough to be included with applets, meaning that its download size should be
+ * in the neighborhood of 25-30 Kb. Though we're far less limited nowadays on
+ * size constraints, we remain extremely limited in terms of time, and do not
+ * have volunteers who are available to maintain a larger SVG library.
+ *
+ * For more sophisticated import/export, consider the
+ * Batik
+ * library from the Apache Software Foundation.
+ *
+ * Batik is used in the SVG Export library in Processing 3, however using it
+ * for full SVG import is still a considerable amount of work. Wiring it to
+ * Java2D wouldn't be too bad, but using it with OpenGL, JavaFX, and features
+ * like begin/endRecord() and begin/endRaw() would be considerable effort.
+ *
+ * Future improvements to this library may focus on this properly supporting
+ * a specific subset of SVG, for instance the simpler SVG profiles known as
+ * SVG Tiny or Basic ,
+ * although we still would not support the interactivity options.
+ *
+ *
+ *
+ * A minimal example program using SVG:
+ * (assuming a working moo.svg is in your data folder)
+ *
+ *
+ * PShape moo;
+ *
+ * void setup() {
+ * size(400, 400);
+ * moo = loadShape("moo.svg");
+ * }
+ * void draw() {
+ * background(255);
+ * shape(moo, mouseX, mouseY);
+ * }
+ *
+ */
+public class PShapeSVG extends PShape {
+ XML element;
+
+ /// Values between 0 and 1.
+ protected float opacity;
+ float strokeOpacity;
+ float fillOpacity;
+
+ /** Width of containing SVG (used for percentages). */
+ protected float svgWidth;
+
+ /** Height of containing SVG (used for percentages). */
+ protected float svgHeight;
+
+ /** √((w² + h²)/2) of containing SVG (used for percentages). */
+ protected float svgSizeXY;
+
+ protected Gradient strokeGradient;
+ String strokeName; // id of another object, gradients only?
+
+ protected Gradient fillGradient;
+ String fillName; // id of another object
+
+
+ /**
+ * Initializes a new SVG object from the given XML object.
+ */
+ public PShapeSVG(XML svg) {
+ this(null, svg, true);
+
+ if (!svg.getName().equals("svg")) {
+ if (svg.getName().toLowerCase().equals("html")) {
+ // Common case is that files aren't downloaded properly
+ throw new RuntimeException("This appears to be a web page, not an SVG file.");
+ } else {
+ throw new RuntimeException("The root node is not , it's <" + svg.getName() + ">");
+ }
+ }
+ }
+
+
+ protected PShapeSVG(PShapeSVG parent, XML properties, boolean parseKids) {
+ setParent(parent);
+
+ // Need to get width/height in early.
+ if (properties.getName().equals("svg")) {
+ String unitWidth = properties.getString("width");
+ String unitHeight = properties.getString("height");
+
+ // Can't handle width/height as percentages easily. I'm just going
+ // to put in 100 as a dummy value, beacuse this means that it will
+ // come out as a reasonable value.
+ if (unitWidth != null) width = parseUnitSize(unitWidth, 100);
+ if (unitHeight != null) height = parseUnitSize(unitHeight, 100);
+
+ String viewBoxStr = properties.getString("viewBox");
+ if (viewBoxStr != null) {
+ float[] viewBox = PApplet.parseFloat(PApplet.splitTokens(viewBoxStr));
+ if (unitWidth == null || unitHeight == null) {
+ // Not proper parsing of the viewBox, but will cover us for cases where
+ // the width and height of the object is not specified.
+ width = viewBox[2];
+ height = viewBox[3];
+ } else {
+ // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
+ // TODO: preserveAspectRatio.
+ if (matrix == null) matrix = new PMatrix2D();
+ matrix.scale(width/viewBox[2], height/viewBox[3]);
+ matrix.translate(-viewBox[0], -viewBox[1]);
+ }
+ }
+
+ // Negative size is illegal.
+ if (width < 0 || height < 0)
+ throw new RuntimeException(": width (" + width +
+ ") and height (" + height + ") must not be negative.");
+
+ // It's technically valid to have width or height == 0. Not specified at
+ // all is what to test for.
+ if ((unitWidth == null || unitHeight == null) && viewBoxStr == null) {
+ //throw new RuntimeException("width/height not specified");
+ PGraphics.showWarning("The width and/or height is not " +
+ "readable in the tag of this file.");
+ // For the spec, the default is 100% and 100%. For purposes
+ // here, insert a dummy value because this is prolly just a
+ // font or something for which the w/h doesn't matter.
+ width = 1;
+ height = 1;
+ }
+
+ svgWidth = width;
+ svgHeight = height;
+ svgSizeXY = PApplet.sqrt((svgWidth*svgWidth + svgHeight*svgHeight)/2.0f);
+ }
+
+ element = properties;
+ name = properties.getString("id");
+ // @#$(* adobe illustrator mangles names of objects when re-saving
+ if (name != null) {
+ while (true) {
+ String[] m = PApplet.match(name, "_x([A-Za-z0-9]{2})_");
+ if (m == null) break;
+ char repair = (char) PApplet.unhex(m[1]);
+ name = name.replace(m[0], "" + repair);
+ }
+ }
+
+ String displayStr = properties.getString("display", "inline");
+ visible = !displayStr.equals("none");
+
+ String transformStr = properties.getString("transform");
+ if (transformStr != null) {
+ if (matrix == null) {
+ matrix = parseTransform(transformStr);
+ } else {
+ matrix.preApply(parseTransform(transformStr));
+ }
+ }
+
+ if (parseKids) {
+ parseColors(properties);
+ parseChildren(properties);
+ }
+ }
+
+
+ // Broken out so that subclasses can copy any additional variables
+ // (i.e. fillGradientPaint and strokeGradientPaint)
+ protected void setParent(PShapeSVG parent) {
+ // Need to set this so that findChild() works.
+ // Otherwise 'parent' is null until addChild() is called later.
+ this.parent = parent;
+
+ if (parent == null) {
+ // set values to their defaults according to the SVG spec
+ stroke = false;
+ strokeColor = 0xff000000;
+ strokeWeight = 1;
+ strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec
+ strokeJoin = PConstants.MITER;
+ strokeGradient = null;
+// strokeGradientPaint = null;
+ strokeName = null;
+
+ fill = true;
+ fillColor = 0xff000000;
+ fillGradient = null;
+// fillGradientPaint = null;
+ fillName = null;
+
+ //hasTransform = false;
+ //transformation = null; //new float[] { 1, 0, 0, 1, 0, 0 };
+
+ // svgWidth, svgHeight, and svgXYSize done below.
+
+ strokeOpacity = 1;
+ fillOpacity = 1;
+ opacity = 1;
+
+ } else {
+ stroke = parent.stroke;
+ strokeColor = parent.strokeColor;
+ strokeWeight = parent.strokeWeight;
+ strokeCap = parent.strokeCap;
+ strokeJoin = parent.strokeJoin;
+ strokeGradient = parent.strokeGradient;
+// strokeGradientPaint = parent.strokeGradientPaint;
+ strokeName = parent.strokeName;
+
+ fill = parent.fill;
+ fillColor = parent.fillColor;
+ fillGradient = parent.fillGradient;
+// fillGradientPaint = parent.fillGradientPaint;
+ fillName = parent.fillName;
+
+ svgWidth = parent.svgWidth;
+ svgHeight = parent.svgHeight;
+ svgSizeXY = parent.svgSizeXY;
+
+ opacity = parent.opacity;
+ }
+
+ // The rect and ellipse modes are set to CORNER since it is the expected
+ // mode for svg shapes.
+ rectMode = CORNER;
+ ellipseMode = CORNER;
+ }
+
+
+ /** Factory method for subclasses. */
+ protected PShapeSVG createShape(PShapeSVG parent, XML properties, boolean parseKids) {
+ return new PShapeSVG(parent, properties, parseKids);
+ }
+
+
+ protected void parseChildren(XML graphics) {
+ XML[] elements = graphics.getChildren();
+ children = new PShape[elements.length];
+ childCount = 0;
+
+ for (XML elem : elements) {
+ PShape kid = parseChild(elem);
+ if (kid != null) addChild(kid);
+ }
+ children = (PShape[]) PApplet.subset(children, 0, childCount);
+ }
+
+
+ /**
+ * Parse a child XML element.
+ * Override this method to add parsing for more SVG elements.
+ */
+ protected PShape parseChild(XML elem) {
+// System.err.println("parsing child in pshape " + elem.getName());
+ String name = elem.getName();
+ PShapeSVG shape = null;
+
+
+ if (name == null) {
+ // just some whitespace that can be ignored (hopefully)
+
+ } else if (name.equals("g")) {
+ shape = createShape(this, elem, true);
+
+ } else if (name.equals("defs")) {
+ // generally this will contain gradient info, so may
+ // as well just throw it into a group element for parsing
+ shape = createShape(this, elem, true);
+
+ } else if (name.equals("line")) {
+ shape = createShape(this, elem, true);
+ shape.parseLine();
+
+ } else if (name.equals("circle")) {
+ shape = createShape(this, elem, true);
+ shape.parseEllipse(true);
+
+ } else if (name.equals("ellipse")) {
+ shape = createShape(this, elem, true);
+ shape.parseEllipse(false);
+
+ } else if (name.equals("rect")) {
+ shape = createShape(this, elem, true);
+ shape.parseRect();
+
+ } else if (name.equals("image")) {
+ shape = createShape(this, elem, true);
+ shape.parseImage();
+
+ } else if (name.equals("polygon")) {
+ shape = createShape(this, elem, true);
+ shape.parsePoly(true);
+
+ } else if (name.equals("polyline")) {
+ shape = createShape(this, elem, true);
+ shape.parsePoly(false);
+
+ } else if (name.equals("path")) {
+ shape = createShape(this, elem, true);
+ shape.parsePath();
+
+ } else if (name.equals("radialGradient")) {
+ return new RadialGradient(this, elem);
+
+ } else if (name.equals("linearGradient")) {
+ return new LinearGradient(this, elem);
+
+ } else if (name.equals("font")) {
+ return new Font(this, elem);
+
+// } else if (name.equals("font-face")) {
+// return new FontFace(this, elem);
+
+// } else if (name.equals("glyph") || name.equals("missing-glyph")) {
+// return new FontGlyph(this, elem);
+
+ } else if (name.equals("text")) { // || name.equals("font")) {
+ return new Text(this, elem);
+
+ } else if (name.equals("tspan")) {
+// return new LineOfText(this, elem);
+ PGraphics.showWarning("tspan elements are not supported.");
+
+ } else if (name.equals("filter")) {
+ PGraphics.showWarning("Filters are not supported.");
+
+ } else if (name.equals("mask")) {
+ PGraphics.showWarning("Masks are not supported.");
+
+ } else if (name.equals("pattern")) {
+ PGraphics.showWarning("Patterns are not supported.");
+
+ } else if (name.equals("stop")) {
+ // stop tag is handled by gradient parser, so don't warn about it
+
+ } else if (name.equals("sodipodi:namedview")) {
+ // these are always in Inkscape files, the warnings get tedious
+
+ } else if (name.equals("metadata")
+ || name.equals("title") || name.equals("desc")) {
+ // fontforge just stuffs in as a comment.
+ // All harmless stuff, irrelevant to rendering.
+ return null;
+
+ } else if (!name.startsWith("#")) {
+ PGraphics.showWarning("Ignoring <" + name + "> tag.");
+// new Exception().printStackTrace();
+ }
+ return shape;
+ }
+
+
+ protected void parseLine() {
+ kind = LINE;
+ family = PRIMITIVE;
+ params = new float[] {
+ getFloatWithUnit(element, "x1", svgWidth),
+ getFloatWithUnit(element, "y1", svgHeight),
+ getFloatWithUnit(element, "x2", svgWidth),
+ getFloatWithUnit(element, "y2", svgHeight)
+ };
+ }
+
+
+ /**
+ * Handles parsing ellipse and circle tags.
+ * @param circle true if this is a circle and not an ellipse
+ */
+ protected void parseEllipse(boolean circle) {
+ kind = ELLIPSE;
+ family = PRIMITIVE;
+ params = new float[4];
+
+ params[0] = getFloatWithUnit(element, "cx", svgWidth);
+ params[1] = getFloatWithUnit(element, "cy", svgHeight);
+
+ float rx, ry;
+ if (circle) {
+ rx = ry = getFloatWithUnit(element, "r", svgSizeXY);
+ } else {
+ rx = getFloatWithUnit(element, "rx", svgWidth);
+ ry = getFloatWithUnit(element, "ry", svgHeight);
+ }
+ params[0] -= rx;
+ params[1] -= ry;
+
+ params[2] = rx*2;
+ params[3] = ry*2;
+ }
+
+
+ protected void parseRect() {
+ kind = RECT;
+ family = PRIMITIVE;
+ params = new float[] {
+ getFloatWithUnit(element, "x", svgWidth),
+ getFloatWithUnit(element, "y", svgHeight),
+ getFloatWithUnit(element, "width", svgWidth),
+ getFloatWithUnit(element, "height", svgHeight)
+ };
+ }
+
+
+ protected void parseImage() {
+ kind = RECT;
+ textureMode = NORMAL;
+
+ family = PRIMITIVE;
+ params = new float[] {
+ getFloatWithUnit(element, "x", svgWidth),
+ getFloatWithUnit(element, "y", svgHeight),
+ getFloatWithUnit(element, "width", svgWidth),
+ getFloatWithUnit(element, "height", svgHeight)
+ };
+
+ this.imagePath = element.getString("xlink:href");
+ }
+
+ /**
+ * Parse a polyline or polygon from an SVG file.
+ * Syntax defined at http://www.w3.org/TR/SVG/shapes.html#PointsBNF
+ * @param close true if shape is closed (polygon), false if not (polyline)
+ */
+ protected void parsePoly(boolean close) {
+ family = PATH;
+ this.close = close;
+
+ String pointsAttr = element.getString("points");
+ if (pointsAttr != null) {
+ Pattern pattern = Pattern.compile("([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)(,?\\s*)([+-]?[\\d]+(\\.[\\d]+)?([eE][+-][\\d]+)?)");
+ Matcher matcher = pattern.matcher(pointsAttr);
+ vertexCount = 0;
+ while (matcher.find()) {
+ vertexCount++;
+ }
+ matcher.reset();
+ vertices = new float[vertexCount][2];
+ for (int i = 0; i < vertexCount; i++) {
+ matcher.find();
+ vertices[i][X] = Float.parseFloat(matcher.group(1));
+ vertices[i][Y] = Float.parseFloat(matcher.group(5));
+ }
+// String[] pointsBuffer = PApplet.splitTokens(pointsAttr);
+// vertexCount = pointsBuffer.length;
+// vertices = new float[vertexCount][2];
+// for (int i = 0; i < vertexCount; i++) {
+// String pb[] = PApplet.splitTokens(pointsBuffer[i], ", \t\r\n");
+// vertices[i][X] = Float.parseFloat(pb[0]);
+// vertices[i][Y] = Float.parseFloat(pb[1]);
+// }
+ }
+ }
+
+
+ protected void parsePath() {
+ family = PATH;
+ kind = 0;
+
+ String pathData = element.getString("d");
+ if (pathData == null || PApplet.trim(pathData).length() == 0) {
+ return;
+ }
+ char[] pathDataChars = pathData.toCharArray();
+
+ StringBuilder pathBuffer = new StringBuilder();
+ boolean lastSeparate = false;
+
+ for (int i = 0; i < pathDataChars.length; i++) {
+ char c = pathDataChars[i];
+ boolean separate = false;
+
+ if (c == 'M' || c == 'm' ||
+ c == 'L' || c == 'l' ||
+ c == 'H' || c == 'h' ||
+ c == 'V' || c == 'v' ||
+ c == 'C' || c == 'c' || // beziers
+ c == 'S' || c == 's' ||
+ c == 'Q' || c == 'q' || // quadratic beziers
+ c == 'T' || c == 't' ||
+ c == 'A' || c == 'a' || // elliptical arc
+ c == 'Z' || c == 'z' || // closepath
+ c == ',') {
+ separate = true;
+ if (i != 0) {
+ pathBuffer.append("|");
+ }
+ }
+ if (c == 'Z' || c == 'z') {
+ separate = false;
+ }
+ if (c == '-' && !lastSeparate) {
+ // allow for 'e' notation in numbers, e.g. 2.10e-9
+ // http://dev.processing.org/bugs/show_bug.cgi?id=1408
+ if (i == 0 || pathDataChars[i-1] != 'e') {
+ pathBuffer.append("|");
+ }
+ }
+ if (c != ',') {
+ pathBuffer.append(c); //"" + pathDataBuffer.charAt(i));
+ }
+ if (separate && c != ',' && c != '-') {
+ pathBuffer.append("|");
+ }
+ lastSeparate = separate;
+ }
+
+ // use whitespace constant to get rid of extra spaces and CR or LF
+ String[] pathTokens =
+ PApplet.splitTokens(pathBuffer.toString(), "|" + WHITESPACE);
+ vertices = new float[pathTokens.length][2];
+ vertexCodes = new int[pathTokens.length];
+
+ float cx = 0;
+ float cy = 0;
+ int i = 0;
+
+ char implicitCommand = '\0';
+// char prevCommand = '\0';
+ boolean prevCurve = false;
+ float ctrlX, ctrlY;
+ // store values for closepath so that relative coords work properly
+ float movetoX = 0;
+ float movetoY = 0;
+
+ while (i < pathTokens.length) {
+ char c = pathTokens[i].charAt(0);
+ if (((c >= '0' && c <= '9') || (c == '-')) && implicitCommand != '\0') {
+ c = implicitCommand;
+ i--;
+ } else {
+ implicitCommand = c;
+ }
+ switch (c) {
+
+ case 'M': // M - move to (absolute)
+ cx = PApplet.parseFloat(pathTokens[i + 1]);
+ cy = PApplet.parseFloat(pathTokens[i + 2]);
+ movetoX = cx;
+ movetoY = cy;
+ parsePathMoveto(cx, cy);
+ implicitCommand = 'L';
+ i += 3;
+ break;
+
+ case 'm': // m - move to (relative)
+ cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ cy = cy + PApplet.parseFloat(pathTokens[i + 2]);
+ movetoX = cx;
+ movetoY = cy;
+ parsePathMoveto(cx, cy);
+ implicitCommand = 'l';
+ i += 3;
+ break;
+
+ case 'L':
+ cx = PApplet.parseFloat(pathTokens[i + 1]);
+ cy = PApplet.parseFloat(pathTokens[i + 2]);
+ parsePathLineto(cx, cy);
+ i += 3;
+ break;
+
+ case 'l':
+ cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ cy = cy + PApplet.parseFloat(pathTokens[i + 2]);
+ parsePathLineto(cx, cy);
+ i += 3;
+ break;
+
+ // horizontal lineto absolute
+ case 'H':
+ cx = PApplet.parseFloat(pathTokens[i + 1]);
+ parsePathLineto(cx, cy);
+ i += 2;
+ break;
+
+ // horizontal lineto relative
+ case 'h':
+ cx = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ parsePathLineto(cx, cy);
+ i += 2;
+ break;
+
+ case 'V':
+ cy = PApplet.parseFloat(pathTokens[i + 1]);
+ parsePathLineto(cx, cy);
+ i += 2;
+ break;
+
+ case 'v':
+ cy = cy + PApplet.parseFloat(pathTokens[i + 1]);
+ parsePathLineto(cx, cy);
+ i += 2;
+ break;
+
+ // C - curve to (absolute)
+ case 'C': {
+ float ctrlX1 = PApplet.parseFloat(pathTokens[i + 1]);
+ float ctrlY1 = PApplet.parseFloat(pathTokens[i + 2]);
+ float ctrlX2 = PApplet.parseFloat(pathTokens[i + 3]);
+ float ctrlY2 = PApplet.parseFloat(pathTokens[i + 4]);
+ float endX = PApplet.parseFloat(pathTokens[i + 5]);
+ float endY = PApplet.parseFloat(pathTokens[i + 6]);
+ parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 7;
+ prevCurve = true;
+ }
+ break;
+
+ // c - curve to (relative)
+ case 'c': {
+ float ctrlX1 = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ float ctrlY1 = cy + PApplet.parseFloat(pathTokens[i + 2]);
+ float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 3]);
+ float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 4]);
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 5]);
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 6]);
+ parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 7;
+ prevCurve = true;
+ }
+ break;
+
+ // S - curve to shorthand (absolute)
+ // Draws a cubic Bézier curve from the current point to (x,y). The first
+ // control point is assumed to be the reflection of the second control
+ // point on the previous command relative to the current point.
+ // (x2,y2) is the second control point (i.e., the control point
+ // at the end of the curve). S (uppercase) indicates that absolute
+ // coordinates will follow; s (lowercase) indicates that relative
+ // coordinates will follow. Multiple sets of coordinates may be specified
+ // to draw a polybézier. At the end of the command, the new current point
+ // becomes the final (x,y) coordinate pair used in the polybézier.
+ case 'S': {
+ // (If there is no previous command or if the previous command was not
+ // an C, c, S or s, assume the first control point is coincident with
+ // the current point.)
+ if (!prevCurve) {
+ ctrlX = cx;
+ ctrlY = cy;
+ } else {
+ float ppx = vertices[vertexCount-2][X];
+ float ppy = vertices[vertexCount-2][Y];
+ float px = vertices[vertexCount-1][X];
+ float py = vertices[vertexCount-1][Y];
+ ctrlX = px + (px - ppx);
+ ctrlY = py + (py - ppy);
+ }
+ float ctrlX2 = PApplet.parseFloat(pathTokens[i + 1]);
+ float ctrlY2 = PApplet.parseFloat(pathTokens[i + 2]);
+ float endX = PApplet.parseFloat(pathTokens[i + 3]);
+ float endY = PApplet.parseFloat(pathTokens[i + 4]);
+ parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 5;
+ prevCurve = true;
+ }
+ break;
+
+ // s - curve to shorthand (relative)
+ case 's': {
+ if (!prevCurve) {
+ ctrlX = cx;
+ ctrlY = cy;
+ } else {
+ float ppx = vertices[vertexCount-2][X];
+ float ppy = vertices[vertexCount-2][Y];
+ float px = vertices[vertexCount-1][X];
+ float py = vertices[vertexCount-1][Y];
+ ctrlX = px + (px - ppx);
+ ctrlY = py + (py - ppy);
+ }
+ float ctrlX2 = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ float ctrlY2 = cy + PApplet.parseFloat(pathTokens[i + 2]);
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 3]);
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 4]);
+ parsePathCurveto(ctrlX, ctrlY, ctrlX2, ctrlY2, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 5;
+ prevCurve = true;
+ }
+ break;
+
+ // Q - quadratic curve to (absolute)
+ // Draws a quadratic Bézier curve from the current point to (x,y) using
+ // (x1,y1) as the control point. Q (uppercase) indicates that absolute
+ // coordinates will follow; q (lowercase) indicates that relative
+ // coordinates will follow. Multiple sets of coordinates may be specified
+ // to draw a polybézier. At the end of the command, the new current point
+ // becomes the final (x,y) coordinate pair used in the polybézier.
+ case 'Q': {
+ ctrlX = PApplet.parseFloat(pathTokens[i + 1]);
+ ctrlY = PApplet.parseFloat(pathTokens[i + 2]);
+ float endX = PApplet.parseFloat(pathTokens[i + 3]);
+ float endY = PApplet.parseFloat(pathTokens[i + 4]);
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 5;
+ prevCurve = true;
+ }
+ break;
+
+ // q - quadratic curve to (relative)
+ case 'q': {
+ ctrlX = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ ctrlY = cy + PApplet.parseFloat(pathTokens[i + 2]);
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 3]);
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 4]);
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 5;
+ prevCurve = true;
+ }
+ break;
+
+ // T - quadratic curveto shorthand (absolute)
+ // The control point is assumed to be the reflection of the control
+ // point on the previous command relative to the current point.
+ case 'T': {
+ // If there is no previous command or if the previous command was
+ // not a Q, q, T or t, assume the control point is coincident
+ // with the current point.
+ if (!prevCurve) {
+ ctrlX = cx;
+ ctrlY = cy;
+ } else {
+ float ppx = vertices[vertexCount-2][X];
+ float ppy = vertices[vertexCount-2][Y];
+ float px = vertices[vertexCount-1][X];
+ float py = vertices[vertexCount-1][Y];
+ ctrlX = px + (px - ppx);
+ ctrlY = py + (py - ppy);
+ }
+ float endX = PApplet.parseFloat(pathTokens[i + 1]);
+ float endY = PApplet.parseFloat(pathTokens[i + 2]);
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 3;
+ prevCurve = true;
+ }
+ break;
+
+ // t - quadratic curveto shorthand (relative)
+ case 't': {
+ if (!prevCurve) {
+ ctrlX = cx;
+ ctrlY = cy;
+ } else {
+ float ppx = vertices[vertexCount-2][X];
+ float ppy = vertices[vertexCount-2][Y];
+ float px = vertices[vertexCount-1][X];
+ float py = vertices[vertexCount-1][Y];
+ ctrlX = px + (px - ppx);
+ ctrlY = py + (py - ppy);
+ }
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 1]);
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 2]);
+ //parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ parsePathQuadto(ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 3;
+ prevCurve = true;
+ }
+ break;
+
+ // A - elliptical arc to (absolute)
+ case 'A': {
+ float rx = PApplet.parseFloat(pathTokens[i + 1]);
+ float ry = PApplet.parseFloat(pathTokens[i + 2]);
+ float angle = PApplet.parseFloat(pathTokens[i + 3]);
+ boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
+ boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
+ float endX = PApplet.parseFloat(pathTokens[i + 6]);
+ float endY = PApplet.parseFloat(pathTokens[i + 7]);
+ parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 8;
+ prevCurve = true;
+ }
+ break;
+
+ // a - elliptical arc to (relative)
+ case 'a': {
+ float rx = PApplet.parseFloat(pathTokens[i + 1]);
+ float ry = PApplet.parseFloat(pathTokens[i + 2]);
+ float angle = PApplet.parseFloat(pathTokens[i + 3]);
+ boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
+ boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
+ float endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
+ float endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
+ parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
+ cx = endX;
+ cy = endY;
+ i += 8;
+ prevCurve = true;
+ }
+ break;
+
+ case 'Z':
+ case 'z':
+ // since closing the path, the 'current' point needs
+ // to return back to the last moveto location.
+ // http://code.google.com/p/processing/issues/detail?id=1058
+ cx = movetoX;
+ cy = movetoY;
+ close = true;
+ i++;
+ break;
+
+ default:
+ String parsed =
+ PApplet.join(PApplet.subset(pathTokens, 0, i), ",");
+ String unparsed =
+ PApplet.join(PApplet.subset(pathTokens, i), ",");
+ System.err.println("parsed: " + parsed);
+ System.err.println("unparsed: " + unparsed);
+ throw new RuntimeException("shape command not handled: " + pathTokens[i]);
+ }
+// prevCommand = c;
+ }
+ }
+
+
+// private void parsePathCheck(int num) {
+// if (vertexCount + num-1 >= vertices.length) {
+// //vertices = (float[][]) PApplet.expand(vertices);
+// float[][] temp = new float[vertexCount << 1][2];
+// System.arraycopy(vertices, 0, temp, 0, vertexCount);
+// vertices = temp;
+// }
+// }
+
+ private void parsePathVertex(float x, float y) {
+ if (vertexCount == vertices.length) {
+ //vertices = (float[][]) PApplet.expand(vertices);
+ float[][] temp = new float[vertexCount << 1][2];
+ System.arraycopy(vertices, 0, temp, 0, vertexCount);
+ vertices = temp;
+ }
+ vertices[vertexCount][X] = x;
+ vertices[vertexCount][Y] = y;
+ vertexCount++;
+ }
+
+
+ private void parsePathCode(int what) {
+ if (vertexCodeCount == vertexCodes.length) {
+ vertexCodes = PApplet.expand(vertexCodes);
+ }
+ vertexCodes[vertexCodeCount++] = what;
+ }
+
+
+ private void parsePathMoveto(float px, float py) {
+ if (vertexCount > 0) {
+ parsePathCode(BREAK);
+ }
+ parsePathCode(VERTEX);
+ parsePathVertex(px, py);
+ }
+
+
+ private void parsePathLineto(float px, float py) {
+ parsePathCode(VERTEX);
+ parsePathVertex(px, py);
+ }
+
+
+ private void parsePathCurveto(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3) {
+ parsePathCode(BEZIER_VERTEX);
+ parsePathVertex(x1, y1);
+ parsePathVertex(x2, y2);
+ parsePathVertex(x3, y3);
+ }
+
+// private void parsePathQuadto(float x1, float y1,
+// float cx, float cy,
+// float x2, float y2) {
+// //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2);
+//// parsePathCode(BEZIER_VERTEX);
+// parsePathCode(QUAD_BEZIER_VERTEX);
+// // x1/y1 already covered by last moveto, lineto, or curveto
+//
+// parsePathVertex(x1 + ((cx-x1)*2/3.0f), y1 + ((cy-y1)*2/3.0f));
+// parsePathVertex(x2 + ((cx-x2)*2/3.0f), y2 + ((cy-y2)*2/3.0f));
+// parsePathVertex(x2, y2);
+// }
+
+ private void parsePathQuadto(float cx, float cy,
+ float x2, float y2) {
+ //System.out.println("quadto: " + x1 + "," + y1 + " " + cx + "," + cy + " " + x2 + "," + y2);
+// parsePathCode(BEZIER_VERTEX);
+ parsePathCode(QUADRATIC_VERTEX);
+ // x1/y1 already covered by last moveto, lineto, or curveto
+ parsePathVertex(cx, cy);
+ parsePathVertex(x2, y2);
+ }
+
+
+ // Approximates elliptical arc by several bezier segments.
+ // Meets SVG standard requirements from:
+ // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
+ // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ // Based on arc to bezier curve equations from:
+ // http://www.spaceroots.org/documents/ellipse/node22.html
+ private void parsePathArcto(float x1, float y1,
+ float rx, float ry,
+ float angle,
+ boolean fa, boolean fs,
+ float x2, float y2) {
+ if (x1 == x2 && y1 == y2) return;
+ if (rx == 0 || ry == 0) { parsePathLineto(x2, y2); return; }
+
+ rx = PApplet.abs(rx); ry = PApplet.abs(ry);
+
+ float phi = PApplet.radians(((angle % 360) + 360) % 360);
+ float cosPhi = PApplet.cos(phi), sinPhi = PApplet.sin(phi);
+
+ float x1r = ( cosPhi * (x1 - x2) + sinPhi * (y1 - y2)) / 2;
+ float y1r = (-sinPhi * (x1 - x2) + cosPhi * (y1 - y2)) / 2;
+
+ float cxr, cyr;
+ {
+ float A = (x1r*x1r) / (rx*rx) + (y1r*y1r) / (ry*ry);
+ if (A > 1) {
+ // No solution, scale ellipse up according to SVG standard
+ float sqrtA = PApplet.sqrt(A);
+ rx *= sqrtA; cxr = 0;
+ ry *= sqrtA; cyr = 0;
+ } else {
+ float k = ((fa == fs) ? -1f : 1f) *
+ PApplet.sqrt((rx*rx * ry*ry) / ((rx*rx * y1r*y1r) + (ry*ry * x1r*x1r)) - 1f);
+ cxr = k * rx * y1r / ry;
+ cyr = -k * ry * x1r / rx;
+ }
+ }
+
+ float cx = cosPhi * cxr - sinPhi * cyr + (x1 + x2) / 2;
+ float cy = sinPhi * cxr + cosPhi * cyr + (y1 + y2) / 2;
+
+ float phi1, phiDelta;
+ {
+ float sx = ( x1r - cxr) / rx, sy = ( y1r - cyr) / ry;
+ float tx = (-x1r - cxr) / rx, ty = (-y1r - cyr) / ry;
+ phi1 = PApplet.atan2(sy, sx);
+ phiDelta = (((PApplet.atan2(ty, tx) - phi1) % TWO_PI) + TWO_PI) % TWO_PI;
+ if (!fs) phiDelta -= TWO_PI;
+ }
+
+ // One segment can not cover more that PI, less than PI/2 is
+ // recommended to avoid visible inaccuracies caused by rounding errors
+ int segmentCount = PApplet.ceil(PApplet.abs(phiDelta) / TWO_PI * 4);
+
+ float inc = phiDelta / segmentCount;
+ float a = PApplet.sin(inc) *
+ (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3;
+
+ float sinPhi1 = PApplet.sin(phi1), cosPhi1 = PApplet.cos(phi1);
+
+ float p1x = x1;
+ float p1y = y1;
+ float relq1x = a * (-rx * cosPhi * sinPhi1 - ry * sinPhi * cosPhi1);
+ float relq1y = a * (-rx * sinPhi * sinPhi1 + ry * cosPhi * cosPhi1);
+
+ for (int i = 0; i < segmentCount; i++) {
+ float eta = phi1 + (i + 1) * inc;
+ float sinEta = PApplet.sin(eta), cosEta = PApplet.cos(eta);
+
+ float p2x = cx + rx * cosPhi * cosEta - ry * sinPhi * sinEta;
+ float p2y = cy + rx * sinPhi * cosEta + ry * cosPhi * sinEta;
+ float relq2x = a * (-rx * cosPhi * sinEta - ry * sinPhi * cosEta);
+ float relq2y = a * (-rx * sinPhi * sinEta + ry * cosPhi * cosEta);
+
+ if (i == segmentCount - 1) { p2x = x2; p2y = y2; }
+
+ parsePathCode(BEZIER_VERTEX);
+ parsePathVertex(p1x + relq1x, p1y + relq1y);
+ parsePathVertex(p2x - relq2x, p2y - relq2y);
+ parsePathVertex(p2x, p2y);
+
+ p1x = p2x; relq1x = relq2x;
+ p1y = p2y; relq1y = relq2y;
+ }
+ }
+
+
+ /**
+ * Parse the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
+ * is rotated relative to the SVG definition, so parameters are rearranged
+ * here. More about the transformation matrices in
+ * this section
+ * of the SVG documentation.
+ * @param matrixStr text of the matrix param.
+ * @return a good old-fashioned PMatrix2D
+ */
+ static protected PMatrix2D parseTransform(String matrixStr) {
+ matrixStr = matrixStr.trim();
+ PMatrix2D outgoing = null;
+ int start = 0;
+ int stop = -1;
+ while ((stop = matrixStr.indexOf(')', start)) != -1) {
+ PMatrix2D m = parseSingleTransform(matrixStr.substring(start, stop+1));
+ if (outgoing == null) {
+ outgoing = m;
+ } else {
+ outgoing.apply(m);
+ }
+ start = stop + 1;
+ }
+ return outgoing;
+ }
+
+
+ static protected PMatrix2D parseSingleTransform(String matrixStr) {
+ //String[] pieces = PApplet.match(matrixStr, "^\\s*(\\w+)\\((.*)\\)\\s*$");
+ String[] pieces = PApplet.match(matrixStr, "[,\\s]*(\\w+)\\((.*)\\)");
+ if (pieces == null) {
+ System.err.println("Could not parse transform " + matrixStr);
+ return null;
+ }
+ float[] m = PApplet.parseFloat(PApplet.splitTokens(pieces[2], ", "));
+ if (pieces[1].equals("matrix")) {
+ return new PMatrix2D(m[0], m[2], m[4], m[1], m[3], m[5]);
+
+ } else if (pieces[1].equals("translate")) {
+ float tx = m[0];
+ float ty = (m.length == 2) ? m[1] : m[0];
+ return new PMatrix2D(1, 0, tx, 0, 1, ty);
+
+ } else if (pieces[1].equals("scale")) {
+ float sx = m[0];
+ float sy = (m.length == 2) ? m[1] : m[0];
+ return new PMatrix2D(sx, 0, 0, 0, sy, 0);
+
+ } else if (pieces[1].equals("rotate")) {
+ float angle = m[0];
+
+ if (m.length == 1) {
+ float c = PApplet.cos(angle);
+ float s = PApplet.sin(angle);
+ // SVG version is cos(a) sin(a) -sin(a) cos(a) 0 0
+ return new PMatrix2D(c, -s, 0, s, c, 0);
+
+ } else if (m.length == 3) {
+ PMatrix2D mat = new PMatrix2D(0, 1, m[1], 1, 0, m[2]);
+ mat.rotate(m[0]);
+ mat.translate(-m[1], -m[2]);
+ return mat;
+ }
+
+ } else if (pieces[1].equals("skewX")) {
+ return new PMatrix2D(1, 0, 1, PApplet.tan(m[0]), 0, 0);
+
+ } else if (pieces[1].equals("skewY")) {
+ return new PMatrix2D(1, 0, 1, 0, PApplet.tan(m[0]), 0);
+ }
+ return null;
+ }
+
+
+ protected void parseColors(XML properties) {
+ if (properties.hasAttribute("opacity")) {
+ String opacityText = properties.getString("opacity");
+ setOpacity(opacityText);
+ }
+
+ if (properties.hasAttribute("stroke")) {
+ String strokeText = properties.getString("stroke");
+ setColor(strokeText, false);
+ }
+
+ if (properties.hasAttribute("stroke-opacity")) {
+ String strokeOpacityText = properties.getString("stroke-opacity");
+ setStrokeOpacity(strokeOpacityText);
+ }
+
+ if (properties.hasAttribute("stroke-width")) {
+ // if NaN (i.e. if it's 'inherit') then default back to the inherit setting
+ String lineweight = properties.getString("stroke-width");
+ setStrokeWeight(lineweight);
+ }
+
+ if (properties.hasAttribute("stroke-linejoin")) {
+ String linejoin = properties.getString("stroke-linejoin");
+ setStrokeJoin(linejoin);
+ }
+
+ if (properties.hasAttribute("stroke-linecap")) {
+ String linecap = properties.getString("stroke-linecap");
+ setStrokeCap(linecap);
+ }
+
+ // fill defaults to black (though stroke defaults to "none")
+ // http://www.w3.org/TR/SVG/painting.html#FillProperties
+ if (properties.hasAttribute("fill")) {
+ String fillText = properties.getString("fill");
+ setColor(fillText, true);
+ }
+
+ if (properties.hasAttribute("fill-opacity")) {
+ String fillOpacityText = properties.getString("fill-opacity");
+ setFillOpacity(fillOpacityText);
+ }
+
+ if (properties.hasAttribute("style")) {
+ String styleText = properties.getString("style");
+ String[] styleTokens = PApplet.splitTokens(styleText, ";");
+
+ //PApplet.println(styleTokens);
+ for (int i = 0; i < styleTokens.length; i++) {
+ String[] tokens = PApplet.splitTokens(styleTokens[i], ":");
+ //PApplet.println(tokens);
+
+ tokens[0] = PApplet.trim(tokens[0]);
+
+ if (tokens[0].equals("fill")) {
+ setColor(tokens[1], true);
+
+ } else if(tokens[0].equals("fill-opacity")) {
+ setFillOpacity(tokens[1]);
+
+ } else if(tokens[0].equals("stroke")) {
+ setColor(tokens[1], false);
+
+ } else if(tokens[0].equals("stroke-width")) {
+ setStrokeWeight(tokens[1]);
+
+ } else if(tokens[0].equals("stroke-linecap")) {
+ setStrokeCap(tokens[1]);
+
+ } else if(tokens[0].equals("stroke-linejoin")) {
+ setStrokeJoin(tokens[1]);
+
+ } else if(tokens[0].equals("stroke-opacity")) {
+ setStrokeOpacity(tokens[1]);
+
+ } else if(tokens[0].equals("opacity")) {
+ setOpacity(tokens[1]);
+
+ } else {
+ // Other attributes are not yet implemented
+ }
+ }
+ }
+ }
+
+
+ void setOpacity(String opacityText) {
+ opacity = PApplet.parseFloat(opacityText);
+ strokeColor = ((int) (opacity * 255)) << 24 | strokeColor & 0xFFFFFF;
+ fillColor = ((int) (opacity * 255)) << 24 | fillColor & 0xFFFFFF;
+ }
+
+
+ void setStrokeWeight(String lineweight) {
+ strokeWeight = parseUnitSize(lineweight, svgSizeXY);
+ }
+
+
+ void setStrokeOpacity(String opacityText) {
+ strokeOpacity = PApplet.parseFloat(opacityText);
+ strokeColor = ((int) (strokeOpacity * 255)) << 24 | strokeColor & 0xFFFFFF;
+ }
+
+
+ void setStrokeJoin(String linejoin) {
+ if (linejoin.equals("inherit")) {
+ // do nothing, will inherit automatically
+
+ } else if (linejoin.equals("miter")) {
+ strokeJoin = PConstants.MITER;
+
+ } else if (linejoin.equals("round")) {
+ strokeJoin = PConstants.ROUND;
+
+ } else if (linejoin.equals("bevel")) {
+ strokeJoin = PConstants.BEVEL;
+ }
+ }
+
+
+ void setStrokeCap(String linecap) {
+ if (linecap.equals("inherit")) {
+ // do nothing, will inherit automatically
+
+ } else if (linecap.equals("butt")) {
+ strokeCap = PConstants.SQUARE;
+
+ } else if (linecap.equals("round")) {
+ strokeCap = PConstants.ROUND;
+
+ } else if (linecap.equals("square")) {
+ strokeCap = PConstants.PROJECT;
+ }
+ }
+
+
+ void setFillOpacity(String opacityText) {
+ fillOpacity = PApplet.parseFloat(opacityText);
+ fillColor = ((int) (fillOpacity * 255)) << 24 | fillColor & 0xFFFFFF;
+ }
+
+
+ void setColor(String colorText, boolean isFill) {
+ colorText = colorText.trim();
+ int opacityMask = fillColor & 0xFF000000;
+ boolean visible = true;
+ int color = 0;
+ String name = "";
+// String lColorText = colorText.toLowerCase();
+ Gradient gradient = null;
+// Object paint = null;
+ if (colorText.equals("none")) {
+ visible = false;
+ } else if (colorText.startsWith("url(#")) {
+ name = colorText.substring(5, colorText.length() - 1);
+ Object object = findChild(name);
+ if (object instanceof Gradient) {
+ gradient = (Gradient) object;
+ // in 3.0a11, do this on first draw inside PShapeJava2D
+// paint = calcGradientPaint(gradient); //, opacity);
+ } else {
+// visible = false;
+ System.err.println("url " + name + " refers to unexpected data: " + object);
+ }
+ } else {
+ // Prints errors itself.
+ color = opacityMask | parseSimpleColor(colorText);
+ }
+ if (isFill) {
+ fill = visible;
+ fillColor = color;
+ fillName = name;
+ fillGradient = gradient;
+// fillGradientPaint = paint;
+ } else {
+ stroke = visible;
+ strokeColor = color;
+ strokeName = name;
+ strokeGradient = gradient;
+// strokeGradientPaint = paint;
+ }
+ }
+
+
+ /**
+ * Parses the "color" datatype only, and prints an error if it is not of this form.
+ * http://www.w3.org/TR/SVG/types.html#DataTypeColor
+ * @return 0xRRGGBB (no alpha). Zero on error.
+ */
+ static protected int parseSimpleColor(String colorText) {
+ colorText = colorText.toLowerCase().trim();
+ //if (colorNames.containsKey(colorText)) {
+ if (colorNames.hasKey(colorText)) {
+ return colorNames.get(colorText);
+ } else if (colorText.startsWith("#")) {
+ if (colorText.length() == 4) {
+ // Short form: #ABC, transform to long form #AABBCC
+ colorText = colorText.replaceAll("^#(.)(.)(.)$", "#$1$1$2$2$3$3");
+ }
+ return (Integer.parseInt(colorText.substring(1), 16)) & 0xFFFFFF;
+ //System.out.println("hex for fill is " + PApplet.hex(fillColor));
+ } else if (colorText.startsWith("rgb")) {
+ return parseRGB(colorText);
+ } else {
+ System.err.println("Cannot parse \"" + colorText + "\".");
+ return 0;
+ }
+ }
+
+
+ /**
+ * Deliberately conforms to the HTML 4.01 color spec + en-gb grey, rather
+ * than the (unlikely to be useful) entire 147-color system used in SVG.
+ */
+ static protected IntDict colorNames = new IntDict(new Object[][] {
+ { "aqua", 0x00ffff },
+ { "black", 0x000000 },
+ { "blue", 0x0000ff },
+ { "fuchsia", 0xff00ff },
+ { "gray", 0x808080 },
+ { "grey", 0x808080 },
+ { "green", 0x008000 },
+ { "lime", 0x00ff00 },
+ { "maroon", 0x800000 },
+ { "navy", 0x000080 },
+ { "olive", 0x808000 },
+ { "purple", 0x800080 },
+ { "red", 0xff0000 },
+ { "silver", 0xc0c0c0 },
+ { "teal", 0x008080 },
+ { "white", 0xffffff },
+ { "yellow", 0xffff00 }
+ });
+
+ /*
+ static protected Map colorNames;
+ static {
+ colorNames = new HashMap();
+ colorNames.put("aqua", 0x00ffff);
+ colorNames.put("black", 0x000000);
+ colorNames.put("blue", 0x0000ff);
+ colorNames.put("fuchsia", 0xff00ff);
+ colorNames.put("gray", 0x808080);
+ colorNames.put("grey", 0x808080);
+ colorNames.put("green", 0x008000);
+ colorNames.put("lime", 0x00ff00);
+ colorNames.put("maroon", 0x800000);
+ colorNames.put("navy", 0x000080);
+ colorNames.put("olive", 0x808000);
+ colorNames.put("purple", 0x800080);
+ colorNames.put("red", 0xff0000);
+ colorNames.put("silver", 0xc0c0c0);
+ colorNames.put("teal", 0x008080);
+ colorNames.put("white", 0xffffff);
+ colorNames.put("yellow", 0xffff00);
+ }
+ */
+
+ static protected int parseRGB(String what) {
+ int leftParen = what.indexOf('(') + 1;
+ int rightParen = what.indexOf(')');
+ String sub = what.substring(leftParen, rightParen);
+ String[] values = PApplet.splitTokens(sub, ", ");
+ int rgbValue = 0;
+ if (values.length == 3) {
+ // Color spec allows for rgb values to be percentages.
+ for (int i = 0; i < 3; i++) {
+ rgbValue <<= 8;
+ if (values[i].endsWith("%")) {
+ rgbValue |= (int)(PApplet.constrain(255*parseFloatOrPercent(values[i]), 0, 255));
+ } else {
+ rgbValue |= PApplet.constrain(PApplet.parseInt(values[i]), 0, 255);
+ }
+ }
+ } else System.err.println("Could not read color \"" + what + "\".");
+
+ return rgbValue;
+ }
+
+
+ //static protected Map parseStyleAttributes(String style) {
+ static protected StringDict parseStyleAttributes(String style) {
+ //Map table = new HashMap();
+ StringDict table = new StringDict();
+// if (style == null) return table;
+ if (style != null) {
+ String[] pieces = style.split(";");
+ for (int i = 0; i < pieces.length; i++) {
+ String[] parts = pieces[i].split(":");
+ //table.put(parts[0], parts[1]);
+ table.set(parts[0], parts[1]);
+ }
+ }
+ return table;
+ }
+
+
+ /**
+ * Used in place of element.getFloatAttribute(a) because we can
+ * have a unit suffix (length or coordinate).
+ * @param element what to parse
+ * @param attribute name of the attribute to get
+ * @param relativeTo (float) Used for %. When relative to viewbox, should
+ * be svgWidth for horizontal dimentions, svgHeight for vertical, and
+ * svgXYSize for anything else.
+ * @return unit-parsed version of the data
+ */
+ static protected float getFloatWithUnit(XML element, String attribute, float relativeTo) {
+ String val = element.getString(attribute);
+ return (val == null) ? 0 : parseUnitSize(val, relativeTo);
+ }
+
+
+ /**
+ * Parse a size that may have a suffix for its units.
+ * This assumes 90dpi, which implies, as given in the
+ * units spec:
+ *
+ * "1pt" equals "1.25px" (and therefore 1.25 user units)
+ * "1pc" equals "15px" (and therefore 15 user units)
+ * "1mm" would be "3.543307px" (3.543307 user units)
+ * "1cm" equals "35.43307px" (and therefore 35.43307 user units)
+ * "1in" equals "90px" (and therefore 90 user units)
+ *
+ * @param relativeTo (float) Used for %. When relative to viewbox, should
+ * be svgWidth for horizontal dimentions, svgHeight for vertical, and
+ * svgXYSize for anything else.
+ */
+ static protected float parseUnitSize(String text, float relativeTo) {
+ int len = text.length() - 2;
+
+ if (text.endsWith("pt")) {
+ return PApplet.parseFloat(text.substring(0, len)) * 1.25f;
+ } else if (text.endsWith("pc")) {
+ return PApplet.parseFloat(text.substring(0, len)) * 15;
+ } else if (text.endsWith("mm")) {
+ return PApplet.parseFloat(text.substring(0, len)) * 3.543307f;
+ } else if (text.endsWith("cm")) {
+ return PApplet.parseFloat(text.substring(0, len)) * 35.43307f;
+ } else if (text.endsWith("in")) {
+ return PApplet.parseFloat(text.substring(0, len)) * 90;
+ } else if (text.endsWith("px")) {
+ return PApplet.parseFloat(text.substring(0, len));
+ } else if (text.endsWith("%")) {
+ return relativeTo * parseFloatOrPercent(text);
+ } else {
+ return PApplet.parseFloat(text);
+ }
+ }
+
+
+ static protected float parseFloatOrPercent(String text) {
+ text = text.trim();
+ if (text.endsWith("%")) {
+ return Float.parseFloat(text.substring(0, text.length() - 1)) / 100.0f;
+ } else {
+ return Float.parseFloat(text);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ static public class Gradient extends PShapeSVG {
+ Matrix transform;
+
+ public float[] offset;
+ public int[] color;
+ public int count;
+
+ public Gradient(PShapeSVG parent, XML properties) {
+ super(parent, properties, true);
+
+ XML elements[] = properties.getChildren();
+ offset = new float[elements.length];
+ color = new int[elements.length];
+
+ //
+ for (int i = 0; i < elements.length; i++) {
+ XML elem = elements[i];
+ String name = elem.getName();
+ if (name.equals("stop")) {
+ String offsetAttr = elem.getString("offset");
+ offset[count] = parseFloatOrPercent(offsetAttr);
+
+ String style = elem.getString("style");
+ //Map styles = parseStyleAttributes(style);
+ StringDict styles = parseStyleAttributes(style);
+
+ String colorStr = styles.get("stop-color");
+ if (colorStr == null) {
+ colorStr = elem.getString("stop-color");
+ if (colorStr == null) colorStr = "#000000";
+ }
+ String opacityStr = styles.get("stop-opacity");
+ if (opacityStr == null) {
+ opacityStr = elem.getString("stop-opacity");
+ if (opacityStr == null) opacityStr = "1";
+ }
+ int tupacity = PApplet.constrain(
+ (int)(PApplet.parseFloat(opacityStr) * 255), 0, 255);
+ color[count] = (tupacity << 24) | parseSimpleColor(colorStr);
+ count++;
+ }
+ }
+ offset = PApplet.subset(offset, 0, count);
+ color = PApplet.subset(color, 0, count);
+ }
+ }
+
+
+ public class LinearGradient extends Gradient {
+ public float x1, y1, x2, y2;
+
+ public LinearGradient(PShapeSVG parent, XML properties) {
+ super(parent, properties);
+
+ this.x1 = getFloatWithUnit(properties, "x1", svgWidth);
+ this.y1 = getFloatWithUnit(properties, "y1", svgHeight);
+ this.x2 = getFloatWithUnit(properties, "x2", svgWidth);
+ this.y2 = getFloatWithUnit(properties, "y2", svgHeight);
+
+ String transformStr =
+ properties.getString("gradientTransform");
+
+ if (transformStr != null) {
+ float t[] = parseTransform(transformStr).get(null);
+ //this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]);
+ transform = new Matrix();
+ transform.setValues(new float[] { // TODO don't create temp floats
+ t[0], t[1], t[2],
+ t[3], t[4], t[5],
+ 0, 0, 1
+ });
+
+// Point2D t1 = transform.transform(new Point2D.Float(x1, y1), null);
+// Point2D t2 = transform.transform(new Point2D.Float(x2, y2), null);
+ float[] t1 = new float[] { x1, y1 };
+ float[] t2 = new float[] { x2, y2 };
+ transform.mapPoints(t1);
+ transform.mapPoints(t2);
+
+// this.x1 = (float) t1.getX();
+// this.y1 = (float) t1.getY();
+// this.x2 = (float) t2.getX();
+// this.y2 = (float) t2.getY();
+ x1 = t1[0];
+ y1 = t1[1];
+ x2 = t2[0];
+ y2 = t2[1];
+ }
+ }
+ }
+
+
+ public class RadialGradient extends Gradient {
+ public float cx, cy, r;
+
+ public RadialGradient(PShapeSVG parent, XML properties) {
+ super(parent, properties);
+
+ this.cx = getFloatWithUnit(properties, "cx", svgWidth);
+ this.cy = getFloatWithUnit(properties, "cy", svgHeight);
+ this.r = getFloatWithUnit(properties, "r", svgSizeXY);
+
+ String transformStr =
+ properties.getString("gradientTransform");
+
+ if (transformStr != null) {
+ float t[] = parseTransform(transformStr).get(null);
+// this.transform = new AffineTransform(t[0], t[3], t[1], t[4], t[2], t[5]);
+ transform = new Matrix();
+ transform.setValues(new float[] { // TODO don't create temp floats
+ t[0], t[1], t[2],
+ t[3], t[4], t[5],
+ 0, 0, 1
+ });
+
+// Point2D t1 = transform.transform(new Point2D.Float(cx, cy), null);
+// Point2D t2 = transform.transform(new Point2D.Float(cx + r, cy), null);
+ float[] t1 = new float[] { cx, cy };
+ float[] t2 = new float[] { cx + r, cy };
+ transform.mapPoints(t1);
+ transform.mapPoints(t2);
+
+// this.cx = (float) t1.getX();
+// this.cy = (float) t1.getY();
+// this.r = (float) (t2.getX() - t1.getX());
+ cx = t1[0];
+ cy = t1[1];
+ r = t2[0] - t1[0];
+ }
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+// static private float TEXT_QUALITY = 1;
+ public static final int PLAIN = 0;
+ public static final int BOLD = 1;
+ public static final int ITALIC = 2;
+
+ static private PFont parseFont(XML properties) {
+ String fontFamily = null;
+ float size = 10;
+ int weight = PLAIN; // 0
+ int italic = 0;
+
+ if (properties.hasAttribute("style")) {
+ String styleText = properties.getString("style");
+ String[] styleTokens = PApplet.splitTokens(styleText, ";");
+
+ //PApplet.println(styleTokens);
+ for (int i = 0; i < styleTokens.length; i++) {
+ String[] tokens = PApplet.splitTokens(styleTokens[i], ":");
+ //PApplet.println(tokens);
+
+ tokens[0] = PApplet.trim(tokens[0]);
+
+ if (tokens[0].equals("font-style")) {
+ // PApplet.println("font-style: " + tokens[1]);
+ if (tokens[1].contains("italic")) {
+ italic = ITALIC;
+ }
+ } else if (tokens[0].equals("font-variant")) {
+ // PApplet.println("font-variant: " + tokens[1]);
+ // setFillOpacity(tokens[1]);
+
+ } else if (tokens[0].equals("font-weight")) {
+ // PApplet.println("font-weight: " + tokens[1]);
+
+ if (tokens[1].contains("bold")) {
+ weight = BOLD;
+ // PApplet.println("Bold weight ! ");
+ }
+
+
+ } else if (tokens[0].equals("font-stretch")) {
+ // not supported.
+
+ } else if (tokens[0].equals("font-size")) {
+ // PApplet.println("font-size: " + tokens[1]);
+ size = Float.parseFloat(tokens[1].split("px")[0]);
+ // PApplet.println("font-size-parsed: " + size);
+ } else if (tokens[0].equals("line-height")) {
+ // not supported
+
+ } else if (tokens[0].equals("font-family")) {
+ // PApplet.println("Font-family: " + tokens[1]);
+ fontFamily = tokens[1];
+
+ } else if (tokens[0].equals("text-align")) {
+ // not supported
+
+ } else if (tokens[0].equals("letter-spacing")) {
+ // not supported
+
+ } else if (tokens[0].equals("word-spacing")) {
+ // not supported
+
+ } else if (tokens[0].equals("writing-mode")) {
+ // not supported
+
+ } else if (tokens[0].equals("text-anchor")) {
+ // not supported
+
+ } else {
+ // Other attributes are not yet implemented
+ }
+ }
+ }
+ if (fontFamily == null) {
+ return null;
+ }
+// size = size * TEXT_QUALITY;
+
+ return createFont(fontFamily, weight | italic, size, true);
+ }
+
+
+ static protected PFont createFont(String name, int weight,
+ float size, boolean smooth) {
+ //System.out.println("Try to create a font of " + name + " family, " + weight);
+// java.awt.Font baseFont = new java.awt.Font(name, weight, (int) size);
+
+ //System.out.println("Resulting family : " + baseFont.getFamily() + " " + baseFont.getStyle());
+// return new PFont(baseFont.deriveFont(size), smooth, null);
+ return null;
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ static public class Text extends PShapeSVG {
+ protected PFont font;
+
+ public Text(PShapeSVG parent, XML properties) {
+ super(parent, properties, true);
+
+ // get location
+ float x = Float.parseFloat(properties.getString("x"));
+ float y = Float.parseFloat(properties.getString("y"));
+
+ if (matrix == null) {
+ matrix = new PMatrix2D();
+ }
+ matrix.translate(x, y);
+
+ family = GROUP;
+
+ font = parseFont(properties);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ static public class LineOfText extends PShapeSVG {
+ String textToDisplay;
+ PFont font;
+
+ public LineOfText(PShapeSVG parent, XML properties) {
+ // TODO: child should ideally be parsed too for inline content.
+ super(parent, properties, false);
+
+ //get location
+ float x = Float.parseFloat(properties.getString("x"));
+ float y = Float.parseFloat(properties.getString("y"));
+
+ float parentX = Float.parseFloat(parent.element.getString("x"));
+ float parentY = Float.parseFloat(parent.element.getString("y"));
+
+ if (matrix == null) matrix = new PMatrix2D();
+ matrix.translate(x - parentX, (y - parentY) / 2f);
+
+ // get the first properties
+ parseColors(properties);
+ font = parseFont(properties);
+
+ // cleaned up syntax but removing b/c unused [fry 190118]
+ //boolean isLine = properties.getString("role").equals("line");
+
+ if (this.childCount > 0) {
+ // no inline content yet.
+ }
+
+ String text = properties.getContent();
+ textToDisplay = text;
+ }
+
+ @Override
+ public void drawImpl(PGraphics g) {
+ if (font == null) {
+ font = ((Text) parent).font;
+ if (font == null) {
+ return;
+ }
+ }
+
+ pre(g);
+// g.textFont(font, font.size / TEXT_QUALITY);
+ g.textFont(font, font.size);
+ g.text(textToDisplay, 0, 0);
+ post(g);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ static public class Font extends PShapeSVG {
+ public FontFace face;
+
+ public Map namedGlyphs;
+ public Map unicodeGlyphs;
+
+ public int glyphCount;
+ public FontGlyph[] glyphs;
+ public FontGlyph missingGlyph;
+
+ int horizAdvX;
+
+
+ public Font(PShapeSVG parent, XML properties) {
+ super(parent, properties, false);
+// handle(parent, properties);
+
+ XML[] elements = properties.getChildren();
+
+ horizAdvX = properties.getInt("horiz-adv-x", 0);
+
+ namedGlyphs = new HashMap<>();
+ unicodeGlyphs = new HashMap<>();
+ glyphCount = 0;
+ glyphs = new FontGlyph[elements.length];
+
+ for (int i = 0; i < elements.length; i++) {
+ String name = elements[i].getName();
+ XML elem = elements[i];
+ if (name == null) {
+ // skip it
+ } else if (name.equals("glyph")) {
+ FontGlyph fg = new FontGlyph(this, elem, this);
+ if (fg.isLegit()) {
+ if (fg.name != null) {
+ namedGlyphs.put(fg.name, fg);
+ }
+ if (fg.unicode != 0) {
+ unicodeGlyphs.put(Character.valueOf(fg.unicode), fg);
+ }
+ }
+ glyphs[glyphCount++] = fg;
+
+ } else if (name.equals("missing-glyph")) {
+// System.out.println("got missing glyph inside ");
+ missingGlyph = new FontGlyph(this, elem, this);
+ } else if (name.equals("font-face")) {
+ face = new FontFace(this, elem);
+ } else {
+ System.err.println("Ignoring " + name + " inside ");
+ }
+ }
+ }
+
+
+ protected void drawShape() {
+ // does nothing for fonts
+ }
+
+
+ public void drawString(PGraphics g, String str, float x, float y, float size) {
+ // 1) scale by the 1.0/unitsPerEm
+ // 2) scale up by a font size
+ g.pushMatrix();
+ float s = size / face.unitsPerEm;
+ //System.out.println("scale is " + s);
+ // swap y coord at the same time, since fonts have y=0 at baseline
+ g.translate(x, y);
+ g.scale(s, -s);
+ char[] c = str.toCharArray();
+ for (int i = 0; i < c.length; i++) {
+ // call draw on each char (pulling it w/ the unicode table)
+ FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i]));
+ if (fg != null) {
+ fg.draw(g);
+ // add horizAdvX/unitsPerEm to the x coordinate along the way
+ g.translate(fg.horizAdvX, 0);
+ } else {
+ System.err.println("'" + c[i] + "' not available.");
+ }
+ }
+ g.popMatrix();
+ }
+
+
+ public void drawChar(PGraphics g, char c, float x, float y, float size) {
+ g.pushMatrix();
+ float s = size / face.unitsPerEm;
+ g.translate(x, y);
+ g.scale(s, -s);
+ FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c));
+ if (fg != null) g.shape(fg);
+ g.popMatrix();
+ }
+
+
+ public float textWidth(String str, float size) {
+ float w = 0;
+ char[] c = str.toCharArray();
+ for (int i = 0; i < c.length; i++) {
+ // call draw on each char (pulling it w/ the unicode table)
+ FontGlyph fg = unicodeGlyphs.get(Character.valueOf(c[i]));
+ if (fg != null) {
+ w += (float) fg.horizAdvX / face.unitsPerEm;
+ }
+ }
+ return w * size;
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ static class FontFace extends PShapeSVG {
+ int horizOriginX; // dflt 0
+ int horizOriginY; // dflt 0
+ // int horizAdvX; // no dflt?
+ int vertOriginX; // dflt horizAdvX/2
+ int vertOriginY; // dflt ascent
+ int vertAdvY; // dflt 1em (unitsPerEm value)
+
+ String fontFamily;
+ int fontWeight; // can also be normal or bold (also comma separated)
+ String fontStretch;
+ int unitsPerEm; // dflt 1000
+ int[] panose1; // dflt "0 0 0 0 0 0 0 0 0 0"
+ int ascent;
+ int descent;
+ int[] bbox; // spec says comma separated, tho not w/ forge
+ int underlineThickness;
+ int underlinePosition;
+ //String unicodeRange; // gonna ignore for now
+
+
+ public FontFace(PShapeSVG parent, XML properties) {
+ super(parent, properties, true);
+
+ unitsPerEm = properties.getInt("units-per-em", 1000);
+ }
+
+
+ protected void drawShape() {
+ // nothing to draw in the font face attribute
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ static public class FontGlyph extends PShapeSVG { // extends Path
+ public String name;
+ char unicode;
+ int horizAdvX;
+
+ public FontGlyph(PShapeSVG parent, XML properties, Font font) {
+ super(parent, properties, true);
+ super.parsePath(); // ??
+
+ name = properties.getString("glyph-name");
+ String u = properties.getString("unicode");
+ unicode = 0;
+ if (u != null) {
+ if (u.length() == 1) {
+ unicode = u.charAt(0);
+ //System.out.println("unicode for " + name + " is " + u);
+ } else {
+ System.err.println("unicode for " + name +
+ " is more than one char: " + u);
+ }
+ }
+ if (properties.hasAttribute("horiz-adv-x")) {
+ horizAdvX = properties.getInt("horiz-adv-x");
+ } else {
+ horizAdvX = font.horizAdvX;
+ }
+ }
+
+
+ protected boolean isLegit() { // TODO need a better way to handle this...
+ return vertexCount != 0;
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ /**
+ * Get a particular element based on its SVG ID. When editing SVG by hand,
+ * this is the id="" tag on any SVG element. When editing from Illustrator,
+ * these IDs can be edited by expanding the layers palette. The names used
+ * in the layers palette, both for the layers or the shapes and groups
+ * beneath them can be used here.
+ *
+ * // This code grabs "Layer 3" and the shapes beneath it.
+ * PShape layer3 = svg.getChild("Layer 3");
+ *
+ */
+ @Override
+ public PShape getChild(String name) {
+ PShape found = super.getChild(name);
+ if (found == null) {
+ // Otherwise try with underscores instead of spaces
+ // (this is how Illustrator handles spaces in the layer names).
+ found = super.getChild(name.replace(' ', '_'));
+ }
+ // Set bounding box based on the parent bounding box
+ if (found != null) {
+// found.x = this.x;
+// found.y = this.y;
+ found.width = this.width;
+ found.height = this.height;
+ }
+ return found;
+ }
+
+
+ /**
+ * Prints out the SVG document. Useful for parsing.
+ */
+ public void print() {
+ PApplet.println(element.toString());
+ }
+}
diff --git a/core/src/processing/core/PStyle.java b/libs/processing-core/src/main/java/processing/core/PStyle.java
similarity index 91%
rename from core/src/processing/core/PStyle.java
rename to libs/processing-core/src/main/java/processing/core/PStyle.java
index f7f388950..f5e1ecffb 100644
--- a/core/src/processing/core/PStyle.java
+++ b/libs/processing-core/src/main/java/processing/core/PStyle.java
@@ -3,7 +3,9 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2008-10 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2006-12 Ben Fry and Casey Reas
+ Copyright (c) 2004-06 Michael Chang
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -29,6 +31,8 @@ public class PStyle implements PConstants {
public int ellipseMode;
public int shapeMode;
+ public int blendMode;
+
public int colorMode;
public float colorModeX;
public float colorModeY;
diff --git a/libs/processing-core/src/main/java/processing/core/PSurface.java b/libs/processing-core/src/main/java/processing/core/PSurface.java
new file mode 100644
index 000000000..4a2ec763b
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/core/PSurface.java
@@ -0,0 +1,108 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.core;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.AssetManager;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.io.File;
+import java.io.InputStream;
+
+import processing.android.AppComponent;
+import processing.android.ServiceEngine;
+
+/*
+ * Holds the surface view associated with the sketch, and the rendering thread
+ * handling
+ */
+public interface PSurface {
+ public static final int REQUEST_PERMISSIONS = 1;
+
+ public AppComponent getComponent();
+ public Context getContext();
+ public Activity getActivity();
+ public ServiceEngine getEngine();
+
+ public void dispose();
+
+ public String getName();
+
+ public View getResource(int id);
+
+ public Rect getVisibleFrame();
+
+ public SurfaceView getSurfaceView();
+ public SurfaceHolder getSurfaceHolder();
+
+ public View getRootView();
+ public void setRootView(View view);
+
+ public void initView(int sketchWidth, int sketchHeight);
+ public void initView(int sketchWidth, int sketchHeight, boolean parentSize,
+ LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState);
+
+ public void startActivity(Intent intent);
+
+ public void runOnUiThread(Runnable action);
+
+ public void setOrientation(int which);
+
+ public void setHasOptionsMenu(boolean hasMenu);
+
+ public File getFilesDir();
+
+ public File getFileStreamPath(String path);
+
+ public InputStream openFileInput(String filename);
+
+ public AssetManager getAssets();
+
+ public void setSystemUiVisibility(int visibility);
+
+ public void startThread();
+
+ public void pauseThread();
+
+ public void resumeThread();
+
+ public boolean stopThread();
+
+ public boolean isStopped();
+
+ public void finish();
+
+ public void setFrameRate(float fps);
+
+ public boolean hasPermission(String permission);
+ public void requestPermissions(String[] permissions);
+}
diff --git a/libs/processing-core/src/main/java/processing/core/PSurfaceNone.java b/libs/processing-core/src/main/java/processing/core/PSurfaceNone.java
new file mode 100644
index 000000000..5858ed1f7
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/core/PSurfaceNone.java
@@ -0,0 +1,595 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.core;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.LayoutInflater;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.os.ResultReceiver;
+
+import android.service.wallpaper.WallpaperService;
+import android.support.wearable.watchface.WatchFaceService;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import processing.android.AppComponent;
+import processing.android.PFragment;
+import processing.android.ServiceEngine;
+import processing.android.PermissionRequestor;
+
+/**
+ * Base surface for Android2D and OpenGL renderers.
+ * It includes the implementation of the rendering thread.
+ */
+public class PSurfaceNone implements PSurface, PConstants {
+ protected PApplet sketch;
+ protected PGraphics graphics;
+ protected AppComponent component;
+ protected Activity activity;
+
+ protected boolean surfaceReady;
+ protected SurfaceView surfaceView;
+ protected View view;
+
+ protected WallpaperService wallpaper;
+ protected WatchFaceService watchface;
+
+ protected boolean requestedThreadStart = false;
+ protected Thread thread;
+ protected boolean paused;
+ protected final Object pauseObject = new Object();
+
+ protected float frameRateTarget = 60;
+ protected long frameRatePeriod = 1000000000L / 60L;
+
+
+ @Override
+ public AppComponent getComponent() {
+ return component;
+ }
+
+
+ @Override
+ public Context getContext() {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ return activity;
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ return wallpaper;
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ return watchface;
+ }
+ return null;
+ }
+
+
+ @Override
+ public Activity getActivity() {
+ return activity;
+ }
+
+
+ @Override
+ public ServiceEngine getEngine() {
+ return component.getEngine();
+ }
+
+
+ @Override
+ public View getRootView() {
+ return view;
+ }
+
+
+ @Override
+ public String getName() {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ return activity.getComponentName().getPackageName();
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ return wallpaper.getPackageName();
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ return watchface.getPackageName();
+ }
+ return "";
+ }
+
+
+ @Override
+ public View getResource(int id) {
+ return activity.findViewById(id);
+ }
+
+
+ @Override
+ public Rect getVisibleFrame() {
+ Rect frame = new Rect();
+ if (view != null) {
+ // According to the docs:
+ // https://developer.android.com/reference/android/view/View.html#getWindowVisibleDisplayFrame(android.graphics.Rect)
+ // don't use in performance critical code like drawing.
+ view.getWindowVisibleDisplayFrame(frame);
+ }
+ return frame;
+ }
+
+
+ @Override
+ public void dispose() {
+ sketch = null;
+ graphics = null;
+
+ if (activity != null) {
+ // In API level 21 you can do
+ // activity.releaseInstance();
+ // to ask the app to free up its memory.
+ // https://developer.android.com/reference/android/app/Activity.html#releaseInstance()
+ // but seems redundant to call it here, since dispose() is triggered by
+ // the onDestroy() handler, which means that the app is already
+ // being destroyed.
+ }
+
+ if (view != null) {
+ view.destroyDrawingCache();
+ }
+
+ if (component != null) {
+ component.dispose();
+ }
+
+ if (surfaceView != null) {
+ surfaceView.getHolder().getSurface().release();
+ }
+ }
+
+
+ @Override
+ public void setRootView(View view) {
+ this.view = view;
+ }
+
+
+ @Override
+ public SurfaceView getSurfaceView() {
+ return surfaceView;
+ }
+
+
+ @Override
+ // TODO this is only used by A2D, when finishing up a draw. but if the
+ // surfaceview has changed, then it might belong to an a3d surfaceview. hrm.
+ public SurfaceHolder getSurfaceHolder() {
+ SurfaceView view = getSurfaceView();
+ if (view == null) {
+ // Watch faces don't have a surface view associated to them.
+ return null;
+ } else {
+ return view.getHolder();
+ }
+ }
+
+
+ @Override
+ public void initView(int sketchWidth, int sketchHeight) {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ int displayWidth = component.getDisplayWidth();
+ int displayHeight = component.getDisplayHeight();
+ View rootView;
+ if (sketchWidth == displayWidth && sketchHeight == displayHeight) {
+ rootView = getSurfaceView();
+ } else {
+ RelativeLayout overallLayout = new RelativeLayout(activity);
+ RelativeLayout.LayoutParams lp =
+ new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.addRule(RelativeLayout.CENTER_IN_PARENT);
+
+ LinearLayout layout = new LinearLayout(activity);
+ layout.addView(getSurfaceView(), sketchWidth, sketchHeight);
+ overallLayout.addView(layout, lp);
+ overallLayout.setBackgroundColor(sketch.sketchWindowColor());
+ rootView = overallLayout;
+ }
+ setRootView(rootView);
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ setRootView(getSurfaceView());
+ }
+ }
+
+
+ @Override
+ public void initView(int sketchWidth, int sketchHeight, boolean parentSize,
+ LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
+ ViewGroup rootView = (ViewGroup)inflater.inflate(sketch.parentLayout, container, false);
+
+ View view = getSurfaceView();
+ if (parentSize) {
+ LinearLayout.LayoutParams lp;
+ lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ lp.weight = 1.0f;
+ lp.setMargins(0, 0, 0, 0);
+ view.setPadding(0,0,0,0);
+ rootView.addView(view, lp);
+ } else {
+ RelativeLayout layout = new RelativeLayout(activity);
+ RelativeLayout.LayoutParams lp =
+ new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.addRule(RelativeLayout.CENTER_IN_PARENT);
+
+ layout.addView(view, sketchWidth, sketchHeight);
+ rootView.addView(layout, lp);
+ }
+ rootView.setBackgroundColor(sketch.sketchWindowColor());
+ setRootView(rootView);
+ }
+
+
+ @Override
+ public void startActivity(Intent intent) {
+ component.startActivity(intent);
+ }
+
+
+ @Override
+ public void runOnUiThread(Runnable action) {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ activity.runOnUiThread(action);
+ }
+ }
+
+
+ @Override
+ public void setOrientation(int which) {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ if (which == PORTRAIT) {
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ } else if (which == LANDSCAPE) {
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+ }
+ }
+
+
+ public void setHasOptionsMenu(boolean hasMenu) {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ ((PFragment)component).setHasOptionsMenu(hasMenu);
+ }
+ }
+
+
+
+ @Override
+ public File getFilesDir() {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ return activity.getFilesDir();
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ return wallpaper.getFilesDir();
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ return watchface.getFilesDir();
+ }
+ return null;
+ }
+
+
+ @Override
+ public File getFileStreamPath(String path) {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ return activity.getFileStreamPath(path);
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ return wallpaper.getFileStreamPath(path);
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ return watchface.getFileStreamPath(path);
+ }
+ return null;
+ }
+
+
+ @Override
+ public InputStream openFileInput(String filename) {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ try {
+ return activity.openFileInput(filename);
+ } catch (FileNotFoundException e) {
+ System.err.println("Cannot open file " + filename);
+ }
+ }
+ return null;
+ }
+
+
+ @Override
+ public AssetManager getAssets() {
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ return activity.getAssets();
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ return wallpaper.getBaseContext().getAssets();
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ return watchface.getBaseContext().getAssets();
+ }
+ return null;
+ }
+
+
+ @Override
+ public void setSystemUiVisibility(int visibility) {
+ int kind = component.getKind();
+ if (kind == AppComponent.FRAGMENT || kind == AppComponent.WALLPAPER) {
+ surfaceView.setSystemUiVisibility(visibility);
+ }
+ }
+
+
+ @Override
+ public void finish() {
+ if (component == null) return;
+
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ // This is the correct way to stop the sketch programmatically, according to the developer's docs:
+ // https://developer.android.com/reference/android/app/Activity.html#onDestroy()
+ // https://developer.android.com/reference/android/app/Activity.html#finish()
+ // and online discussions:
+ // http://stackoverflow.com/questions/2033914/quitting-an-application-is-that-frowned-upon/2034238
+ // finish() it will trigger an onDestroy() event, which will translate down through the
+ // activity hierarchy and eventually pausing and stopping Processing's animation thread, etc.
+ activity.finish();
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ // stopSelf() stops a service from within:
+ // https://developer.android.com/reference/android/app/Service.html#stopSelf()
+ wallpaper.stopSelf();
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ watchface.stopSelf();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ // Thread handling
+
+
+ public Thread createThread() {
+ return new AnimationThread();
+ }
+
+
+ @Override
+ public void startThread() {
+ if (!surfaceReady) {
+ requestedThreadStart = true;
+ return;
+ }
+
+ if (thread == null) {
+ thread = createThread();
+ thread.start();
+ requestedThreadStart = false;
+ } else {
+ throw new IllegalStateException("Thread already started in " +
+ getClass().getSimpleName());
+ }
+ }
+
+
+ @Override
+ public void pauseThread() {
+ if (!surfaceReady) return;
+
+ paused = true;
+ }
+
+
+ @Override
+ public void resumeThread() {
+ if (!surfaceReady) return;
+
+ if (thread == null) {
+ thread = createThread();
+ thread.start();
+ }
+
+ paused = false;
+ synchronized (pauseObject) {
+ pauseObject.notifyAll(); // wake up the animation thread
+ }
+ }
+
+
+ @Override
+ public boolean stopThread() {
+ if (!surfaceReady) return true;
+
+ if (thread == null) {
+ return false;
+ }
+
+ thread.interrupt();
+ thread = null;
+
+ return true;
+ }
+
+
+ @Override
+ public boolean isStopped() {
+ return thread == null;
+ }
+
+
+ public void setFrameRate(float fps) {
+ frameRateTarget = fps;
+ frameRatePeriod = (long) (1000000000.0 / frameRateTarget);
+ }
+
+
+ protected void checkPause() throws InterruptedException {
+ synchronized (pauseObject) {
+ while (paused) {
+ pauseObject.wait();
+ }
+ }
+ }
+
+
+ protected void callDraw() {
+ component.requestDraw();
+ if (component.canDraw() && sketch != null) {
+ sketch.handleDraw();
+ }
+ }
+
+
+ public class AnimationThread extends Thread {
+ public AnimationThread() {
+ super("Animation Thread");
+ }
+
+ /**
+ * Main method for the primary animation thread.
+ * Painting in AWT and Swing
+ */
+ @Override
+ public void run() { // not good to make this synchronized, locks things up
+ long beforeTime = System.nanoTime();
+ long overSleepTime = 0L;
+
+ int noDelays = 0;
+ // Number of frames with a delay of 0 ms before the
+ // animation thread yields to other running threads.
+ final int NO_DELAYS_PER_YIELD = 15;
+
+ if (sketch == null) return;
+
+ // un-pause the sketch and get rolling
+ sketch.start();
+
+ while ((Thread.currentThread() == thread) &&
+ (sketch != null && !sketch.finished)) {
+ if (Thread.currentThread().isInterrupted()) {
+ return;
+ }
+ try {
+ checkPause();
+ } catch (InterruptedException e) {
+ return;
+ }
+
+ callDraw();
+
+ // wait for update & paint to happen before drawing next frame
+ // this is necessary since the drawing is sometimes in a
+ // separate thread, meaning that the next frame will start
+ // before the update/paint is completed
+
+ long afterTime = System.nanoTime();
+ long timeDiff = afterTime - beforeTime;
+ //System.out.println("time diff is " + timeDiff);
+ long sleepTime = (frameRatePeriod - timeDiff) - overSleepTime;
+
+ if (sleepTime > 0) { // some time left in this cycle
+ try {
+ Thread.sleep(sleepTime / 1000000L, (int) (sleepTime % 1000000L));
+ noDelays = 0; // Got some sleep, not delaying anymore
+ } catch (InterruptedException ex) {
+ System.err.println("Cannot properly set the timing for the draw animation.");
+ }
+
+ overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
+
+ } else { // sleepTime <= 0; the frame took longer than the period
+ overSleepTime = 0L;
+ noDelays++;
+
+ if (noDelays > NO_DELAYS_PER_YIELD) {
+ Thread.yield(); // give another thread a chance to run
+ noDelays = 0;
+ }
+ }
+
+ beforeTime = System.nanoTime();
+ }
+ }
+ }
+
+
+ public boolean hasPermission(String permission) {
+ int res = ContextCompat.checkSelfPermission(getContext(), permission);
+ return res == PackageManager.PERMISSION_GRANTED;
+ }
+
+
+ public void requestPermissions(String[] permissions) {
+ if (component.isService()) {
+ // https://developer.android.com/training/articles/wear-permissions.html
+ // Inspired by PermissionHelper.java from Michael von Glasow:
+ // https://github.com/mvglasow/satstat/blob/master/src/com/vonglasow/michael/satstat/utils/PermissionHelper.java
+ // Example of use:
+ // https://github.com/mvglasow/satstat/blob/master/src/com/vonglasow/michael/satstat/PasvLocListenerService.java
+ final ServiceEngine eng = getEngine();
+ if (eng != null) { // A valid service should have a non-null engine at this point, but just in case
+ ResultReceiver resultReceiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {
+ @Override
+ protected void onReceiveResult (int resultCode, Bundle resultData) {
+ String[] outPermissions = resultData.getStringArray(PermissionRequestor.KEY_PERMISSIONS);
+ int[] grantResults = resultData.getIntArray(PermissionRequestor.KEY_GRANT_RESULTS);
+ eng.onRequestPermissionsResult(resultCode, outPermissions, grantResults);
+ }
+ };
+ final Intent permIntent = new Intent(getContext(), PermissionRequestor.class);
+ permIntent.putExtra(PermissionRequestor.KEY_RESULT_RECEIVER, resultReceiver);
+ permIntent.putExtra(PermissionRequestor.KEY_PERMISSIONS, permissions);
+ permIntent.putExtra(PermissionRequestor.KEY_REQUEST_CODE, REQUEST_PERMISSIONS);
+ // Show the dialog requesting the permissions
+ permIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(permIntent);
+ }
+ } else if (activity != null) {
+ // Requesting permissions from user when the app resumes.
+ // Nice example on how to handle user response
+ // http://stackoverflow.com/a/35495855
+ // More on permission in Android 23:
+ // https://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en
+ ActivityCompat.requestPermissions(activity, permissions, REQUEST_PERMISSIONS);
+ }
+ }
+}
diff --git a/core/src/processing/core/PVector.java b/libs/processing-core/src/main/java/processing/core/PVector.java
similarity index 90%
rename from core/src/processing/core/PVector.java
rename to libs/processing-core/src/main/java/processing/core/PVector.java
index 725c208b1..d1c074f92 100644
--- a/core/src/processing/core/PVector.java
+++ b/libs/processing-core/src/main/java/processing/core/PVector.java
@@ -3,8 +3,10 @@
/*
Part of the Processing project - http://processing.org
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2008-12 Ben Fry and Casey Reas
Copyright (c) 2008 Dan Shiffman
- Copyright (c) 2008-10 Ben Fry and Casey Reas
+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -68,12 +70,6 @@
* @webref math
*/
public class PVector implements Serializable {
-
- /**
- * Generated 2010-09-14 by jdf
- */
- private static final long serialVersionUID = -6717872085945400694L;
-
/**
* ( begin auto-generated from PVector_x.xml )
*
@@ -119,6 +115,7 @@ public class PVector implements Serializable {
/** Array so that this can be temporarily used in an array context */
transient protected float[] array;
+
/**
* Constructor for an empty vector: x, y, and z are set to 0.
*/
@@ -149,6 +146,7 @@ public PVector(float x, float y) {
this.z = 0;
}
+
/**
* ( begin auto-generated from PVector_set.xml )
*
@@ -161,33 +159,35 @@ public PVector(float x, float y) {
* @param x the x component of the vector
* @param y the y component of the vector
* @param z the z component of the vector
- * @brief Set the x, y, and z component of the vector
+ * @brief Set the components of the vector
*/
- public void set(float x, float y, float z) {
+ public PVector set(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
+ return this;
}
+
/**
- *
- * @webref pvector:method
* @param x the x component of the vector
* @param y the y component of the vector
- * @brief Set the x, y components of the vector
*/
- public void set(float x, float y) {
+ public PVector set(float x, float y) {
this.x = x;
this.y = y;
+ return this;
}
+
/**
* @param v any variable of type PVector
*/
- public void set(PVector v) {
+ public PVector set(PVector v) {
x = v.x;
y = v.y;
z = v.z;
+ return this;
}
@@ -195,7 +195,7 @@ public void set(PVector v) {
* Set the x, y (and maybe z) coordinates using a float[] array as the source.
* @param source array to copy from
*/
- public void set(float[] source) {
+ public PVector set(float[] source) {
if (source.length >= 2) {
x = source[0];
y = source[1];
@@ -203,6 +203,7 @@ public void set(float[] source) {
if (source.length >= 3) {
z = source[2];
}
+ return this;
}
@@ -220,9 +221,10 @@ public void set(float[] source) {
* @see PVector#random3D()
*/
static public PVector random2D() {
- return random2D(null,null);
+ return random2D(null, null);
}
+
/**
* Make a new 2D unit vector with a random direction
* using Processing's current random number generator
@@ -230,7 +232,7 @@ static public PVector random2D() {
* @return the random PVector
*/
static public PVector random2D(PApplet parent) {
- return random2D(null,parent);
+ return random2D(null, parent);
}
/**
@@ -239,18 +241,23 @@ static public PVector random2D(PApplet parent) {
* @return the random PVector
*/
static public PVector random2D(PVector target) {
- return random2D(target,null);
+ return random2D(target, null);
}
+
/**
- * Make a new 2D unit vector with a random direction
+ * Make a new 2D unit vector with a random direction. Pass in the parent
+ * PApplet if you want randomSeed() to work (and be predictable). Or leave
+ * it null and be... random.
* @return the random PVector
*/
static public PVector random2D(PVector target, PApplet parent) {
- if (parent == null) return fromAngle((float)(Math.random()*Math.PI*2),target);
- else return fromAngle(parent.random(PConstants.TWO_PI),target);
+ return (parent == null) ?
+ fromAngle((float) (Math.random() * Math.PI*2), target) :
+ fromAngle(parent.random(PConstants.TAU), target);
}
+
/**
* ( begin auto-generated from PVector_random3D.xml )
*
@@ -265,9 +272,10 @@ static public PVector random2D(PVector target, PApplet parent) {
* @see PVector#random2D()
*/
static public PVector random3D() {
- return random3D(null,null);
+ return random3D(null, null);
}
+
/**
* Make a new 3D unit vector with a random direction
* using Processing's current random number generator
@@ -275,18 +283,20 @@ static public PVector random3D() {
* @return the random PVector
*/
static public PVector random3D(PApplet parent) {
- return random3D(null,parent);
+ return random3D(null, parent);
}
+
/**
* Set a 3D vector to a random unit vector with a random direction
* @param target the target vector (if null, a new vector will be created)
* @return the random PVector
*/
static public PVector random3D(PVector target) {
- return random3D(target,null);
+ return random3D(target, null);
}
+
/**
* Make a new 3D unit vector with a random direction
* @return the random PVector
@@ -312,6 +322,7 @@ static public PVector random3D(PVector target, PApplet parent) {
return target;
}
+
/**
* ( begin auto-generated from PVector_sub.xml )
*
@@ -322,7 +333,7 @@ static public PVector random3D(PVector target, PApplet parent) {
* @webref pvector:method
* @usage web_application
* @brief Make a new 2D unit vector from an angle
- * @param angle the angle
+ * @param angle the angle in radians
* @return the new unit PVector
*/
static public PVector fromAngle(float angle) {
@@ -345,8 +356,9 @@ static public PVector fromAngle(float angle, PVector target) {
return target;
}
+
/**
- * ( begin auto-generated from PVector_get.xml )
+ * ( begin auto-generated from PVector_copy.xml )
*
* Gets a copy of the vector, returns a PVector object.
*
@@ -356,10 +368,17 @@ static public PVector fromAngle(float angle, PVector target) {
* @usage web_application
* @brief Get a copy of the vector
*/
- public PVector get() {
+ public PVector copy() {
return new PVector(x, y, z);
}
+
+ @Deprecated
+ public PVector get() {
+ return copy();
+ }
+
+
/**
* @param target
*/
@@ -396,6 +415,7 @@ public float mag() {
return (float) Math.sqrt(x*x + y*y + z*z);
}
+
/**
* ( begin auto-generated from PVector_mag.xml )
*
@@ -416,6 +436,7 @@ public float magSq() {
return (x*x + y*y + z*z);
}
+
/**
* ( begin auto-generated from PVector_add.xml )
*
@@ -432,21 +453,33 @@ public float magSq() {
* @param v the vector to be added
* @brief Adds x, y, and z components to a vector, one vector to another, or two independent vectors
*/
- public void add(PVector v) {
+ public PVector add(PVector v) {
x += v.x;
y += v.y;
z += v.z;
+ return this;
}
+
/**
* @param x x component of the vector
* @param y y component of the vector
+ */
+ public PVector add(float x, float y) {
+ this.x += x;
+ this.y += y;
+ return this;
+ }
+
+
+ /**
* @param z z component of the vector
*/
- public void add(float x, float y, float z) {
+ public PVector add(float x, float y, float z) {
this.x += x;
this.y += y;
this.z += z;
+ return this;
}
@@ -490,21 +523,33 @@ static public PVector add(PVector v1, PVector v2, PVector target) {
* @param v any variable of type PVector
* @brief Subtract x, y, and z components from a vector, one vector from another, or two independent vectors
*/
- public void sub(PVector v) {
+ public PVector sub(PVector v) {
x -= v.x;
y -= v.y;
z -= v.z;
+ return this;
}
+
/**
* @param x the x component of the vector
* @param y the y component of the vector
+ */
+ public PVector sub(float x, float y) {
+ this.x -= x;
+ this.y -= y;
+ return this;
+ }
+
+
+ /**
* @param z the z component of the vector
*/
- public void sub(float x, float y, float z) {
+ public PVector sub(float x, float y, float z) {
this.x -= x;
this.y -= y;
this.z -= z;
+ return this;
}
@@ -517,10 +562,9 @@ static public PVector sub(PVector v1, PVector v2) {
return sub(v1, v2, null);
}
+
/**
* Subtract one vector from another and store in another vector
- * @param v1 the x, y, and z components of a PVector object
- * @param v2 the x, y, and z components of a PVector object
* @param target PVector in which to store the result
*/
static public PVector sub(PVector v1, PVector v2, PVector target) {
@@ -545,10 +589,11 @@ static public PVector sub(PVector v1, PVector v2, PVector target) {
* @brief Multiply a vector by a scalar
* @param n the number to multiply with the vector
*/
- public void mult(float n) {
+ public PVector mult(float n) {
x *= n;
y *= n;
z *= n;
+ return this;
}
@@ -574,7 +619,6 @@ static public PVector mult(PVector v, float n, PVector target) {
}
-
/**
* ( begin auto-generated from PVector_div.xml )
*
@@ -587,10 +631,11 @@ static public PVector mult(PVector v, float n, PVector target) {
* @brief Divide a vector by a scalar
* @param n the number by which to divide the vector
*/
- public void div(float n) {
+ public PVector div(float n) {
x /= n;
y /= n;
z /= n;
+ return this;
}
@@ -603,6 +648,7 @@ static public PVector div(PVector v, float n) {
return div(v, n, null);
}
+
/**
* Divide a vector by a scalar and store the result in another vector.
* @param target PVector in which to store the result
@@ -668,6 +714,7 @@ public float dot(PVector v) {
return x*v.x + y*v.y + z*v.z;
}
+
/**
* @param x x component of the vector
* @param y y component of the vector
@@ -677,6 +724,7 @@ public float dot(float x, float y, float z) {
return this.x*x + this.y*y + this.z*z;
}
+
/**
* @param v1 any variable of type PVector
* @param v2 any variable of type PVector
@@ -720,6 +768,7 @@ public PVector cross(PVector v, PVector target) {
return target;
}
+
/**
* @param v1 any variable of type PVector
* @param v2 any variable of type PVector
@@ -750,11 +799,12 @@ static public PVector cross(PVector v1, PVector v2, PVector target) {
* @usage web_application
* @brief Normalize the vector to a length of 1
*/
- public void normalize() {
+ public PVector normalize() {
float m = mag();
if (m != 0 && m != 1) {
div(m);
}
+ return this;
}
@@ -788,13 +838,15 @@ public PVector normalize(PVector target) {
* @param max the maximum magnitude for the vector
* @brief Limit the magnitude of the vector
*/
- public void limit(float max) {
+ public PVector limit(float max) {
if (magSq() > max*max) {
normalize();
mult(max);
}
+ return this;
}
+
/**
* ( begin auto-generated from PVector_setMag.xml )
*
@@ -807,11 +859,13 @@ public void limit(float max) {
* @param len the new length for this vector
* @brief Set the magnitude of the vector
*/
- public void setMag(float len) {
+ public PVector setMag(float len) {
normalize();
mult(len);
+ return this;
}
+
/**
* Sets the magnitude of this vector, storing the result in another vector.
* @param target Set to null to create a new vector
@@ -824,6 +878,7 @@ public PVector setMag(PVector target, float len) {
return target;
}
+
/**
* ( begin auto-generated from PVector_setMag.xml )
*
@@ -837,8 +892,8 @@ public PVector setMag(PVector target, float len) {
* @brief Calculate the angle of rotation for this vector
*/
public float heading() {
- float angle = (float) Math.atan2(-y, x);
- return -1*angle;
+ float angle = (float) Math.atan2(y, x);
+ return angle;
}
@@ -860,11 +915,12 @@ public float heading2D() {
* @brief Rotate the vector by an angle (2D only)
* @param theta the angle of rotation
*/
- public void rotate(float theta) {
- float xTemp = x;
+ public PVector rotate(float theta) {
+ float temp = x;
// Might need to check for rounding errors like with angleBetween function?
x = x*PApplet.cos(theta) - y*PApplet.sin(theta);
- y = xTemp*PApplet.sin(theta) + y*PApplet.cos(theta);
+ y = temp*PApplet.sin(theta) + y*PApplet.cos(theta);
+ return this;
}
@@ -879,37 +935,43 @@ public void rotate(float theta) {
* @usage web_application
* @brief Linear interpolate the vector to another vector
* @param v the vector to lerp to
- * @param amt The amount of interpolation; some value between 0.0 (old vector) and 1.0 (new vector). 0.1 is very near the new vector. 0.5 is halfway in between.
+ * @param amt The amount of interpolation; some value between 0.0 (old vector) and 1.0 (new vector). 0.1 is very near the old vector; 0.5 is halfway in between.
+ * @see PApplet#lerp(float, float, float)
*/
- public void lerp(PVector v, float amt) {
- x = PApplet.lerp(x,v.x,amt);
- y = PApplet.lerp(y,v.y,amt);
- z = PApplet.lerp(z,v.z,amt);
+ public PVector lerp(PVector v, float amt) {
+ x = PApplet.lerp(x, v.x, amt);
+ y = PApplet.lerp(y, v.y, amt);
+ z = PApplet.lerp(z, v.z, amt);
+ return this;
}
+
/**
* Linear interpolate between two vectors (returns a new PVector object)
* @param v1 the vector to start from
* @param v2 the vector to lerp to
*/
public static PVector lerp(PVector v1, PVector v2, float amt) {
- PVector v = v1.get();
+ PVector v = v1.copy();
v.lerp(v2, amt);
return v;
}
+
/**
* Linear interpolate the vector to x,y,z values
* @param x the x component to lerp to
* @param y the y component to lerp to
* @param z the z component to lerp to
*/
- public void lerp(float x, float y, float z, float amt) {
- this.x = PApplet.lerp(this.x,x,amt);
- this.y = PApplet.lerp(this.y,y,amt);
- this.z = PApplet.lerp(this.z,z,amt);
+ public PVector lerp(float x, float y, float z, float amt) {
+ this.x = PApplet.lerp(this.x, x, amt);
+ this.y = PApplet.lerp(this.y, y, amt);
+ this.z = PApplet.lerp(this.z, z, amt);
+ return this;
}
+
/**
* ( begin auto-generated from PVector_angleBetween.xml )
*
@@ -926,9 +988,9 @@ public void lerp(float x, float y, float z, float amt) {
static public float angleBetween(PVector v1, PVector v2) {
// We get NaN if we pass in a zero vector which can cause problems
- // Zero seems like a reasonable angle between a (0,0) vector and something else
- if (v1.x == 0 && v1.y == 0) return 0.0f;
- if (v2.x == 0 && v2.y == 0) return 0.0f;
+ // Zero seems like a reasonable angle between a (0,0,0) vector and something else
+ if (v1.x == 0 && v1.y == 0 && v1.z == 0 ) return 0.0f;
+ if (v2.x == 0 && v2.y == 0 && v2.z == 0 ) return 0.0f;
double dot = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
double v1mag = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
@@ -978,14 +1040,17 @@ public float[] array() {
return array;
}
+
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof PVector))
+ if (!(obj instanceof PVector)) {
return false;
+ }
final PVector p = (PVector) obj;
return x == p.x && y == p.y && z == p.z;
}
+
@Override
public int hashCode() {
int result = 1;
@@ -994,4 +1059,4 @@ public int hashCode() {
result = 31 * result + Float.floatToIntBits(z);
return result;
}
-}
\ No newline at end of file
+}
diff --git a/libs/processing-core/src/main/java/processing/data/DoubleDict.java b/libs/processing-core/src/main/java/processing/data/DoubleDict.java
new file mode 100644
index 000000000..f2a9adf10
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/data/DoubleDict.java
@@ -0,0 +1,850 @@
+package processing.data;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import processing.core.PApplet;
+
+
+/**
+ * A simple table class to use a String as a lookup for an double value.
+ *
+ * @webref data:composite
+ * @see IntDict
+ * @see StringDict
+ */
+public class DoubleDict {
+
+ /** Number of elements in the table */
+ protected int count;
+
+ protected String[] keys;
+ protected double[] values;
+
+ /** Internal implementation for faster lookups */
+ private HashMap indices = new HashMap<>();
+
+
+ public DoubleDict() {
+ count = 0;
+ keys = new String[10];
+ values = new double[10];
+ }
+
+
+ /**
+ * Create a new lookup with a specific size. This is more efficient than not
+ * specifying a size. Use it when you know the rough size of the thing you're creating.
+ *
+ * @nowebref
+ */
+ public DoubleDict(int length) {
+ count = 0;
+ keys = new String[length];
+ values = new double[length];
+ }
+
+
+ /**
+ * Read a set of entries from a Reader that has each key/value pair on
+ * a single line, separated by a tab.
+ *
+ * @nowebref
+ */
+ public DoubleDict(BufferedReader reader) {
+ String[] lines = PApplet.loadStrings(reader);
+ keys = new String[lines.length];
+ values = new double[lines.length];
+
+ for (int i = 0; i < lines.length; i++) {
+ String[] pieces = PApplet.split(lines[i], '\t');
+ if (pieces.length == 2) {
+ keys[count] = pieces[0];
+ values[count] = PApplet.parseFloat(pieces[1]);
+ indices.put(pieces[0], count);
+ count++;
+ }
+ }
+ }
+
+
+ /**
+ * @nowebref
+ */
+ public DoubleDict(String[] keys, double[] values) {
+ if (keys.length != values.length) {
+ throw new IllegalArgumentException("key and value arrays must be the same length");
+ }
+ this.keys = keys;
+ this.values = values;
+ count = keys.length;
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ /**
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
+ *
+ * new FloatDict(new Object[][] {
+ * { "key1", 1 },
+ * { "key2", 2 }
+ * });
+ *
+ */
+ public DoubleDict(Object[][] pairs) {
+ count = pairs.length;
+ this.keys = new String[count];
+ this.values = new double[count];
+ for (int i = 0; i < count; i++) {
+ keys[i] = (String) pairs[i][0];
+ values[i] = (Float) pairs[i][1];
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ public DoubleDict(Map incoming) {
+ count = incoming.size();
+ keys = new String[count];
+ values = new double[count];
+ int index = 0;
+ for (Map.Entry e : incoming.entrySet()) {
+ keys[index] = e.getKey();
+ values[index] = e.getValue();
+ indices.put(keys[index], index);
+ index++;
+ }
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Returns the number of key/value pairs
+ */
+ public int size() {
+ return count;
+ }
+
+
+ /**
+ * Resize the internal data, this can only be used to shrink the list.
+ * Helpful for situations like sorting and then grabbing the top 50 entries.
+ */
+ public void resize(int length) {
+ if (length == count) return;
+
+ if (length > count) {
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
+ }
+ if (length < 1) {
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
+ }
+
+ String[] newKeys = new String[length];
+ double[] newValues = new double[length];
+ PApplet.arrayCopy(keys, newKeys, length);
+ PApplet.arrayCopy(values, newValues, length);
+ keys = newKeys;
+ values = newValues;
+ count = length;
+ resetIndices();
+ }
+
+
+ /**
+ * Remove all entries.
+ *
+ * @webref doubledict:method
+ * @brief Remove all entries
+ */
+ public void clear() {
+ count = 0;
+ indices = new HashMap<>();
+ }
+
+
+ private void resetIndices() {
+ indices = new HashMap<>(count);
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public class Entry {
+ public String key;
+ public double value;
+
+ Entry(String key, double value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+
+ public Iterable entries() {
+ return new Iterable() {
+
+ public Iterator iterator() {
+ return entryIterator();
+ }
+ };
+ }
+
+
+ public Iterator entryIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Entry next() {
+ ++index;
+ Entry e = new Entry(keys[index], values[index]);
+ return e;
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public String key(int index) {
+ return keys[index];
+ }
+
+
+ protected void crop() {
+ if (count != keys.length) {
+ keys = PApplet.subset(keys, 0, count);
+ values = PApplet.subset(values, 0, count);
+ }
+ }
+
+
+ public Iterable keys() {
+ return new Iterable() {
+
+ @Override
+ public Iterator iterator() {
+ return keyIterator();
+ }
+ };
+ }
+
+
+ // Use this to iterate when you want to be able to remove elements along the way
+ public Iterator keyIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public String next() {
+ return key(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ /**
+ * Return a copy of the internal keys array. This array can be modified.
+ *
+ * @webref doubledict:method
+ * @brief Return a copy of the internal keys array
+ */
+ public String[] keyArray() {
+ crop();
+ return keyArray(null);
+ }
+
+
+ public String[] keyArray(String[] outgoing) {
+ if (outgoing == null || outgoing.length != count) {
+ outgoing = new String[count];
+ }
+ System.arraycopy(keys, 0, outgoing, 0, count);
+ return outgoing;
+ }
+
+
+ public double value(int index) {
+ return values[index];
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Return the internal array being used to store the values
+ */
+ public Iterable values() {
+ return new Iterable() {
+
+ @Override
+ public Iterator iterator() {
+ return valueIterator();
+ }
+ };
+ }
+
+
+ public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Double next() {
+ return value(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ /**
+ * Create a new array and copy each of the values into it.
+ *
+ * @webref doubledict:method
+ * @brief Create a new array and copy each of the values into it
+ */
+ public double[] valueArray() {
+ crop();
+ return valueArray(null);
+ }
+
+
+ /**
+ * Fill an already-allocated array with the values (more efficient than
+ * creating a new array each time). If 'array' is null, or not the same
+ * size as the number of values, a new array will be allocated and returned.
+ */
+ public double[] valueArray(double[] array) {
+ if (array == null || array.length != size()) {
+ array = new double[count];
+ }
+ System.arraycopy(values, 0, array, 0, count);
+ return array;
+ }
+
+
+ /**
+ * Return a value for the specified key.
+ *
+ * @webref doubledict:method
+ * @brief Return a value for the specified key
+ */
+ public double get(String key) {
+ int index = index(key);
+ if (index == -1) {
+ throw new IllegalArgumentException("No key named '" + key + "'");
+ }
+ return values[index];
+ }
+
+
+ public double get(String key, double alternate) {
+ int index = index(key);
+ if (index == -1) {
+ return alternate;
+ }
+ return values[index];
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Create a new key/value pair or change the value of one
+ */
+ public void set(String key, double amount) {
+ int index = index(key);
+ if (index == -1) {
+ create(key, amount);
+ } else {
+ values[index] = amount;
+ }
+ }
+
+
+ public void setIndex(int index, String key, double value) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ keys[index] = key;
+ values[index] = value;
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Check if a key is a part of the data structure
+ */
+ public boolean hasKey(String key) {
+ return index(key) != -1;
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Add to a value
+ */
+ public void add(String key, double amount) {
+ int index = index(key);
+ if (index == -1) {
+ create(key, amount);
+ } else {
+ values[index] += amount;
+ }
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Subtract from a value
+ */
+ public void sub(String key, double amount) {
+ add(key, -amount);
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Multiply a value
+ */
+ public void mult(String key, double amount) {
+ int index = index(key);
+ if (index != -1) {
+ values[index] *= amount;
+ }
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Divide a value
+ */
+ public void div(String key, double amount) {
+ int index = index(key);
+ if (index != -1) {
+ values[index] /= amount;
+ }
+ }
+
+
+ private void checkMinMax(String functionName) {
+ if (count == 0) {
+ String msg =
+ String.format("Cannot use %s() on an empty %s.",
+ functionName, getClass().getSimpleName());
+ throw new RuntimeException(msg);
+ }
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Return the smallest value
+ */
+ public int minIndex() {
+ //checkMinMax("minIndex");
+ if (count == 0) return -1;
+
+ // Will still return NaN if there are 1 or more entries, and they're all NaN
+ double m = Float.NaN;
+ int mi = -1;
+ for (int i = 0; i < count; i++) {
+ // find one good value to start
+ if (values[i] == values[i]) {
+ m = values[i];
+ mi = i;
+
+ // calculate the rest
+ for (int j = i+1; j < count; j++) {
+ double d = values[j];
+ if ((d == d) && (d < m)) {
+ m = values[j];
+ mi = j;
+ }
+ }
+ break;
+ }
+ }
+ return mi;
+ }
+
+
+ // return the key for the minimum value
+ public String minKey() {
+ checkMinMax("minKey");
+ int index = minIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ // return the minimum value, or throw an error if there are no values
+ public double minValue() {
+ checkMinMax("minValue");
+ int index = minIndex();
+ if (index == -1) {
+ return Float.NaN;
+ }
+ return values[index];
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Return the largest value
+ */
+ // The index of the entry that has the max value. Reference above is incorrect.
+ public int maxIndex() {
+ //checkMinMax("maxIndex");
+ if (count == 0) {
+ return -1;
+ }
+ // Will still return NaN if there is 1 or more entries, and they're all NaN
+ double m = Double.NaN;
+ int mi = -1;
+ for (int i = 0; i < count; i++) {
+ // find one good value to start
+ if (values[i] == values[i]) {
+ m = values[i];
+ mi = i;
+
+ // calculate the rest
+ for (int j = i+1; j < count; j++) {
+ double d = values[j];
+ if (!Double.isNaN(d) && (d > m)) {
+ m = values[j];
+ mi = j;
+ }
+ }
+ break;
+ }
+ }
+ return mi;
+ }
+
+
+ /** The key for a max value; null if empty or everything is NaN (no max). */
+ public String maxKey() {
+ //checkMinMax("maxKey");
+ int index = maxIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ /** The max value. (Or NaN if no entries or they're all NaN.) */
+ public double maxValue() {
+ //checkMinMax("maxValue");
+ int index = maxIndex();
+ if (index == -1) {
+ return Float.NaN;
+ }
+ return values[index];
+ }
+
+
+ public double sum() {
+ double sum = 0;
+ for (int i = 0; i < count; i++) {
+ sum += values[i];
+ }
+ return sum;
+ }
+
+
+ public int index(String what) {
+ Integer found = indices.get(what);
+ return (found == null) ? -1 : found.intValue();
+ }
+
+
+ protected void create(String what, double much) {
+ if (count == keys.length) {
+ keys = PApplet.expand(keys);
+ values = PApplet.expand(values);
+ }
+ indices.put(what, Integer.valueOf(count));
+ keys[count] = what;
+ values[count] = much;
+ count++;
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Remove a key/value pair
+ */
+ public double remove(String key) {
+ int index = index(key);
+ if (index == -1) {
+ throw new NoSuchElementException("'" + key + "' not found");
+ }
+ double value = values[index];
+ removeIndex(index);
+ return value;
+ }
+
+
+ public double removeIndex(int index) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ double value = values[index];
+ indices.remove(keys[index]);
+ for (int i = index; i < count-1; i++) {
+ keys[i] = keys[i+1];
+ values[i] = values[i+1];
+ indices.put(keys[i], i);
+ }
+ count--;
+ keys[count] = null;
+ values[count] = 0;
+ return value;
+ }
+
+
+ public void swap(int a, int b) {
+ String tkey = keys[a];
+ double tvalue = values[a];
+ keys[a] = keys[b];
+ values[a] = values[b];
+ keys[b] = tkey;
+ values[b] = tvalue;
+
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
+ }
+
+
+ /**
+ * Sort the keys alphabetically (ignoring case). Uses the value as a
+ * tie-breaker (only really possible with a key that has a case change).
+ *
+ * @webref doubledict:method
+ * @brief Sort the keys alphabetically
+ */
+ public void sortKeys() {
+ sortImpl(true, false, true);
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Sort the keys alphabetically in reverse
+ */
+ public void sortKeysReverse() {
+ sortImpl(true, true, true);
+ }
+
+
+ /**
+ * Sort by values in descending order (largest value will be at [0]).
+ *
+ * @webref doubledict:method
+ * @brief Sort by values in ascending order
+ */
+ public void sortValues() {
+ sortValues(true);
+ }
+
+
+ /**
+ * Set true to ensure that the order returned is identical. Slightly
+ * slower because the tie-breaker for identical values compares the keys.
+ * @param stable
+ */
+ public void sortValues(boolean stable) {
+ sortImpl(false, false, stable);
+ }
+
+
+ /**
+ * @webref doubledict:method
+ * @brief Sort by values in descending order
+ */
+ public void sortValuesReverse() {
+ sortValuesReverse(true);
+ }
+
+
+ public void sortValuesReverse(boolean stable) {
+ sortImpl(false, true, stable);
+ }
+
+
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
+ final boolean stable) {
+ Sort s = new Sort() {
+ @Override
+ public int size() {
+ if (useKeys) {
+ return count; // don't worry about NaN values
+
+ } else if (count == 0) { // skip the NaN check, it'll AIOOBE
+ return 0;
+
+ } else { // first move NaN values to the end of the list
+ int right = count - 1;
+ while (values[right] != values[right]) {
+ right--;
+ if (right == -1) {
+ return 0; // all values are NaN
+ }
+ }
+ for (int i = right; i >= 0; --i) {
+ if (Double.isNaN(values[i])) {
+ swap(i, right);
+ --right;
+ }
+ }
+ return right + 1;
+ }
+ }
+
+ @Override
+ public int compare(int a, int b) {
+ double diff = 0;
+ if (useKeys) {
+ diff = keys[a].compareToIgnoreCase(keys[b]);
+ if (diff == 0) {
+ diff = values[a] - values[b];
+ }
+ } else { // sort values
+ diff = values[a] - values[b];
+ if (diff == 0 && stable) {
+ diff = keys[a].compareToIgnoreCase(keys[b]);
+ }
+ }
+ if (diff == 0) {
+ return 0;
+ } else if (reverse) {
+ return diff < 0 ? 1 : -1;
+ } else {
+ return diff < 0 ? -1 : 1;
+ }
+ }
+
+ @Override
+ public void swap(int a, int b) {
+ DoubleDict.this.swap(a, b);
+ }
+ };
+ s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ resetIndices();
+ }
+
+
+ /**
+ * Sum all of the values in this dictionary, then return a new FloatDict of
+ * each key, divided by the total sum. The total for all values will be ~1.0.
+ * @return a FloatDict with the original keys, mapped to their pct of the total
+ */
+ public DoubleDict getPercent() {
+ double sum = sum();
+ DoubleDict outgoing = new DoubleDict();
+ for (int i = 0; i < size(); i++) {
+ double percent = value(i) / sum;
+ outgoing.set(key(i), percent);
+ }
+ return outgoing;
+ }
+
+
+ /** Returns a duplicate copy of this object. */
+ public DoubleDict copy() {
+ DoubleDict outgoing = new DoubleDict(count);
+ System.arraycopy(keys, 0, outgoing.keys, 0, count);
+ System.arraycopy(values, 0, outgoing.values, 0, count);
+ for (int i = 0; i < count; i++) {
+ outgoing.indices.put(keys[i], i);
+ }
+ outgoing.count = count;
+ return outgoing;
+ }
+
+
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write tab-delimited entries out to
+ * @param writer
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(keys[i] + "\t" + values[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ StringList items = new StringList();
+ for (int i = 0; i < count; i++) {
+ items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
+ }
+ return "{ " + items.join(", ") + " }";
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/data/DoubleList.java b/libs/processing-core/src/main/java/processing/data/DoubleList.java
new file mode 100644
index 000000000..ae47a8442
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/data/DoubleList.java
@@ -0,0 +1,928 @@
+package processing.data;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Random;
+
+import processing.core.PApplet;
+
+
+/**
+ * Helper class for a list of floats. Lists are designed to have some of the
+ * features of ArrayLists, but to maintain the simplicity and efficiency of
+ * working with arrays.
+ *
+ * Functions like sort() and shuffle() always act on the list itself. To get
+ * a sorted copy, use list.copy().sort().
+ *
+ * @webref data:composite
+ * @see IntList
+ * @see StringList
+ */
+public class DoubleList implements Iterable {
+ int count;
+ double[] data;
+
+
+ public DoubleList() {
+ data = new double[10];
+ }
+
+
+ /**
+ * @nowebref
+ */
+ public DoubleList(int length) {
+ data = new double[length];
+ }
+
+
+ /**
+ * @nowebref
+ */
+ public DoubleList(double[] list) {
+ count = list.length;
+ data = new double[count];
+ System.arraycopy(list, 0, data, 0, count);
+ }
+
+
+ /**
+ * Construct an FloatList from an iterable pile of objects.
+ * For instance, a double array, an array of strings, who knows).
+ * Un-parseable or null values will be set to NaN.
+ * @nowebref
+ */
+ public DoubleList(Iterable iter) {
+ this(10);
+ for (Object o : iter) {
+ if (o == null) {
+ append(Double.NaN);
+ } else if (o instanceof Number) {
+ append(((Number) o).doubleValue());
+ } else {
+ append(PApplet.parseFloat(o.toString().trim()));
+ }
+ }
+ crop();
+ }
+
+
+ /**
+ * Construct an FloatList from a random pile of objects.
+ * Un-parseable or null values will be set to NaN.
+ */
+ public DoubleList(Object... items) {
+ // nuts, no good way to pass missingValue to this fn (varargs must be last)
+ final double missingValue = Double.NaN;
+
+ count = items.length;
+ data = new double[count];
+ int index = 0;
+ for (Object o : items) {
+ double value = missingValue;
+ if (o != null) {
+ if (o instanceof Number) {
+ value = ((Number) o).doubleValue();
+ } else {
+ try {
+ value = Double.parseDouble(o.toString().trim());
+ } catch (NumberFormatException nfe) {
+ value = missingValue;
+ }
+ }
+ }
+ data[index++] = value;
+ }
+ }
+
+
+ /**
+ * Improve efficiency by removing allocated but unused entries from the
+ * internal array used to store the data. Set to private, though it could
+ * be useful to have this public if lists are frequently making drastic
+ * size changes (from very large to very small).
+ */
+ private void crop() {
+ if (count != data.length) {
+ data = PApplet.subset(data, 0, count);
+ }
+ }
+
+
+ /**
+ * Get the length of the list.
+ *
+ * @webref doublelist:method
+ * @brief Get the length of the list
+ */
+ public int size() {
+ return count;
+ }
+
+
+ public void resize(int length) {
+ if (length > data.length) {
+ double[] temp = new double[length];
+ System.arraycopy(data, 0, temp, 0, count);
+ data = temp;
+
+ } else if (length > count) {
+ Arrays.fill(data, count, length, 0);
+ }
+ count = length;
+ }
+
+
+ /**
+ * Remove all entries from the list.
+ *
+ * @webref doublelist:method
+ * @brief Remove all entries from the list
+ */
+ public void clear() {
+ count = 0;
+ }
+
+
+ /**
+ * Get an entry at a particular index.
+ *
+ * @webref doublelist:method
+ * @brief Get an entry at a particular index
+ */
+ public double get(int index) {
+ if (index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return data[index];
+ }
+
+
+ /**
+ * Set the entry at a particular index. If the index is past the length of
+ * the list, it'll expand the list to accommodate, and fill the intermediate
+ * entries with 0s.
+ *
+ * @webref doublelist:method
+ * @brief Set the entry at a particular index
+ */
+ public void set(int index, double what) {
+ if (index >= count) {
+ data = PApplet.expand(data, index+1);
+ for (int i = count; i < index; i++) {
+ data[i] = 0;
+ }
+ count = index+1;
+ }
+ data[index] = what;
+ }
+
+
+ /** Just an alias for append(), but matches pop() */
+ public void push(double value) {
+ append(value);
+ }
+
+
+ public double pop() {
+ if (count == 0) {
+ throw new RuntimeException("Can't call pop() on an empty list");
+ }
+ double value = get(count-1);
+ count--;
+ return value;
+ }
+
+
+ /**
+ * Remove an element from the specified index.
+ *
+ * @webref doublelist:method
+ * @brief Remove an element from the specified index
+ */
+ public double remove(int index) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ double entry = data[index];
+// int[] outgoing = new int[count - 1];
+// System.arraycopy(data, 0, outgoing, 0, index);
+// count--;
+// System.arraycopy(data, index + 1, outgoing, 0, count - index);
+// data = outgoing;
+ // For most cases, this actually appears to be faster
+ // than arraycopy() on an array copying into itself.
+ for (int i = index; i < count-1; i++) {
+ data[i] = data[i+1];
+ }
+ count--;
+ return entry;
+ }
+
+
+ // Remove the first instance of a particular value,
+ // and return the index at which it was found.
+ public int removeValue(int value) {
+ int index = index(value);
+ if (index != -1) {
+ remove(index);
+ return index;
+ }
+ return -1;
+ }
+
+
+ // Remove all instances of a particular value,
+ // and return the number of values found and removed
+ public int removeValues(int value) {
+ int ii = 0;
+ if (Double.isNaN(value)) {
+ for (int i = 0; i < count; i++) {
+ if (!Double.isNaN(data[i])) {
+ data[ii++] = data[i];
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ if (data[i] != value) {
+ data[ii++] = data[i];
+ }
+ }
+ }
+ int removed = count - ii;
+ count = ii;
+ return removed;
+ }
+
+
+ /** Replace the first instance of a particular value */
+ public boolean replaceValue(double value, double newValue) {
+ if (Double.isNaN(value)) {
+ for (int i = 0; i < count; i++) {
+ if (Double.isNaN(data[i])) {
+ data[i] = newValue;
+ return true;
+ }
+ }
+ } else {
+ int index = index(value);
+ if (index != -1) {
+ data[index] = newValue;
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /** Replace all instances of a particular value */
+ public boolean replaceValues(double value, double newValue) {
+ boolean changed = false;
+ if (Double.isNaN(value)) {
+ for (int i = 0; i < count; i++) {
+ if (Double.isNaN(data[i])) {
+ data[i] = newValue;
+ changed = true;
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ if (data[i] == value) {
+ data[i] = newValue;
+ changed = true;
+ }
+ }
+ }
+ return changed;
+ }
+
+
+
+ /**
+ * Add a new entry to the list.
+ *
+ * @webref doublelist:method
+ * @brief Add a new entry to the list
+ */
+ public void append(double value) {
+ if (count == data.length) {
+ data = PApplet.expand(data);
+ }
+ data[count++] = value;
+ }
+
+
+ public void append(double[] values) {
+ for (double v : values) {
+ append(v);
+ }
+ }
+
+
+ public void append(DoubleList list) {
+ for (double v : list.values()) { // will concat the list...
+ append(v);
+ }
+ }
+
+
+ /** Add this value, but only if it's not already in the list. */
+ public void appendUnique(double value) {
+ if (!hasValue(value)) {
+ append(value);
+ }
+ }
+
+
+// public void insert(int index, int value) {
+// if (index+1 > count) {
+// if (index+1 < data.length) {
+// }
+// }
+// if (index >= data.length) {
+// data = PApplet.expand(data, index+1);
+// data[index] = value;
+// count = index+1;
+//
+// } else if (count == data.length) {
+// if (index >= count) {
+// //int[] temp = new int[count << 1];
+// System.arraycopy(data, 0, temp, 0, index);
+// temp[index] = value;
+// System.arraycopy(data, index, temp, index+1, count - index);
+// data = temp;
+//
+// } else {
+// // data[] has room to grow
+// // for() loop believed to be faster than System.arraycopy over itself
+// for (int i = count; i > index; --i) {
+// data[i] = data[i-1];
+// }
+// data[index] = value;
+// count++;
+// }
+// }
+
+
+ public void insert(int index, double value) {
+ insert(index, new double[] { value });
+ }
+
+
+ // same as splice
+ public void insert(int index, double[] values) {
+ if (index < 0) {
+ throw new IllegalArgumentException("insert() index cannot be negative: it was " + index);
+ }
+ if (index >= data.length) {
+ throw new IllegalArgumentException("insert() index " + index + " is past the end of this list");
+ }
+
+ double[] temp = new double[count + values.length];
+
+ // Copy the old values, but not more than already exist
+ System.arraycopy(data, 0, temp, 0, Math.min(count, index));
+
+ // Copy the new values into the proper place
+ System.arraycopy(values, 0, temp, index, values.length);
+
+// if (index < count) {
+ // The index was inside count, so it's a true splice/insert
+ System.arraycopy(data, index, temp, index+values.length, count - index);
+ count = count + values.length;
+// } else {
+// // The index was past 'count', so the new count is weirder
+// count = index + values.length;
+// }
+ data = temp;
+ }
+
+
+ public void insert(int index, DoubleList list) {
+ insert(index, list.values());
+ }
+
+
+ // below are aborted attempts at more optimized versions of the code
+ // that are harder to read and debug...
+
+// if (index + values.length >= count) {
+// // We're past the current 'count', check to see if we're still allocated
+// // index 9, data.length = 10, values.length = 1
+// if (index + values.length < data.length) {
+// // There's still room for these entries, even though it's past 'count'.
+// // First clear out the entries leading up to it, however.
+// for (int i = count; i < index; i++) {
+// data[i] = 0;
+// }
+// data[index] =
+// }
+// if (index >= data.length) {
+// int length = index + values.length;
+// int[] temp = new int[length];
+// System.arraycopy(data, 0, temp, 0, count);
+// System.arraycopy(values, 0, temp, index, values.length);
+// data = temp;
+// count = data.length;
+// } else {
+//
+// }
+//
+// } else if (count == data.length) {
+// int[] temp = new int[count << 1];
+// System.arraycopy(data, 0, temp, 0, index);
+// temp[index] = value;
+// System.arraycopy(data, index, temp, index+1, count - index);
+// data = temp;
+//
+// } else {
+// // data[] has room to grow
+// // for() loop believed to be faster than System.arraycopy over itself
+// for (int i = count; i > index; --i) {
+// data[i] = data[i-1];
+// }
+// data[index] = value;
+// count++;
+// }
+
+
+ /** Return the first index of a particular value. */
+ public int index(double what) {
+ /*
+ if (indexCache != null) {
+ try {
+ return indexCache.get(what);
+ } catch (Exception e) { // not there
+ return -1;
+ }
+ }
+ */
+ for (int i = 0; i < count; i++) {
+ if (data[i] == what) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Check if a number is a part of the list
+ */
+ public boolean hasValue(double value) {
+ if (Double.isNaN(value)) {
+ for (int i = 0; i < count; i++) {
+ if (Double.isNaN(data[i])) {
+ return true;
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ if (data[i] == value) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ private void boundsProblem(int index, String method) {
+ final String msg = String.format("The list size is %d. " +
+ "You cannot %s() to element %d.", count, method, index);
+ throw new ArrayIndexOutOfBoundsException(msg);
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Add to a value
+ */
+ public void add(int index, double amount) {
+ if (index < count) {
+ data[index] += amount;
+ } else {
+ boundsProblem(index, "add");
+ }
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Subtract from a value
+ */
+ public void sub(int index, double amount) {
+ if (index < count) {
+ data[index] -= amount;
+ } else {
+ boundsProblem(index, "sub");
+ }
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Multiply a value
+ */
+ public void mult(int index, double amount) {
+ if (index < count) {
+ data[index] *= amount;
+ } else {
+ boundsProblem(index, "mult");
+ }
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Divide a value
+ */
+ public void div(int index, double amount) {
+ if (index < count) {
+ data[index] /= amount;
+ } else {
+ boundsProblem(index, "div");
+ }
+ }
+
+
+ private void checkMinMax(String functionName) {
+ if (count == 0) {
+ String msg =
+ String.format("Cannot use %s() on an empty %s.",
+ functionName, getClass().getSimpleName());
+ throw new RuntimeException(msg);
+ }
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Return the smallest value
+ */
+ public double min() {
+ checkMinMax("min");
+ int index = minIndex();
+ return index == -1 ? Double.NaN : data[index];
+ }
+
+
+ public int minIndex() {
+ checkMinMax("minIndex");
+ double m = Double.NaN;
+ int mi = -1;
+ for (int i = 0; i < count; i++) {
+ // find one good value to start
+ if (data[i] == data[i]) {
+ m = data[i];
+ mi = i;
+
+ // calculate the rest
+ for (int j = i+1; j < count; j++) {
+ double d = data[j];
+ if (!Double.isNaN(d) && (d < m)) {
+ m = data[j];
+ mi = j;
+ }
+ }
+ break;
+ }
+ }
+ return mi;
+ }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Return the largest value
+ */
+ public double max() {
+ checkMinMax("max");
+ int index = maxIndex();
+ return index == -1 ? Double.NaN : data[index];
+ }
+
+
+ public int maxIndex() {
+ checkMinMax("maxIndex");
+ double m = Double.NaN;
+ int mi = -1;
+ for (int i = 0; i < count; i++) {
+ // find one good value to start
+ if (data[i] == data[i]) {
+ m = data[i];
+ mi = i;
+
+ // calculate the rest
+ for (int j = i+1; j < count; j++) {
+ double d = data[j];
+ if (!Double.isNaN(d) && (d > m)) {
+ m = data[j];
+ mi = j;
+ }
+ }
+ break;
+ }
+ }
+ return mi;
+ }
+
+
+ public double sum() {
+ double sum = 0;
+ for (int i = 0; i < count; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+
+
+ /**
+ * Sorts the array in place.
+ *
+ * @webref doublelist:method
+ * @brief Sorts an array, lowest to highest
+ */
+ public void sort() {
+ Arrays.sort(data, 0, count);
+ }
+
+
+ /**
+ * Reverse sort, orders values from highest to lowest
+ *
+ * @webref doublelist:method
+ * @brief Reverse sort, orders values from highest to lowest
+ */
+ public void sortReverse() {
+ new Sort() {
+ @Override
+ public int size() {
+ // if empty, don't even mess with the NaN check, it'll AIOOBE
+ if (count == 0) {
+ return 0;
+ }
+ // move NaN values to the end of the list and don't sort them
+ int right = count - 1;
+ while (data[right] != data[right]) {
+ right--;
+ if (right == -1) { // all values are NaN
+ return 0;
+ }
+ }
+ for (int i = right; i >= 0; --i) {
+ double v = data[i];
+ if (v != v) {
+ data[i] = data[right];
+ data[right] = v;
+ --right;
+ }
+ }
+ return right + 1;
+ }
+
+ @Override
+ public int compare(int a, int b) {
+ double diff = data[b] - data[a];
+ return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
+ }
+
+ @Override
+ public void swap(int a, int b) {
+ double temp = data[a];
+ data[a] = data[b];
+ data[b] = temp;
+ }
+ }.run();
+ }
+
+
+ // use insert()
+// public void splice(int index, int value) {
+// }
+
+
+// public void subset(int start) {
+// subset(start, count - start);
+// }
+
+
+// public void subset(int start, int num) {
+// for (int i = 0; i < num; i++) {
+// data[i] = data[i+start];
+// }
+// count = num;
+// }
+
+
+ /**
+ * @webref doublelist:method
+ * @brief Reverse the order of the list elements
+ */
+ public void reverse() {
+ int ii = count - 1;
+ for (int i = 0; i < count/2; i++) {
+ double t = data[i];
+ data[i] = data[ii];
+ data[ii] = t;
+ --ii;
+ }
+ }
+
+
+ /**
+ * Randomize the order of the list elements. Note that this does not
+ * obey the randomSeed() function in PApplet.
+ *
+ * @webref doublelist:method
+ * @brief Randomize the order of the list elements
+ */
+ public void shuffle() {
+ Random r = new Random();
+ int num = count;
+ while (num > 1) {
+ int value = r.nextInt(num);
+ num--;
+ double temp = data[num];
+ data[num] = data[value];
+ data[value] = temp;
+ }
+ }
+
+
+ /**
+ * Randomize the list order using the random() function from the specified
+ * sketch, allowing shuffle() to use its current randomSeed() setting.
+ */
+ public void shuffle(PApplet sketch) {
+ int num = count;
+ while (num > 1) {
+ int value = (int) sketch.random(num);
+ num--;
+ double temp = data[num];
+ data[num] = data[value];
+ data[value] = temp;
+ }
+ }
+
+
+ public DoubleList copy() {
+ DoubleList outgoing = new DoubleList(data);
+ outgoing.count = count;
+ return outgoing;
+ }
+
+
+ /**
+ * Returns the actual array being used to store the data. For advanced users,
+ * this is the fastest way to access a large list. Suitable for iterating
+ * with a for() loop, but modifying the list will have terrible consequences.
+ */
+ public double[] values() {
+ crop();
+ return data;
+ }
+
+
+ /** Implemented this way so that we can use a FloatList in a for loop. */
+ @Override
+ public Iterator iterator() {
+// }
+//
+//
+// public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ DoubleList.this.remove(index);
+ index--;
+ }
+
+ public Double next() {
+ return data[++index];
+ }
+
+ public boolean hasNext() {
+ return index+1 < count;
+ }
+ };
+ }
+
+
+ /**
+ * Create a new array with a copy of all the values.
+ * @return an array sized by the length of the list with each of the values.
+ * @webref doublelist:method
+ * @brief Create a new array with a copy of all the values
+ */
+ public double[] array() {
+ return array(null);
+ }
+
+
+ /**
+ * Copy values into the specified array. If the specified array is null or
+ * not the same size, a new array will be allocated.
+ * @param array
+ */
+ public double[] array(double[] array) {
+ if (array == null || array.length != count) {
+ array = new double[count];
+ }
+ System.arraycopy(data, 0, array, 0, count);
+ return array;
+ }
+
+
+ /**
+ * Returns a normalized version of this array. Called getPercent() for
+ * consistency with the Dict classes. It's a getter method because it needs
+ * to returns a new list (because IntList/Dict can't do percentages or
+ * normalization in place on int values).
+ */
+ public DoubleList getPercent() {
+ double sum = 0;
+ for (double value : array()) {
+ sum += value;
+ }
+ DoubleList outgoing = new DoubleList(count);
+ for (int i = 0; i < count; i++) {
+ double percent = data[i] / sum;
+ outgoing.set(i, percent);
+ }
+ return outgoing;
+ }
+
+
+ public DoubleList getSubset(int start) {
+ return getSubset(start, count - start);
+ }
+
+
+ public DoubleList getSubset(int start, int num) {
+ double[] subset = new double[num];
+ System.arraycopy(data, start, subset, 0, num);
+ return new DoubleList(subset);
+ }
+
+
+ public String join(String separator) {
+ if (count == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(data[0]);
+ for (int i = 1; i < count; i++) {
+ sb.append(separator);
+ sb.append(data[i]);
+ }
+ return sb.toString();
+ }
+
+
+ public void print() {
+ for (int i = 0; i < count; i++) {
+ System.out.format("[%d] %f%n", i, data[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write entries to a PrintWriter, one per line
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(data[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ return "[ " + join(", ") + " ]";
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
+ }
+}
diff --git a/core/src/processing/data/FloatDict.java b/libs/processing-core/src/main/java/processing/data/FloatDict.java
similarity index 56%
rename from core/src/processing/data/FloatDict.java
rename to libs/processing-core/src/main/java/processing/data/FloatDict.java
index 8d146ee74..9495563ad 100644
--- a/core/src/processing/data/FloatDict.java
+++ b/libs/processing-core/src/main/java/processing/data/FloatDict.java
@@ -3,6 +3,7 @@
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import processing.core.PApplet;
@@ -23,7 +24,7 @@ public class FloatDict {
protected float[] values;
/** Internal implementation for faster lookups */
- private HashMap indices = new HashMap();
+ private HashMap indices = new HashMap<>();
public FloatDict() {
@@ -53,23 +54,22 @@ public FloatDict(int length) {
* @nowebref
*/
public FloatDict(BufferedReader reader) {
-// public FloatHash(PApplet parent, String filename) {
String[] lines = PApplet.loadStrings(reader);
keys = new String[lines.length];
values = new float[lines.length];
-// boolean csv = (lines[0].indexOf('\t') == -1);
for (int i = 0; i < lines.length; i++) {
-// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t');
String[] pieces = PApplet.split(lines[i], '\t');
if (pieces.length == 2) {
keys[count] = pieces[0];
values[count] = PApplet.parseFloat(pieces[1]);
+ indices.put(pieces[0], count);
count++;
}
}
}
+
/**
* @nowebref
*/
@@ -85,6 +85,28 @@ public FloatDict(String[] keys, float[] values) {
}
}
+
+ /**
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
+ *
+ * new FloatDict(new Object[][] {
+ * { "key1", 1 },
+ * { "key2", 2 }
+ * });
+ *
+ */
+ public FloatDict(Object[][] pairs) {
+ count = pairs.length;
+ this.keys = new String[count];
+ this.values = new float[count];
+ for (int i = 0; i < count; i++) {
+ keys[i] = (String) pairs[i][0];
+ values[i] = (Float) pairs[i][1];
+ indices.put(keys[i], i);
+ }
+ }
+
+
/**
* @webref floatdict:method
* @brief Returns the number of key/value pairs
@@ -94,6 +116,31 @@ public int size() {
}
+ /**
+ * Resize the internal data, this can only be used to shrink the list.
+ * Helpful for situations like sorting and then grabbing the top 50 entries.
+ */
+ public void resize(int length) {
+ if (length == count) return;
+
+ if (length > count) {
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
+ }
+ if (length < 1) {
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
+ }
+
+ String[] newKeys = new String[length];
+ float[] newValues = new float[length];
+ PApplet.arrayCopy(keys, newKeys, length);
+ PApplet.arrayCopy(values, newValues, length);
+ keys = newKeys;
+ values = newValues;
+ count = length;
+ resetIndices();
+ }
+
+
/**
* Remove all entries.
*
@@ -102,10 +149,67 @@ public int size() {
*/
public void clear() {
count = 0;
- indices = new HashMap();
+ indices = new HashMap<>();
+ }
+
+
+ private void resetIndices() {
+ indices = new HashMap<>(count);
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public class Entry {
+ public String key;
+ public float value;
+
+ Entry(String key, float value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+
+ public Iterable entries() {
+ return new Iterable() {
+
+ public Iterator iterator() {
+ return entryIterator();
+ }
+ };
+ }
+
+
+ public Iterator entryIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Entry next() {
+ ++index;
+ Entry e = new Entry(keys[index], values[index]);
+ return e;
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
}
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
public String key(int index) {
return keys[index];
}
@@ -119,70 +223,36 @@ protected void crop() {
}
-// /**
-// * Return the internal array being used to store the keys. Allocated but
-// * unused entries will be removed. This array should not be modified.
-// */
-// public String[] keys() {
-// crop();
-// return keys;
-// }
-
- /**
- * @webref floatdict:method
- * @brief Return the internal array being used to store the keys
- */
public Iterable keys() {
return new Iterable() {
+ @Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
-
- public void remove() {
- removeIndex(index);
- }
-
- public String next() {
- return key(++index);
- }
-
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ return keyIterator();
}
};
}
- /*
- static class KeyIterator implements Iterator {
- FloatHash parent;
- int index;
-
- public KeyIterator(FloatHash parent) {
- this.parent = parent;
- index = -1;
- }
-
- public void remove() {
- parent.removeIndex(index);
- }
+ // Use this to iterate when you want to be able to remove elements along the way
+ public Iterator keyIterator() {
+ return new Iterator() {
+ int index = -1;
- public String next() {
- return parent.key(++index);
- }
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
- public boolean hasNext() {
- return index+1 < parent.size();
- }
+ public String next() {
+ return key(++index);
+ }
- public void reset() {
- index = -1;
- }
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
}
- */
/**
@@ -192,6 +262,7 @@ public void reset() {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
+ crop();
return keyArray(null);
}
@@ -210,11 +281,6 @@ public float value(int index) {
}
-// public float[] values() {
-// crop();
-// return values;
-// }
-
/**
* @webref floatdict:method
* @brief Return the internal array being used to store the values
@@ -222,22 +288,29 @@ public float value(int index) {
public Iterable values() {
return new Iterable() {
+ @Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
+ return valueIterator();
+ }
+ };
+ }
- public void remove() {
- removeIndex(index);
- }
- public Float next() {
- return value(++index);
- }
+ public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Float next() {
+ return value(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
}
};
}
@@ -250,6 +323,7 @@ public boolean hasNext() {
* @brief Create a new array and copy each of the values into it
*/
public float[] valueArray() {
+ crop();
return valueArray(null);
}
@@ -276,7 +350,18 @@ public float[] valueArray(float[] array) {
*/
public float get(String key) {
int index = index(key);
- if (index == -1) return 0;
+ if (index == -1) {
+ throw new IllegalArgumentException("No key named '" + key + "'");
+ }
+ return values[index];
+ }
+
+
+ public float get(String key, float alternate) {
+ int index = index(key);
+ if (index == -1) {
+ return alternate;
+ }
return values[index];
}
@@ -295,6 +380,15 @@ public void set(String key, float amount) {
}
+ public void setIndex(int index, String key, float value) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ keys[index] = key;
+ values[index] = value;
+ }
+
+
/**
* @webref floatdict:method
* @brief Check if a key is a part of the data structure
@@ -304,18 +398,6 @@ public boolean hasKey(String key) {
}
-// /** Increase the value of a specific key by 1. */
-// public void inc(String key) {
-// inc(key, 1);
-//// int index = index(key);
-//// if (index == -1) {
-//// create(key, 1);
-//// } else {
-//// values[index]++;
-//// }
-// }
-
-
/**
* @webref floatdict:method
* @brief Add to a value
@@ -330,12 +412,6 @@ public void add(String key, float amount) {
}
-// /** Decrease the value of a key by 1. */
-// public void dec(String key) {
-// inc(key, -1);
-// }
-
-
/**
* @webref floatdict:method
* @brief Subtract from a value
@@ -384,8 +460,10 @@ private void checkMinMax(String functionName) {
* @brief Return the smallest value
*/
public int minIndex() {
- checkMinMax("minIndex");
- // Will still return NaN if there is 1 or more entries, and they're all NaN
+ //checkMinMax("minIndex");
+ if (count == 0) return -1;
+
+ // Will still return NaN if there are 1 or more entries, and they're all NaN
float m = Float.NaN;
int mi = -1;
for (int i = 0; i < count; i++) {
@@ -397,7 +475,7 @@ public int minIndex() {
// calculate the rest
for (int j = i+1; j < count; j++) {
float d = values[j];
- if (!Float.isNaN(d) && (d < m)) {
+ if ((d == d) && (d < m)) {
m = values[j];
mi = j;
}
@@ -409,15 +487,25 @@ public int minIndex() {
}
+ // return the key for the minimum value
public String minKey() {
checkMinMax("minKey");
- return keys[minIndex()];
+ int index = minIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
}
+ // return the minimum value, or throw an error if there are no values
public float minValue() {
checkMinMax("minValue");
- return values[minIndex()];
+ int index = minIndex();
+ if (index == -1) {
+ return Float.NaN;
+ }
+ return values[index];
}
@@ -427,7 +515,10 @@ public float minValue() {
*/
// The index of the entry that has the max value. Reference above is incorrect.
public int maxIndex() {
- checkMinMax("maxIndex");
+ //checkMinMax("maxIndex");
+ if (count == 0) {
+ return -1;
+ }
// Will still return NaN if there is 1 or more entries, and they're all NaN
float m = Float.NaN;
int mi = -1;
@@ -452,17 +543,46 @@ public int maxIndex() {
}
- /** The key for a max value. */
+ /** The key for a max value; null if empty or everything is NaN (no max). */
public String maxKey() {
- checkMinMax("maxKey");
- return keys[maxIndex()];
+ //checkMinMax("maxKey");
+ int index = maxIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
}
- /** The max value. */
+ /** The max value. (Or NaN if no entries or they're all NaN.) */
public float maxValue() {
- checkMinMax("maxValue");
- return values[maxIndex()];
+ //checkMinMax("maxValue");
+ int index = maxIndex();
+ if (index == -1) {
+ return Float.NaN;
+ }
+ return values[index];
+ }
+
+
+ public float sum() {
+ double amount = sumDouble();
+ if (amount > Float.MAX_VALUE) {
+ throw new RuntimeException("sum() exceeds " + Float.MAX_VALUE + ", use sumDouble()");
+ }
+ if (amount < -Float.MAX_VALUE) {
+ throw new RuntimeException("sum() lower than " + -Float.MAX_VALUE + ", use sumDouble()");
+ }
+ return (float) amount;
+ }
+
+
+ public double sumDouble() {
+ double sum = 0;
+ for (int i = 0; i < count; i++) {
+ sum += values[i];
+ }
+ return sum;
}
@@ -477,7 +597,7 @@ protected void create(String what, float much) {
keys = PApplet.expand(keys);
values = PApplet.expand(values);
}
- indices.put(what, new Integer(count));
+ indices.put(what, Integer.valueOf(count));
keys[count] = what;
values[count] = much;
count++;
@@ -488,21 +608,22 @@ protected void create(String what, float much) {
* @webref floatdict:method
* @brief Remove a key/value pair
*/
- public int remove(String key) {
+ public float remove(String key) {
int index = index(key);
- if (index != -1) {
- removeIndex(index);
+ if (index == -1) {
+ throw new NoSuchElementException("'" + key + "' not found");
}
- return index;
+ float value = values[index];
+ removeIndex(index);
+ return value;
}
- public String removeIndex(int index) {
+ public float removeIndex(int index) {
if (index < 0 || index >= count) {
throw new ArrayIndexOutOfBoundsException(index);
}
- String key = keys[index];
- //System.out.println("index is " + which + " and " + keys[which]);
+ float value = values[index];
indices.remove(keys[index]);
for (int i = index; i < count-1; i++) {
keys[i] = keys[i+1];
@@ -512,11 +633,11 @@ public String removeIndex(int index) {
count--;
keys[count] = null;
values[count] = 0;
- return key;
+ return value;
}
- protected void swap(int a, int b) {
+ public void swap(int a, int b) {
String tkey = keys[a];
float tvalue = values[a];
keys[a] = keys[b];
@@ -524,24 +645,11 @@ protected void swap(int a, int b) {
keys[b] = tkey;
values[b] = tvalue;
- indices.put(keys[a], new Integer(a));
- indices.put(keys[b], new Integer(b));
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
}
-// abstract class InternalSort extends Sort {
-// @Override
-// public int size() {
-// return count;
-// }
-//
-// @Override
-// public void swap(int a, int b) {
-// FloatHash.this.swap(a, b);
-// }
-// }
-
-
/**
* Sort the keys alphabetically (ignoring case). Uses the value as a
* tie-breaker (only really possible with a key that has a case change).
@@ -550,36 +658,16 @@ protected void swap(int a, int b) {
* @brief Sort the keys alphabetically
*/
public void sortKeys() {
- sortImpl(true, false);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-// int result = keys[a].compareToIgnoreCase(keys[b]);
-// if (result != 0) {
-// return result;
-// }
-// return values[b] - values[a];
-// }
-// }.run();
+ sortImpl(true, false, true);
}
/**
* @webref floatdict:method
- * @brief Sort the keys alphabetially in reverse
+ * @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
- sortImpl(true, true);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-// int result = keys[b].compareToIgnoreCase(keys[a]);
-// if (result != 0) {
-// return result;
-// }
-// return values[a] - values[b];
-// }
-// }.run();
+ sortImpl(true, true, true);
}
@@ -590,13 +678,17 @@ public void sortKeysReverse() {
* @brief Sort by values in ascending order
*/
public void sortValues() {
- sortImpl(false, false);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-//
-// }
-// }.run();
+ sortValues(true);
+ }
+
+
+ /**
+ * Set true to ensure that the order returned is identical. Slightly
+ * slower because the tie-breaker for identical values compares the keys.
+ * @param stable
+ */
+ public void sortValues(boolean stable) {
+ sortImpl(false, false, stable);
}
@@ -605,71 +697,65 @@ public void sortValues() {
* @brief Sort by values in descending order
*/
public void sortValuesReverse() {
- sortImpl(false, true);
-// new InternalSort() {
-// @Override
-// public float compare(int a, int b) {
-// float diff = values[b] - values[a];
-// if (diff == 0 && keys[a] != null && keys[b] != null) {
-// diff = keys[a].compareToIgnoreCase(keys[b]);
-// }
-// return descending ? diff : -diff;
-// }
-// }.run();
- }
-
-
-// // ascending puts the largest value at the end
-// // descending puts the largest value at 0
-// public void sortValues(final boolean descending, final boolean tiebreaker) {
-// Sort s = new Sort() {
-// @Override
-// public int size() {
-// return count;
-// }
-//
-// @Override
-// public float compare(int a, int b) {
-// float diff = values[b] - values[a];
-// if (tiebreaker) {
-// if (diff == 0) {
-// diff = keys[a].compareToIgnoreCase(keys[b]);
-// }
-// }
-// return descending ? diff : -diff;
-// }
-//
-// @Override
-// public void swap(int a, int b) {
-// FloatHash.this.swap(a, b);
-// }
-// };
-// s.run();
-// }
-
-
- protected void sortImpl(final boolean useKeys, final boolean reverse) {
+ sortValuesReverse(true);
+ }
+
+
+ public void sortValuesReverse(boolean stable) {
+ sortImpl(false, true, stable);
+ }
+
+
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
+ final boolean stable) {
Sort s = new Sort() {
@Override
public int size() {
- return count;
+ if (useKeys) {
+ return count; // don't worry about NaN values
+
+ } else if (count == 0) { // skip the NaN check, it'll AIOOBE
+ return 0;
+
+ } else { // first move NaN values to the end of the list
+ int right = count - 1;
+ while (values[right] != values[right]) {
+ right--;
+ if (right == -1) {
+ return 0; // all values are NaN
+ }
+ }
+ for (int i = right; i >= 0; --i) {
+ if (Float.isNaN(values[i])) {
+ swap(i, right);
+ --right;
+ }
+ }
+ return right + 1;
+ }
}
@Override
- public float compare(int a, int b) {
+ public int compare(int a, int b) {
float diff = 0;
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
if (diff == 0) {
- return values[a] - values[b];
+ diff = values[a] - values[b];
}
} else { // sort values
diff = values[a] - values[b];
- if (diff == 0) {
+ if (diff == 0 && stable) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
- return reverse ? -diff : diff;
+ if (diff == 0) {
+ return 0;
+ } else if (reverse) {
+ return diff < 0 ? 1 : -1;
+ } else {
+ return diff < 0 ? -1 : 1;
+ }
}
@Override
@@ -678,19 +764,19 @@ public void swap(int a, int b) {
}
};
s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ resetIndices();
}
/**
* Sum all of the values in this dictionary, then return a new FloatDict of
* each key, divided by the total sum. The total for all values will be ~1.0.
- * @return a Dict with the original keys, mapped to their pct of the total
+ * @return a FloatDict with the original keys, mapped to their pct of the total
*/
public FloatDict getPercent() {
- double sum = 0;
- for (float value : valueArray()) {
- sum += value;
- }
+ double sum = sum();
FloatDict outgoing = new FloatDict();
for (int i = 0; i < size(); i++) {
double percent = value(i) / sum;
@@ -713,12 +799,21 @@ public FloatDict copy() {
}
-// /**
-// * Write tab-delimited entries out to the console.
-// */
-// public void print() {
-// write(new PrintWriter(System.out));
-// }
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
/**
@@ -733,17 +828,20 @@ public void write(PrintWriter writer) {
}
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ StringList items = new StringList();
+ for (int i = 0; i < count; i++) {
+ items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
+ }
+ return "{ " + items.join(", ") + " }";
+ }
+
+
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName() + " size=" + size() + " { ");
- for (int i = 0; i < size(); i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append("\"" + keys[i] + "\": " + values[i]);
- }
- sb.append(" }");
- return sb.toString();
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
}
}
diff --git a/core/src/processing/data/FloatList.java b/libs/processing-core/src/main/java/processing/data/FloatList.java
similarity index 77%
rename from core/src/processing/data/FloatList.java
rename to libs/processing-core/src/main/java/processing/data/FloatList.java
index f17f44784..863b05658 100644
--- a/core/src/processing/data/FloatList.java
+++ b/libs/processing-core/src/main/java/processing/data/FloatList.java
@@ -1,5 +1,7 @@
package processing.data;
+import java.io.File;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
@@ -28,6 +30,7 @@ public FloatList() {
data = new float[10];
}
+
/**
* @nowebref
*/
@@ -35,6 +38,7 @@ public FloatList(int length) {
data = new float[length];
}
+
/**
* @nowebref
*/
@@ -44,13 +48,49 @@ public FloatList(float[] list) {
System.arraycopy(list, 0, data, 0, count);
}
+
/**
+ * Construct an FloatList from an iterable pile of objects.
+ * For instance, a float array, an array of strings, who knows).
+ * Un-parseable or null values will be set to NaN.
* @nowebref
*/
- public FloatList(Iterable iter) {
+ public FloatList(Iterable iter) {
this(10);
- for (float v : iter) {
- append(v);
+ for (Object o : iter) {
+ if (o == null) {
+ append(Float.NaN);
+ } else if (o instanceof Number) {
+ append(((Number) o).floatValue());
+ } else {
+ append(PApplet.parseFloat(o.toString().trim()));
+ }
+ }
+ crop();
+ }
+
+
+ /**
+ * Construct an FloatList from a random pile of objects.
+ * Un-parseable or null values will be set to NaN.
+ */
+ public FloatList(Object... items) {
+ // nuts, no good way to pass missingValue to this fn (varargs must be last)
+ final float missingValue = Float.NaN;
+
+ count = items.length;
+ data = new float[count];
+ int index = 0;
+ for (Object o : items) {
+ float value = missingValue;
+ if (o != null) {
+ if (o instanceof Number) {
+ value = ((Number) o).floatValue();
+ } else {
+ value = PApplet.parseFloat(o.toString().trim(), missingValue);
+ }
+ }
+ data[index++] = value;
}
}
@@ -110,6 +150,9 @@ public void clear() {
* @brief Get an entry at a particular index
*/
public float get(int index) {
+ if (index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
return data[index];
}
@@ -134,6 +177,22 @@ public void set(int index, float what) {
}
+ /** Just an alias for append(), but matches pop() */
+ public void push(float value) {
+ append(value);
+ }
+
+
+ public float pop() {
+ if (count == 0) {
+ throw new RuntimeException("Can't call pop() on an empty list");
+ }
+ float value = get(count-1);
+ count--;
+ return value;
+ }
+
+
/**
* Remove an element from the specified index.
*
@@ -266,6 +325,14 @@ public void append(FloatList list) {
}
+ /** Add this value, but only if it's not already in the list. */
+ public void appendUnique(float value) {
+ if (!hasValue(value)) {
+ append(value);
+ }
+ }
+
+
// public void insert(int index, int value) {
// if (index+1 > count) {
// if (index+1 < data.length) {
@@ -296,8 +363,13 @@ public void append(FloatList list) {
// }
+ public void insert(int index, float value) {
+ insert(index, new float[] { value });
+ }
+
+
// same as splice
- public void insert(int index, int[] values) {
+ public void insert(int index, float[] values) {
if (index < 0) {
throw new IllegalArgumentException("insert() index cannot be negative: it was " + index);
}
@@ -325,7 +397,7 @@ public void insert(int index, int[] values) {
}
- public void insert(int index, IntList list) {
+ public void insert(int index, FloatList list) {
insert(index, list.values());
}
@@ -415,12 +487,23 @@ public boolean hasValue(float value) {
}
+ private void boundsProblem(int index, String method) {
+ final String msg = String.format("The list size is %d. " +
+ "You cannot %s() to element %d.", count, method, index);
+ throw new ArrayIndexOutOfBoundsException(msg);
+ }
+
+
/**
* @webref floatlist:method
* @brief Add to a value
*/
public void add(int index, float amount) {
- data[index] += amount;
+ if (index < count) {
+ data[index] += amount;
+ } else {
+ boundsProblem(index, "add");
+ }
}
@@ -429,7 +512,11 @@ public void add(int index, float amount) {
* @brief Subtract from a value
*/
public void sub(int index, float amount) {
- data[index] -= amount;
+ if (index < count) {
+ data[index] -= amount;
+ } else {
+ boundsProblem(index, "sub");
+ }
}
@@ -438,7 +525,11 @@ public void sub(int index, float amount) {
* @brief Multiply a value
*/
public void mult(int index, float amount) {
- data[index] *= amount;
+ if (index < count) {
+ data[index] *= amount;
+ } else {
+ boundsProblem(index, "mult");
+ }
}
@@ -447,7 +538,11 @@ public void mult(int index, float amount) {
* @brief Divide a value
*/
public void div(int index, float amount) {
- data[index] /= amount;
+ if (index < count) {
+ data[index] /= amount;
+ } else {
+ boundsProblem(index, "div");
+ }
}
@@ -534,11 +629,23 @@ public int maxIndex() {
public float sum() {
- double outgoing = 0;
+ double amount = sumDouble();
+ if (amount > Float.MAX_VALUE) {
+ throw new RuntimeException("sum() exceeds " + Float.MAX_VALUE + ", use sumDouble()");
+ }
+ if (amount < -Float.MAX_VALUE) {
+ throw new RuntimeException("sum() lower than " + -Float.MAX_VALUE + ", use sumDouble()");
+ }
+ return (float) amount;
+ }
+
+
+ public double sumDouble() {
+ double sum = 0;
for (int i = 0; i < count; i++) {
- outgoing += data[i];
+ sum += data[i];
}
- return (float) outgoing;
+ return sum;
}
@@ -563,12 +670,33 @@ public void sortReverse() {
new Sort() {
@Override
public int size() {
- return count;
+ // if empty, don't even mess with the NaN check, it'll AIOOBE
+ if (count == 0) {
+ return 0;
+ }
+ // move NaN values to the end of the list and don't sort them
+ int right = count - 1;
+ while (data[right] != data[right]) {
+ right--;
+ if (right == -1) { // all values are NaN
+ return 0;
+ }
+ }
+ for (int i = right; i >= 0; --i) {
+ float v = data[i];
+ if (v != v) {
+ data[i] = data[right];
+ data[right] = v;
+ --right;
+ }
+ }
+ return right + 1;
}
@Override
- public float compare(int a, int b) {
- return data[b] - data[a];
+ public int compare(int a, int b) {
+ float diff = data[b] - data[a];
+ return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
}
@Override
@@ -601,7 +729,7 @@ public void swap(int a, int b) {
/**
* @webref floatlist:method
- * @brief Reverse sort, orders values by first digit
+ * @brief Reverse the order of the list elements
*/
public void reverse() {
int ii = count - 1;
@@ -669,6 +797,7 @@ public float[] values() {
/** Implemented this way so that we can use a FloatList in a for loop. */
+ @Override
public Iterator iterator() {
// }
//
@@ -679,6 +808,7 @@ public Iterator iterator() {
public void remove() {
FloatList.this.remove(index);
+ index--;
}
public Float next() {
@@ -704,7 +834,8 @@ public float[] array() {
/**
- * Copy as many values as possible into the specified array.
+ * Copy values into the specified array. If the specified array is null or
+ * not the same size, a new array will be allocated.
* @param array
*/
public float[] array(float[] array) {
@@ -762,17 +893,44 @@ public String join(String separator) {
}
+ public void print() {
+ for (int i = 0; i < count; i++) {
+ System.out.format("[%d] %f%n", i, data[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write entries to a PrintWriter, one per line
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(data[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ return "[ " + join(", ") + " ]";
+ }
+
+
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName() + " size=" + size() + " [ ");
- for (int i = 0; i < size(); i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append(i + ": " + data[i]);
- }
- sb.append(" ]");
- return sb.toString();
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
}
}
diff --git a/core/src/processing/data/IntDict.java b/libs/processing-core/src/main/java/processing/data/IntDict.java
similarity index 62%
rename from core/src/processing/data/IntDict.java
rename to libs/processing-core/src/main/java/processing/data/IntDict.java
index 7bc2bae55..96913591f 100644
--- a/core/src/processing/data/IntDict.java
+++ b/libs/processing-core/src/main/java/processing/data/IntDict.java
@@ -3,6 +3,7 @@
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import processing.core.PApplet;
@@ -23,30 +24,7 @@ public class IntDict {
protected int[] values;
/** Internal implementation for faster lookups */
- private HashMap indices = new HashMap();
-
-
-// /**
-// * Create a new object by counting the number of times each unique entry
-// * shows up in the specified String array.
-// */
-// static public IntHash fromTally(String[] list) {
-// IntHash outgoing = new IntHash();
-// for (String s : list) {
-// outgoing.inc(s);
-// }
-// outgoing.crop();
-// return outgoing;
-// }
-//
-//
-// static public IntHash fromOrder(String[] list) {
-// IntHash outgoing = new IntHash();
-// for (int i = 0; i < list.length; i++) {
-// outgoing.set(list[i], i);
-// }
-// return outgoing;
-// }
+ private HashMap indices = new HashMap<>();
public IntDict() {
@@ -76,18 +54,16 @@ public IntDict(int length) {
* @nowebref
*/
public IntDict(BufferedReader reader) {
-// public IntHash(PApplet parent, String filename) {
String[] lines = PApplet.loadStrings(reader);
keys = new String[lines.length];
values = new int[lines.length];
-// boolean csv = (lines[0].indexOf('\t') == -1);
for (int i = 0; i < lines.length; i++) {
-// String[] pieces = csv ? Table.splitLineCSV(lines[i]) : PApplet.split(lines[i], '\t');
String[] pieces = PApplet.split(lines[i], '\t');
if (pieces.length == 2) {
keys[count] = pieces[0];
values[count] = PApplet.parseInt(pieces[1]);
+ indices.put(pieces[0], count);
count++;
}
}
@@ -108,6 +84,28 @@ public IntDict(String[] keys, int[] values) {
}
}
+
+ /**
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
+ *
+ * new FloatDict(new Object[][] {
+ * { "key1", 1 },
+ * { "key2", 2 }
+ * });
+ *
+ */
+ public IntDict(Object[][] pairs) {
+ count = pairs.length;
+ this.keys = new String[count];
+ this.values = new int[count];
+ for (int i = 0; i < count; i++) {
+ keys[i] = (String) pairs[i][0];
+ values[i] = (Integer) pairs[i][1];
+ indices.put(keys[i], i);
+ }
+ }
+
+
/**
* Returns the number of key/value pairs
*
@@ -119,6 +117,29 @@ public int size() {
}
+ /**
+ * Resize the internal data, this can only be used to shrink the list.
+ * Helpful for situations like sorting and then grabbing the top 50 entries.
+ */
+ public void resize(int length) {
+ if (length > count) {
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
+ }
+ if (length < 1) {
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
+ }
+
+ String[] newKeys = new String[length];
+ int[] newValues = new int[length];
+ PApplet.arrayCopy(keys, newKeys, length);
+ PApplet.arrayCopy(values, newValues, length);
+ keys = newKeys;
+ values = newValues;
+ count = length;
+ resetIndices();
+ }
+
+
/**
* Remove all entries.
*
@@ -127,82 +148,86 @@ public int size() {
*/
public void clear() {
count = 0;
- indices = new HashMap();
+ indices = new HashMap<>();
}
+ private void resetIndices() {
+ indices = new HashMap<>(count);
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public class Entry {
+ public String key;
+ public int value;
+
+ Entry(String key, int value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+
+ public Iterable entries() {
+ return new Iterable() {
+
+ public Iterator iterator() {
+ return entryIterator();
+ }
+ };
+ }
+
+
+ public Iterator entryIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Entry next() {
+ ++index;
+ Entry e = new Entry(keys[index], values[index]);
+ return e;
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
public String key(int index) {
return keys[index];
}
-// private void crop() {
-// if (count != keys.length) {
-// keys = PApplet.subset(keys, 0, count);
-// values = PApplet.subset(values, 0, count);
-// }
-// }
+ protected void crop() {
+ if (count != keys.length) {
+ keys = PApplet.subset(keys, 0, count);
+ values = PApplet.subset(values, 0, count);
+ }
+ }
- /**
- * Return the internal array being used to store the keys. Allocated but
- * unused entries will be removed. This array should not be modified.
- *
- * @webref intdict:method
- * @brief Return the internal array being used to store the keys
- */
-// public String[] keys() {
-// crop();
-// return keys;
-// }
-
-
-// public Iterable keys() {
-// return new Iterable() {
-//
-// @Override
-// public Iterator iterator() {
-// return new Iterator() {
-// int index = -1;
-//
-// public void remove() {
-// removeIndex(index);
-// }
-//
-// public String next() {
-// return key(++index);
-// }
-//
-// public boolean hasNext() {
-// return index+1 < size();
-// }
-// };
-// }
-// };
-// }
-
-
- // Use this with 'for' loops
public Iterable keys() {
return new Iterable() {
+ @Override
public Iterator iterator() {
return keyIterator();
-// return new Iterator() {
-// int index = -1;
-//
-// public void remove() {
-// removeIndex(index);
-// }
-//
-// public String next() {
-// return key(++index);
-// }
-//
-// public boolean hasNext() {
-// return index+1 < size();
-// }
-// };
}
};
}
@@ -215,6 +240,7 @@ public Iterator keyIterator() {
public void remove() {
removeIndex(index);
+ index--;
}
public String next() {
@@ -235,6 +261,7 @@ public boolean hasNext() {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
+ crop();
return keyArray(null);
}
@@ -255,11 +282,12 @@ public int value(int index) {
/**
* @webref intdict:method
- * @brief Return the internal array being used to store the keys
+ * @brief Return the internal array being used to store the values
*/
public Iterable values() {
return new Iterable() {
+ @Override
public Iterator iterator() {
return valueIterator();
}
@@ -273,6 +301,7 @@ public Iterator valueIterator() {
public void remove() {
removeIndex(index);
+ index--;
}
public Integer next() {
@@ -293,6 +322,7 @@ public boolean hasNext() {
* @brief Create a new array and copy each of the values into it
*/
public int[] valueArray() {
+ crop();
return valueArray(null);
}
@@ -321,10 +351,20 @@ public int[] valueArray(int[] array) {
*/
public int get(String key) {
int index = index(key);
- if (index == -1) return 0;
+ if (index == -1) {
+ throw new IllegalArgumentException("No key named '" + key + "'");
+ }
+ return values[index];
+ }
+
+
+ public int get(String key, int alternate) {
+ int index = index(key);
+ if (index == -1) return alternate;
return values[index];
}
+
/**
* Create a new key/value pair or change the value of one.
*
@@ -340,6 +380,16 @@ public void set(String key, int amount) {
}
}
+
+ public void setIndex(int index, String key, int value) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ keys[index] = key;
+ values[index] = value;
+ }
+
+
/**
* @webref intdict:method
* @brief Check if a key is a part of the data structure
@@ -360,6 +410,18 @@ public void increment(String key) {
}
+ /**
+ * Merge another dictionary into this one. Calling this increment()
+ * since it doesn't make sense in practice for the other dictionary types,
+ * even though it's technically an add().
+ */
+ public void increment(IntDict dict) {
+ for (int i = 0; i < dict.count; i++) {
+ add(dict.key(i), dict.value(i));
+ }
+ }
+
+
/**
* @webref intdict:method
* @brief Add to a value
@@ -419,7 +481,9 @@ private void checkMinMax(String functionName) {
// return the index of the minimum value
public int minIndex() {
- checkMinMax("minIndex");
+ //checkMinMax("minIndex");
+ if (count == 0) return -1;
+
int index = 0;
int value = values[0];
for (int i = 1; i < count; i++) {
@@ -432,23 +496,30 @@ public int minIndex() {
}
- // return the minimum value
- public int minValue() {
- checkMinMax("minValue");
- return values[minIndex()];
- }
-
-
// return the key for the minimum value
public String minKey() {
checkMinMax("minKey");
- return keys[minIndex()];
+ int index = minIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ // return the minimum value, or throw an error if there are no values
+ public int minValue() {
+ checkMinMax("minValue");
+ return values[minIndex()];
}
// return the index of the max value
public int maxIndex() {
- checkMinMax("maxIndex");
+ //checkMinMax("maxIndex");
+ if (count == 0) {
+ return -1;
+ }
int index = 0;
int value = values[0];
for (int i = 1; i < count; i++) {
@@ -461,17 +532,42 @@ public int maxIndex() {
}
- // return the maximum value
+ /** return the key corresponding to the maximum value or null if no entries */
+ public String maxKey() {
+ //checkMinMax("maxKey");
+ int index = maxIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ // return the maximum value or throw an error if zero length
public int maxValue() {
- checkMinMax("maxValue");
+ checkMinMax("maxIndex");
return values[maxIndex()];
}
- // return the key corresponding to the maximum value
- public String maxKey() {
- checkMinMax("maxKey");
- return keys[maxIndex()];
+ public int sum() {
+ long amount = sumLong();
+ if (amount > Integer.MAX_VALUE) {
+ throw new RuntimeException("sum() exceeds " + Integer.MAX_VALUE + ", use sumLong()");
+ }
+ if (amount < Integer.MIN_VALUE) {
+ throw new RuntimeException("sum() less than " + Integer.MIN_VALUE + ", use sumLong()");
+ }
+ return (int) amount;
+ }
+
+
+ public long sumLong() {
+ long sum = 0;
+ for (int i = 0; i < count; i++) {
+ sum += values[i];
+ }
+ return sum;
}
@@ -486,7 +582,7 @@ protected void create(String what, int much) {
keys = PApplet.expand(keys);
values = PApplet.expand(values);
}
- indices.put(what, new Integer(count));
+ indices.put(what, Integer.valueOf(count));
keys[count] = what;
values[count] = much;
count++;
@@ -498,19 +594,20 @@ protected void create(String what, int much) {
*/
public int remove(String key) {
int index = index(key);
- if (index != -1) {
- removeIndex(index);
+ if (index == -1) {
+ throw new NoSuchElementException("'" + key + "' not found");
}
- return index;
+ int value = values[index];
+ removeIndex(index);
+ return value;
}
- public String removeIndex(int index) {
+ public int removeIndex(int index) {
if (index < 0 || index >= count) {
throw new ArrayIndexOutOfBoundsException(index);
}
- //System.out.println("index is " + which + " and " + keys[which]);
- String key = keys[index];
+ int value = values[index];
indices.remove(keys[index]);
for (int i = index; i < count-1; i++) {
keys[i] = keys[i+1];
@@ -520,11 +617,11 @@ public String removeIndex(int index) {
count--;
keys[count] = null;
values[count] = 0;
- return key;
+ return value;
}
- protected void swap(int a, int b) {
+ public void swap(int a, int b) {
String tkey = keys[a];
int tvalue = values[a];
keys[a] = keys[b];
@@ -532,8 +629,8 @@ protected void swap(int a, int b) {
keys[b] = tkey;
values[b] = tvalue;
- indices.put(keys[a], new Integer(a));
- indices.put(keys[b], new Integer(b));
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
}
@@ -545,7 +642,7 @@ protected void swap(int a, int b) {
* @brief Sort the keys alphabetically
*/
public void sortKeys() {
- sortImpl(true, false);
+ sortImpl(true, false, true);
}
/**
@@ -553,28 +650,34 @@ public void sortKeys() {
* tie-breaker (only really possible with a key that has a case change).
*
* @webref intdict:method
- * @brief Sort the keys alphabetially in reverse
+ * @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
- sortImpl(true, true);
+ sortImpl(true, true, true);
}
/**
-<<<<<<< HEAD
- * Sort by values in descending order (largest value will be at [0]).
- *
-=======
* Sort by values in ascending order. The smallest value will be at [0].
*
->>>>>>> cd467dc12a42d588638aaab06746bebdfb333cc4
* @webref intdict:method
* @brief Sort by values in ascending order
*/
public void sortValues() {
- sortImpl(false, false);
+ sortValues(true);
}
+
+ /**
+ * Set true to ensure that the order returned is identical. Slightly
+ * slower because the tie-breaker for identical values compares the keys.
+ * @param stable
+ */
+ public void sortValues(boolean stable) {
+ sortImpl(false, false, stable);
+ }
+
+
/**
* Sort by values in descending order. The largest value will be at [0].
*
@@ -582,11 +685,17 @@ public void sortValues() {
* @brief Sort by values in descending order
*/
public void sortValuesReverse() {
- sortImpl(false, true);
+ sortValuesReverse(true);
+ }
+
+
+ public void sortValuesReverse(boolean stable) {
+ sortImpl(false, true, stable);
}
- protected void sortImpl(final boolean useKeys, final boolean reverse) {
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
+ final boolean stable) {
Sort s = new Sort() {
@Override
public int size() {
@@ -594,16 +703,16 @@ public int size() {
}
@Override
- public float compare(int a, int b) {
+ public int compare(int a, int b) {
int diff = 0;
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
if (diff == 0) {
- return values[a] - values[b];
+ diff = values[a] - values[b];
}
} else { // sort values
diff = values[a] - values[b];
- if (diff == 0) {
+ if (diff == 0 && stable) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
@@ -616,19 +725,19 @@ public void swap(int a, int b) {
}
};
s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ resetIndices();
}
/**
* Sum all of the values in this dictionary, then return a new FloatDict of
* each key, divided by the total sum. The total for all values will be ~1.0.
- * @return a Dict with the original keys, mapped to their pct of the total
+ * @return an IntDict with the original keys, mapped to their pct of the total
*/
public FloatDict getPercent() {
- double sum = 0;
- for (int value : valueArray()) {
- sum += value;
- }
+ double sum = sum(); // a little more accuracy
FloatDict outgoing = new FloatDict();
for (int i = 0; i < size(); i++) {
double percent = value(i) / sum;
@@ -651,9 +760,25 @@ public IntDict copy() {
}
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
/**
- * Write tab-delimited entries out to
- * @param writer
+ * Write tab-delimited entries to a PrintWriter
*/
public void write(PrintWriter writer) {
for (int i = 0; i < count; i++) {
@@ -663,17 +788,20 @@ public void write(PrintWriter writer) {
}
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ StringList items = new StringList();
+ for (int i = 0; i < count; i++) {
+ items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
+ }
+ return "{ " + items.join(", ") + " }";
+ }
+
+
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName() + " size=" + size() + " { ");
- for (int i = 0; i < size(); i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append("\"" + keys[i] + "\": " + values[i]);
- }
- sb.append(" }");
- return sb.toString();
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
}
}
diff --git a/core/src/processing/data/IntList.java b/libs/processing-core/src/main/java/processing/data/IntList.java
similarity index 80%
rename from core/src/processing/data/IntList.java
rename to libs/processing-core/src/main/java/processing/data/IntList.java
index 8e454a4a0..dc2c89916 100644
--- a/core/src/processing/data/IntList.java
+++ b/libs/processing-core/src/main/java/processing/data/IntList.java
@@ -1,5 +1,7 @@
package processing.data;
+import java.io.File;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
@@ -33,6 +35,7 @@ public IntList() {
data = new int[10];
}
+
/**
* @nowebref
*/
@@ -40,6 +43,7 @@ public IntList(int length) {
data = new int[length];
}
+
/**
* @nowebref
*/
@@ -49,13 +53,48 @@ public IntList(int[] source) {
System.arraycopy(source, 0, data, 0, count);
}
+
/**
+ * Construct an IntList from an iterable pile of objects.
+ * For instance, a float array, an array of strings, who knows).
+ * Un-parseable or null values will be set to 0.
* @nowebref
*/
- public IntList(Iterable iter) {
+ public IntList(Iterable iter) {
this(10);
- for (int v : iter) {
- append(v);
+ for (Object o : iter) {
+ if (o == null) {
+ append(0); // missing value default
+ } else if (o instanceof Number) {
+ append(((Number) o).intValue());
+ } else {
+ append(PApplet.parseInt(o.toString().trim()));
+ }
+ }
+ crop();
+ }
+
+
+ /**
+ * Construct an IntList from a random pile of objects.
+ * Un-parseable or null values will be set to zero.
+ */
+ public IntList(Object... items) {
+ final int missingValue = 0; // nuts, can't be last/final/second arg
+
+ count = items.length;
+ data = new int[count];
+ int index = 0;
+ for (Object o : items) {
+ int value = missingValue;
+ if (o != null) {
+ if (o instanceof Number) {
+ value = ((Number) o).intValue();
+ } else {
+ value = PApplet.parseInt(o.toString().trim(), missingValue);
+ }
+ }
+ data[index++] = value;
}
}
@@ -130,6 +169,9 @@ public void clear() {
* @brief Get an entry at a particular index
*/
public int get(int index) {
+ if (index >= this.count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
return data[index];
}
@@ -154,6 +196,22 @@ public void set(int index, int what) {
}
+ /** Just an alias for append(), but matches pop() */
+ public void push(int value) {
+ append(value);
+ }
+
+
+ public int pop() {
+ if (count == 0) {
+ throw new RuntimeException("Can't call pop() on an empty list");
+ }
+ int value = get(count-1);
+ count--;
+ return value;
+ }
+
+
/**
* Remove an element from the specified index
*
@@ -235,6 +293,14 @@ public void append(IntList list) {
}
+ /** Add this value, but only if it's not already in the list. */
+ public void appendUnique(int value) {
+ if (!hasValue(value)) {
+ append(value);
+ }
+ }
+
+
// public void insert(int index, int value) {
// if (index+1 > count) {
// if (index+1 < data.length) {
@@ -265,6 +331,11 @@ public void append(IntList list) {
// }
+ public void insert(int index, int value) {
+ insert(index, new int[] { value });
+ }
+
+
// same as splice
public void insert(int index, int[] values) {
if (index < 0) {
@@ -399,12 +470,24 @@ public void increment(int index) {
data[index]++;
}
+
+ private void boundsProblem(int index, String method) {
+ final String msg = String.format("The list size is %d. " +
+ "You cannot %s() to element %d.", count, method, index);
+ throw new ArrayIndexOutOfBoundsException(msg);
+ }
+
+
/**
* @webref intlist:method
* @brief Add to a value
*/
public void add(int index, int amount) {
- data[index] += amount;
+ if (index < count) {
+ data[index] += amount;
+ } else {
+ boundsProblem(index, "add");
+ }
}
/**
@@ -412,7 +495,11 @@ public void add(int index, int amount) {
* @brief Subtract from a value
*/
public void sub(int index, int amount) {
- data[index] -= amount;
+ if (index < count) {
+ data[index] -= amount;
+ } else {
+ boundsProblem(index, "sub");
+ }
}
/**
@@ -420,7 +507,11 @@ public void sub(int index, int amount) {
* @brief Multiply a value
*/
public void mult(int index, int amount) {
- data[index] *= amount;
+ if (index < count) {
+ data[index] *= amount;
+ } else {
+ boundsProblem(index, "mult");
+ }
}
/**
@@ -428,7 +519,11 @@ public void mult(int index, int amount) {
* @brief Divide a value
*/
public void div(int index, int amount) {
- data[index] /= amount;
+ if (index < count) {
+ data[index] /= amount;
+ } else {
+ boundsProblem(index, "div");
+ }
}
@@ -503,11 +598,23 @@ public int maxIndex() {
public int sum() {
- int outgoing = 0;
+ long amount = sumLong();
+ if (amount > Integer.MAX_VALUE) {
+ throw new RuntimeException("sum() exceeds " + Integer.MAX_VALUE + ", use sumLong()");
+ }
+ if (amount < Integer.MIN_VALUE) {
+ throw new RuntimeException("sum() less than " + Integer.MIN_VALUE + ", use sumLong()");
+ }
+ return (int) amount;
+ }
+
+
+ public long sumLong() {
+ long sum = 0;
for (int i = 0; i < count; i++) {
- outgoing += data[i];
+ sum += data[i];
}
- return outgoing;
+ return sum;
}
@@ -536,7 +643,7 @@ public int size() {
}
@Override
- public float compare(int a, int b) {
+ public int compare(int a, int b) {
return data[b] - data[a];
}
@@ -569,7 +676,7 @@ public void swap(int a, int b) {
/**
* @webref intlist:method
- * @brief Reverse sort, orders values by first digit
+ * @brief Reverse the order of the list elements
*/
public void reverse() {
int ii = count - 1;
@@ -636,6 +743,7 @@ public int[] values() {
}
+ @Override
public Iterator iterator() {
// public Iterator valueIterator() {
return new Iterator() {
@@ -643,6 +751,7 @@ public Iterator iterator() {
public void remove() {
IntList.this.remove(index);
+ index--;
}
public Integer next() {
@@ -669,7 +778,8 @@ public int[] array() {
/**
- * Copy as many values as possible into the specified array.
+ * Copy values into the specified array. If the specified array is null or
+ * not the same size, a new array will be allocated.
* @param array
*/
public int[] array(int[] array) {
@@ -744,6 +854,19 @@ public FloatList getPercent() {
}
+// /**
+// * Count the number of times each entry is found in this list.
+// * Converts each entry to a String so it can be used as a key.
+// */
+// public IntDict getTally() {
+// IntDict outgoing = new IntDict();
+// for (int i = 0; i < count; i++) {
+// outgoing.increment(String.valueOf(data[i]));
+// }
+// return outgoing;
+// }
+
+
public IntList getSubset(int start) {
return getSubset(start, count - start);
}
@@ -770,17 +893,44 @@ public String join(String separator) {
}
+ public void print() {
+ for (int i = 0; i < count; i++) {
+ System.out.format("[%d] %d%n", i, data[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write entries to a PrintWriter, one per line
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(data[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ return "[ " + join(", ") + " ]";
+ }
+
+
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName() + " size=" + size() + " [ ");
- for (int i = 0; i < size(); i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append(i + ": " + data[i]);
- }
- sb.append(" ]");
- return sb.toString();
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
}
}
diff --git a/core/src/processing/data/JSONArray.java b/libs/processing-core/src/main/java/processing/data/JSONArray.java
similarity index 89%
rename from core/src/processing/data/JSONArray.java
rename to libs/processing-core/src/main/java/processing/data/JSONArray.java
index c25ac93f3..ea8276bd8 100644
--- a/core/src/processing/data/JSONArray.java
+++ b/libs/processing-core/src/main/java/processing/data/JSONArray.java
@@ -36,7 +36,6 @@ of this software and associated documentation files (the "Software"), to deal
import java.io.File;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
@@ -110,7 +109,7 @@ public class JSONArray {
* Construct an empty JSONArray.
*/
public JSONArray() {
- this.myArrayList = new ArrayList();
+ this.myArrayList = new ArrayList<>();
}
@@ -126,7 +125,7 @@ public JSONArray(Reader reader) {
* Construct a JSONArray from a JSONTokener.
*
* @param x A JSONTokener
- * @throws JSONException If there is a syntax error.
+ * @throws RuntimeException If there is a syntax error.
* @nowebref
*/
protected JSONArray(JSONTokener x) {
@@ -166,9 +165,9 @@ protected JSONArray(JSONTokener x) {
* @nowebref
*/
public JSONArray(IntList list) {
- myArrayList = new ArrayList();
+ myArrayList = new ArrayList<>();
for (int item : list.values()) {
- myArrayList.add(new Integer(item));
+ myArrayList.add(Integer.valueOf(item));
}
}
@@ -177,9 +176,9 @@ public JSONArray(IntList list) {
* @nowebref
*/
public JSONArray(FloatList list) {
- myArrayList = new ArrayList();
+ myArrayList = new ArrayList<>();
for (float item : list.values()) {
- myArrayList.add(new Float(item));
+ myArrayList.add(Float.valueOf(item));
}
}
@@ -188,7 +187,7 @@ public JSONArray(FloatList list) {
* @nowebref
*/
public JSONArray(StringList list) {
- myArrayList = new ArrayList();
+ myArrayList = new ArrayList<>();
for (String item : list.values()) {
myArrayList.add(item);
}
@@ -200,7 +199,7 @@ public JSONArray(StringList list) {
* @param source A string that begins with
* [ (left bracket)
* and ends with ] (right bracket) .
- * @throws JSONException If there is a syntax error.
+ * @return {@code null} if there is a syntax error.
*/
static public JSONArray parse(String source) {
try {
@@ -229,7 +228,7 @@ static public JSONArray parse(String source) {
// TODO not decided whether we keep this one, but used heavily by JSONObject
/**
* Construct a JSONArray from an array
- * @throws JSONException If not an array.
+ * @throws RuntimeException If not an array.
*/
protected JSONArray(Object array) {
this();
@@ -262,9 +261,9 @@ private Object opt(int index) {
* Get the object value associated with an index.
* @param index must be between 0 and length() - 1
* @return An object value.
- * @throws JSONException If there is no value for the index.
+ * @throws RuntimeException If there is no value for the index.
*/
- private Object get(int index) {
+ public Object get(int index) {
Object object = opt(index);
if (object == null) {
throw new RuntimeException("JSONArray[" + index + "] not found.");
@@ -280,7 +279,7 @@ private Object get(int index) {
* @brief Gets the String value associated with an index
* @param index must be between 0 and length() - 1
* @return A string value.
- * @throws JSONException If there is no string value for the index.
+ * @throws RuntimeException If there is no string value for the index.
* @see JSONArray#getInt(int)
* @see JSONArray#getFloat(int)
* @see JSONArray#getBoolean(int)
@@ -315,7 +314,7 @@ public String getString(int index, String defaultValue) {
* @brief Gets the int value associated with an index
* @param index must be between 0 and length() - 1
* @return The value.
- * @throws JSONException If the key is not found or if the value is not a number.
+ * @throws RuntimeException If the key is not found or if the value is not a number.
* @see JSONArray#getFloat(int)
* @see JSONArray#getString(int)
* @see JSONArray#getBoolean(int)
@@ -354,7 +353,7 @@ public int getInt(int index, int defaultValue) {
*
* @param index The index must be between 0 and length() - 1
* @return The value.
- * @throws JSONException If the key is not found or if the value cannot
+ * @throws RuntimeException If the key is not found or if the value cannot
* be converted to a number.
*/
public long getLong(int index) {
@@ -416,7 +415,7 @@ public float getFloat(int index, float defaultValue) {
*
* @param index must be between 0 and length() - 1
* @return The value.
- * @throws JSONException If the key is not found or if the value cannot
+ * @throws RuntimeException If the key is not found or if the value cannot
* be converted to a number.
*/
public double getDouble(int index) {
@@ -457,7 +456,7 @@ public double getDouble(int index, double defaultValue) {
* @brief Gets the boolean value associated with an index
* @param index must be between 0 and length() - 1
* @return The truth.
- * @throws JSONException If there is no value for the index or if the
+ * @throws RuntimeException If there is no value for the index or if the
* value is not convertible to boolean.
* @see JSONArray#getInt(int)
* @see JSONArray#getFloat(int)
@@ -503,7 +502,7 @@ public boolean getBoolean(int index, boolean defaultValue) {
* @brief Gets the JSONArray associated with an index value
* @param index must be between 0 and length() - 1
* @return A JSONArray value.
- * @throws JSONException If there is no value for the index. or if the
+ * @throws RuntimeException If there is no value for the index. or if the
* value is not a JSONArray
* @see JSONArray#getJSONObject(int)
* @see JSONArray#setJSONObject(int, JSONObject)
@@ -534,7 +533,7 @@ public JSONArray getJSONArray(int index, JSONArray defaultValue) {
* @brief Gets the JSONObject associated with an index value
* @param index the index value of the object to get
* @return A JSONObject value.
- * @throws JSONException If there is no value for the index or if the
+ * @throws RuntimeException If there is no value for the index or if the
* value is not a JSONObject
* @see JSONArray#getJSONArray(int)
* @see JSONArray#setJSONObject(int, JSONObject)
@@ -718,7 +717,7 @@ public JSONArray append(String value) {
* @return this.
*/
public JSONArray append(int value) {
- this.append(new Integer(value));
+ this.append(Integer.valueOf(value));
return this;
}
@@ -731,7 +730,7 @@ public JSONArray append(int value) {
* @return this.
*/
public JSONArray append(long value) {
- this.append(new Long(value));
+ this.append(Long.valueOf(value));
return this;
}
@@ -741,7 +740,7 @@ public JSONArray append(long value) {
* This will store the value as a double, since there are no floats in JSON.
*
* @param value a float value
- * @throws JSONException if the value is not finite.
+ * @throws RuntimeException if the value is not finite.
* @return this.
*/
public JSONArray append(float value) {
@@ -754,11 +753,11 @@ public JSONArray append(float value) {
*
* @nowebref
* @param value A double value.
- * @throws JSONException if the value is not finite.
+ * @throws RuntimeException if the value is not finite.
* @return this.
*/
public JSONArray append(double value) {
- Double d = new Double(value);
+ Double d = value;
JSONObject.testValidity(d);
this.append(d);
return this;
@@ -838,7 +837,7 @@ protected JSONArray append(Object value) {
// * @param index The subscript.
// * @param value A Collection value.
// * @return this.
-// * @throws JSONException If the index is negative or if the value is
+// * @throws RuntimeException If the index is negative or if the value is
// * not finite.
// */
// public JSONArray set(int index, Collection value) {
@@ -857,7 +856,7 @@ protected JSONArray append(Object value) {
* @param index an index value
* @param value the value to assign
* @return this.
- * @throws JSONException If the index is negative.
+ * @throws RuntimeException If the index is negative.
* @see JSONArray#setInt(int, int)
* @see JSONArray#setFloat(int, float)
* @see JSONArray#setBoolean(int, boolean)
@@ -878,13 +877,13 @@ public JSONArray setString(int index, String value) {
* @param index an index value
* @param value the value to assign
* @return this.
- * @throws JSONException If the index is negative.
+ * @throws RuntimeException If the index is negative.
* @see JSONArray#setFloat(int, float)
* @see JSONArray#setString(int, String)
* @see JSONArray#setBoolean(int, boolean)
*/
public JSONArray setInt(int index, int value) {
- this.set(index, new Integer(value));
+ this.set(index, Integer.valueOf(value));
return this;
}
@@ -896,10 +895,10 @@ public JSONArray setInt(int index, int value) {
* @param index The subscript.
* @param value A long value.
* @return this.
- * @throws JSONException If the index is negative.
+ * @throws RuntimeException If the index is negative.
*/
public JSONArray setLong(int index, long value) {
- return set(index, new Long(value));
+ return set(index, Long.valueOf(value));
}
@@ -932,11 +931,11 @@ public JSONArray setFloat(int index, float value) {
* @param index The subscript.
* @param value A double value.
* @return this.
- * @throws JSONException If the index is negative or if the value is
+ * @throws RuntimeException If the index is negative or if the value is
* not finite.
*/
public JSONArray setDouble(int index, double value) {
- return set(index, new Double(value));
+ return set(index, Double.valueOf(value));
}
@@ -950,7 +949,7 @@ public JSONArray setDouble(int index, double value) {
* @param index an index value
* @param value the value to assign
* @return this.
- * @throws JSONException If the index is negative.
+ * @throws RuntimeException If the index is negative.
* @see JSONArray#setInt(int, int)
* @see JSONArray#setFloat(int, float)
* @see JSONArray#setString(int, String)
@@ -966,7 +965,7 @@ public JSONArray setBoolean(int index, boolean value) {
// * @param index The subscript.
// * @param value The Map value.
// * @return this.
-// * @throws JSONException If the index is negative or if the the value is
+// * @throws RuntimeException If the index is negative or if the the value is
// * an invalid number.
// */
// public JSONArray set(int index, Map value) {
@@ -1012,7 +1011,7 @@ public JSONArray setJSONObject(int index, JSONObject value) {
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object.
* @return this.
- * @throws JSONException If the index is negative or if the the value is
+ * @throws RuntimeException If the index is negative or if the the value is
* an invalid number.
*/
private JSONArray set(int index, Object value) {
@@ -1048,6 +1047,7 @@ public int size() {
/**
* Determine if the value is null.
+ * @webref
* @param index must be between 0 and length() - 1
* @return true if the value at the index is null, or if there is no value.
*/
@@ -1094,18 +1094,42 @@ public Object remove(int index) {
// }
- protected boolean save(OutputStream output) {
- return save(PApplet.createWriter(output));
- }
+// protected boolean save(OutputStream output) {
+// return write(PApplet.createWriter(output), null);
+// }
public boolean save(File file, String options) {
- return save(PApplet.createWriter(file));
+ PrintWriter writer = PApplet.createWriter(file);
+ boolean success = write(writer, options);
+ writer.close();
+ return success;
+ }
+
+
+ public boolean write(PrintWriter output) {
+ return write(output, null);
}
- public boolean save(PrintWriter output) {
- output.print(format(2));
+ public boolean write(PrintWriter output, String options) {
+ int indentFactor = 2;
+ if (options != null) {
+ String[] opts = PApplet.split(options, ',');
+ for (String opt : opts) {
+ if (opt.equals("compact")) {
+ indentFactor = -1;
+ } else if (opt.startsWith("indent=")) {
+ indentFactor = PApplet.parseInt(opt.substring(7), -2);
+ if (indentFactor == -2) {
+ throw new IllegalArgumentException("Could not read a number from " + opt);
+ }
+ } else {
+ System.err.println("Ignoring " + opt);
+ }
+ }
+ }
+ output.print(format(indentFactor));
output.flush();
return true;
}
@@ -1139,27 +1163,26 @@ public String toString() {
public String format(int indentFactor) {
StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
- return this.write(sw, indentFactor, 0).toString();
+ return this.writeInternal(sw, indentFactor, 0).toString();
}
}
- /**
- * Write the contents of the JSONArray as JSON text to a writer. For
- * compactness, no whitespace is added.
- *
- * Warning: This method assumes that the data structure is acyclic.
- *
- * @return The writer.
- */
- protected Writer write(Writer writer) {
- return this.write(writer, -1, 0);
- }
+// /**
+// * Write the contents of the JSONArray as JSON text to a writer. For
+// * compactness, no whitespace is added.
+// *
+// * Warning: This method assumes that the data structure is acyclic.
+// *
+// * @return The writer.
+// */
+// protected Writer write(Writer writer) {
+// return this.write(writer, -1, 0);
+// }
/**
- * Write the contents of the JSONArray as JSON text to a writer. For
- * compactness, no whitespace is added.
+ * Write the contents of the JSONArray as JSON text to a writer.
*
* Warning: This method assumes that the data structure is acyclic.
*
@@ -1169,9 +1192,9 @@ protected Writer write(Writer writer) {
* @param indent
* The indention of the top level.
* @return The writer.
- * @throws JSONException
+ * @throws RuntimeException
*/
- protected Writer write(Writer writer, int indentFactor, int indent) {
+ protected Writer writeInternal(Writer writer, int indentFactor, int indent) {
try {
boolean commanate = false;
int length = this.size();
@@ -1182,9 +1205,10 @@ protected Writer write(Writer writer, int indentFactor, int indent) {
if (length == 1) {
JSONObject.writeValue(writer, this.myArrayList.get(0),
- thisFactor, indent);
+ indentFactor, indent);
+// thisFactor, indent);
} else if (length != 0) {
- final int newindent = indent + thisFactor;
+ final int newIndent = indent + thisFactor;
for (int i = 0; i < length; i += 1) {
if (commanate) {
@@ -1193,9 +1217,11 @@ protected Writer write(Writer writer, int indentFactor, int indent) {
if (indentFactor != -1) {
writer.write('\n');
}
- JSONObject.indent(writer, newindent);
+ JSONObject.indent(writer, newIndent);
+// JSONObject.writeValue(writer, this.myArrayList.get(i),
+// thisFactor, newIndent);
JSONObject.writeValue(writer, this.myArrayList.get(i),
- thisFactor, newindent);
+ indentFactor, newIndent);
commanate = true;
}
if (indentFactor != -1) {
@@ -1217,11 +1243,11 @@ protected Writer write(Writer writer, int indentFactor, int indent) {
* Warning: This method assumes that the data structure is acyclic.
* @param separator A string that will be inserted between the elements.
* @return a string.
- * @throws JSONException If the array contains an invalid number.
+ * @throws RuntimeException If the array contains an invalid number.
*/
public String join(String separator) {
int len = this.size();
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i += 1) {
if (i > 0) {
diff --git a/core/src/processing/data/JSONObject.java b/libs/processing-core/src/main/java/processing/data/JSONObject.java
similarity index 92%
rename from core/src/processing/data/JSONObject.java
rename to libs/processing-core/src/main/java/processing/data/JSONObject.java
index 9b1387d4f..cc7a22de0 100644
--- a/core/src/processing/data/JSONObject.java
+++ b/libs/processing-core/src/main/java/processing/data/JSONObject.java
@@ -50,7 +50,6 @@ of this software and associated documentation files (the "Software"), to deal
import processing.core.PApplet;
-
/**
* A JSONObject is an unordered collection of name/value pairs. Its external
* form is a string wrapped in curly braces with colons between the names and
@@ -96,7 +95,7 @@ of this software and associated documentation files (the "Software"), to deal
* { } [ ] / \ : , = ; # and if they do not look like numbers and
* if they are not the reserved words true, false, or
* null.
- * Keys can be followed by = or => as well as by
+ * Keys can be followed by = or {@code =>} as well as by
* :.
* Values can be followed by ; (semicolon) as
* well as by , (comma) .
@@ -111,7 +110,6 @@ of this software and associated documentation files (the "Software"), to deal
* @see PApplet#saveJSONObject(JSONObject, String)
* @see PApplet#saveJSONArray(JSONArray, String)
*/
-@SuppressWarnings("rawtypes")
public class JSONObject {
/**
* The maximum number of keys in the key pool.
@@ -125,7 +123,7 @@ public class JSONObject {
* string objects. This is used by JSONObject.put(string, object).
*/
private static HashMap keyPool =
- new HashMap(keyPoolSize);
+ new HashMap<>(keyPoolSize);
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -199,7 +197,7 @@ public int hashCode() {
* @nowebref
*/
public JSONObject() {
- this.map = new HashMap();
+ this.map = new HashMap<>();
}
@@ -209,8 +207,6 @@ public JSONObject() {
// * Missing keys are ignored.
// * @param jo A JSONObject.
// * @param names An array of strings.
-// * @throws JSONException
-// * @exception JSONException If a value is a non-finite number or if a name is duplicated.
// */
// public JSONObject(JSONObject jo, String[] names) {
// this();
@@ -234,7 +230,7 @@ public JSONObject(Reader reader) {
/**
* Construct a JSONObject from a JSONTokener.
* @param x A JSONTokener object containing the source string.
- * @throws JSONException If there is a syntax error in the source string
+ * @throws RuntimeException If there is a syntax error in the source string
* or a duplicated key.
*/
protected JSONObject(JSONTokener x) {
@@ -293,10 +289,9 @@ protected JSONObject(JSONTokener x) {
*
* @param map A map object that can be used to initialize the contents of
* the JSONObject.
- * @throws JSONException
*/
protected JSONObject(HashMap map) {
- this.map = new HashMap();
+ this.map = new HashMap<>();
if (map != null) {
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
@@ -314,7 +309,7 @@ protected JSONObject(HashMap map) {
* @nowebref
*/
public JSONObject(IntDict dict) {
- map = new HashMap();
+ map = new HashMap<>();
for (int i = 0; i < dict.size(); i++) {
setInt(dict.key(i), dict.value(i));
}
@@ -325,7 +320,7 @@ public JSONObject(IntDict dict) {
* @nowebref
*/
public JSONObject(FloatDict dict) {
- map = new HashMap();
+ map = new HashMap<>();
for (int i = 0; i < dict.size(); i++) {
setFloat(dict.key(i), dict.value(i));
}
@@ -336,7 +331,7 @@ public JSONObject(FloatDict dict) {
* @nowebref
*/
public JSONObject(StringDict dict) {
- map = new HashMap();
+ map = new HashMap<>();
for (int i = 0; i < dict.size(); i++) {
setString(dict.key(i), dict.value(i));
}
@@ -399,7 +394,7 @@ protected JSONObject(Object bean) {
* @param source A string beginning
* with { (left brace) and ending
* with } (right brace) .
- * @exception JSONException If there is a syntax error in the source
+ * @exception RuntimeException If there is a syntax error in the source
* string or a duplicated key.
*/
static public JSONObject parse(String source) {
@@ -540,15 +535,19 @@ static protected String doubleToString(double d) {
*
* @param key A key string.
* @return The object associated with the key.
- * @throws JSONException if the key is not found.
+ * @throws RuntimeException if the key is not found.
*/
- private Object get(String key) {
+ public Object get(String key) {
if (key == null) {
- throw new RuntimeException("Null key.");
+ throw new RuntimeException("JSONObject.get(null) called");
}
Object object = this.opt(key);
if (object == null) {
- throw new RuntimeException("JSONObject[" + quote(key) + "] not found.");
+ // Adding for rev 0257 in line with other p5 api
+ return null;
+ }
+ if (object == null) {
+ throw new RuntimeException("JSONObject[" + quote(key) + "] not found");
}
return object;
}
@@ -561,17 +560,21 @@ private Object get(String key) {
* @brief Gets the string value associated with a key
* @param key a key string
* @return A string which is the value.
- * @throws JSONException if there is no string value for the key.
+ * @throws RuntimeException if there is no string value for the key.
* @see JSONObject#getInt(String)
* @see JSONObject#getFloat(String)
* @see JSONObject#getBoolean(String)
*/
public String getString(String key) {
Object object = this.get(key);
+ if (object == null) {
+ // Adding for rev 0257 in line with other p5 api
+ return null;
+ }
if (object instanceof String) {
return (String)object;
}
- throw new RuntimeException("JSONObject[" + quote(key) + "] not a string.");
+ throw new RuntimeException("JSONObject[" + quote(key) + "] is not a string");
}
@@ -594,9 +597,9 @@ public String getString(String key, String defaultValue) {
*
* @webref jsonobject:method
* @brief Gets the int value associated with a key
- * @param key a key string
+ * @param key A key string.
* @return The integer value.
- * @throws JSONException if the key is not found or if the value cannot
+ * @throws RuntimeException if the key is not found or if the value cannot
* be converted to an integer.
* @see JSONObject#getFloat(String)
* @see JSONObject#getString(String)
@@ -604,10 +607,12 @@ public String getString(String key, String defaultValue) {
*/
public int getInt(String key) {
Object object = this.get(key);
+ if (object == null) {
+ throw new RuntimeException("JSONObject[" + quote(key) + "] not found");
+ }
try {
- return object instanceof Number
- ? ((Number)object).intValue()
- : Integer.parseInt((String)object);
+ return object instanceof Number ?
+ ((Number)object).intValue() : Integer.parseInt((String)object);
} catch (Exception e) {
throw new RuntimeException("JSONObject[" + quote(key) + "] is not an int.");
}
@@ -638,7 +643,7 @@ public int getInt(String key, int defaultValue) {
*
* @param key A key string.
* @return The long value.
- * @throws JSONException if the key is not found or if the value cannot
+ * @throws RuntimeException if the key is not found or if the value cannot
* be converted to a long.
*/
public long getLong(String key) {
@@ -698,7 +703,7 @@ public float getFloat(String key, float defaultValue) {
* Get the double value associated with a key.
* @param key A key string.
* @return The numeric value.
- * @throws JSONException if the key is not found or
+ * @throws RuntimeException if the key is not found or
* if the value is not a Number object and cannot be converted to a number.
*/
public double getDouble(String key) {
@@ -739,7 +744,7 @@ public double getDouble(String key, double defaultValue) {
* @brief Gets the boolean value associated with a key
* @param key a key string
* @return The truth.
- * @throws JSONException if the value is not a Boolean or the String "true" or "false".
+ * @throws RuntimeException if the value is not a Boolean or the String "true" or "false".
* @see JSONObject#getInt(String)
* @see JSONObject#getFloat(String)
* @see JSONObject#getString(String)
@@ -783,14 +788,17 @@ public boolean getBoolean(String key, boolean defaultValue) {
* @webref jsonobject:method
* @brief Gets the JSONArray value associated with a key
* @param key a key string
- * @return A JSONArray which is the value.
- * @throws JSONException if the key is not found or if the value is not a JSONArray.
+ * @return A JSONArray which is the value, or null if not present
+ * @throws RuntimeException if the value is not a JSONArray.
* @see JSONObject#getJSONObject(String)
* @see JSONObject#setJSONObject(String, JSONObject)
* @see JSONObject#setJSONArray(String, JSONArray)
*/
public JSONArray getJSONArray(String key) {
Object object = this.get(key);
+ if (object == null) {
+ return null;
+ }
if (object instanceof JSONArray) {
return (JSONArray)object;
}
@@ -804,14 +812,17 @@ public JSONArray getJSONArray(String key) {
* @webref jsonobject:method
* @brief Gets the JSONObject value associated with a key
* @param key a key string
- * @return A JSONObject which is the value.
- * @throws JSONException if the key is not found or if the value is not a JSONObject.
+ * @return A JSONObject which is the value or null if not available.
+ * @throws RuntimeException if the value is not a JSONObject.
* @see JSONObject#getJSONArray(String)
* @see JSONObject#setJSONObject(String, JSONObject)
* @see JSONObject#setJSONArray(String, JSONArray)
*/
public JSONObject getJSONObject(String key) {
Object object = this.get(key);
+ if (object == null) {
+ return null;
+ }
if (object instanceof JSONObject) {
return (JSONObject)object;
}
@@ -869,7 +880,7 @@ public JSONObject getJSONObject(String key) {
* @return true if the key exists in the JSONObject.
*/
public boolean hasKey(String key) {
- return this.map.containsKey(key);
+ return map.containsKey(key);
}
@@ -903,7 +914,9 @@ public boolean hasKey(String key) {
/**
* Determine if the value associated with the key is null or if there is
- * no value.
+ * no value.
+ *
+ * @webref
* @param key A key string.
* @return true if there is no value associated with the key or if
* the value is the JSONObject.NULL object.
@@ -964,7 +977,7 @@ public int size() {
* Produce a string from a Number.
* @param number A Number
* @return A String.
- * @throws JSONException If n is a non-finite number.
+ * @throws RuntimeException If number is null or a non-finite number.
*/
private static String numberToString(Number number) {
if (number == null) {
@@ -1164,13 +1177,13 @@ public JSONObject setString(String key, String value) {
* @param key a key string
* @param value the value to assign
* @return this.
- * @throws JSONException If the key is null.
+ * @throws RuntimeException If the key is null.
* @see JSONObject#setFloat(String, float)
* @see JSONObject#setString(String, String)
* @see JSONObject#setBoolean(String, boolean)
*/
public JSONObject setInt(String key, int value) {
- this.put(key, new Integer(value));
+ this.put(key, Integer.valueOf(value));
return this;
}
@@ -1181,10 +1194,10 @@ public JSONObject setInt(String key, int value) {
* @param key A key string.
* @param value A long which is the value.
* @return this.
- * @throws JSONException If the key is null.
+ * @throws RuntimeException If the key is null.
*/
public JSONObject setLong(String key, long value) {
- this.put(key, new Long(value));
+ this.put(key, Long.valueOf(value));
return this;
}
@@ -1193,12 +1206,13 @@ public JSONObject setLong(String key, long value) {
* @brief Put a key/float pair in the JSONObject
* @param key a key string
* @param value the value to assign
+ * @throws RuntimeException If the key is null or if the number is NaN or infinite.
* @see JSONObject#setInt(String, int)
* @see JSONObject#setString(String, String)
* @see JSONObject#setBoolean(String, boolean)
*/
public JSONObject setFloat(String key, float value) {
- this.put(key, new Double(value));
+ this.put(key, Double.valueOf(value));
return this;
}
@@ -1209,10 +1223,10 @@ public JSONObject setFloat(String key, float value) {
* @param key A key string.
* @param value A double which is the value.
* @return this.
- * @throws JSONException If the key is null or if the number is invalid.
+ * @throws RuntimeException If the key is null or if the number is NaN or infinite.
*/
public JSONObject setDouble(String key, double value) {
- this.put(key, new Double(value));
+ this.put(key, Double.valueOf(value));
return this;
}
@@ -1225,7 +1239,7 @@ public JSONObject setDouble(String key, double value) {
* @param key a key string
* @param value the value to assign
* @return this.
- * @throws JSONException If the key is null.
+ * @throws RuntimeException If the key is null.
* @see JSONObject#setInt(String, int)
* @see JSONObject#setFloat(String, float)
* @see JSONObject#setString(String, String)
@@ -1299,10 +1313,10 @@ public JSONObject setJSONArray(String key, JSONArray value) {
* types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
* or the JSONObject.NULL object.
* @return this.
- * @throws JSONException If the value is non-finite number
+ * @throws RuntimeException If the value is non-finite number
* or if the key is null.
*/
- private JSONObject put(String key, Object value) {
+ public JSONObject put(String key, Object value) {
String pooled;
if (key == null) {
throw new RuntimeException("Null key.");
@@ -1312,7 +1326,7 @@ private JSONObject put(String key, Object value) {
pooled = (String)keyPool.get(key);
if (pooled == null) {
if (keyPool.size() >= keyPoolSize) {
- keyPool = new HashMap(keyPoolSize);
+ keyPool = new HashMap<>(keyPoolSize);
}
keyPool.put(key, key);
} else {
@@ -1332,8 +1346,9 @@ private JSONObject put(String key, Object value) {
* with that name.
* @param key
* @param value
- * @return his.
- * @throws JSONException if the key is a duplicate
+ * @return {@code this}.
+ * @throws RuntimeException if the key is a duplicate, or if
+ * {@link #put(String,Object)} throws.
*/
private JSONObject putOnce(String key, Object value) {
if (key != null && value != null) {
@@ -1372,7 +1387,7 @@ private JSONObject putOnce(String key, Object value) {
* @param string A String
* @return A String correctly formatted for insertion in a JSON text.
*/
- static protected String quote(String string) {
+ static public String quote(String string) {
StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
try {
@@ -1384,7 +1399,7 @@ static protected String quote(String string) {
}
}
- static protected Writer quote(String string, Writer w) throws IOException {
+ static public Writer quote(String string, Writer w) throws IOException {
if (string == null || string.length() == 0) {
w.write("\"\"");
return w;
@@ -1494,9 +1509,9 @@ static protected Object stringToValue(String string) {
return d;
}
} else {
- Long myLong = new Long(string);
+ Long myLong = Long.valueOf(string);
if (myLong.longValue() == myLong.intValue()) {
- return new Integer(myLong.intValue());
+ return Integer.valueOf(myLong.intValue());
} else {
return myLong;
}
@@ -1510,8 +1525,9 @@ static protected Object stringToValue(String string) {
/**
* Throw an exception if the object is a NaN or infinite number.
- * @param o The object to test.
- * @throws JSONException If o is a non-finite number.
+ * @param o The object to test. If not Float or Double, accepted without
+ * exceptions.
+ * @throws RuntimeException If o is infinite or NaN.
*/
static protected void testValidity(Object o) {
if (o != null) {
@@ -1556,12 +1572,36 @@ static protected void testValidity(Object o) {
public boolean save(File file, String options) {
- return write(PApplet.createWriter(file));
+ PrintWriter writer = PApplet.createWriter(file);
+ boolean success = write(writer, options);
+ writer.close();
+ return success;
}
public boolean write(PrintWriter output) {
- output.print(format(2));
+ return write(output, null);
+ }
+
+
+ public boolean write(PrintWriter output, String options) {
+ int indentFactor = 2;
+ if (options != null) {
+ String[] opts = PApplet.split(options, ',');
+ for (String opt : opts) {
+ if (opt.equals("compact")) {
+ indentFactor = -1;
+ } else if (opt.startsWith("indent=")) {
+ indentFactor = PApplet.parseInt(opt.substring(7), -2);
+ if (indentFactor == -2) {
+ throw new IllegalArgumentException("Could not read a number from " + opt);
+ }
+ } else {
+ System.err.println("Ignoring " + opt);
+ }
+ }
+ }
+ output.print(format(indentFactor));
output.flush();
return true;
}
@@ -1592,12 +1632,12 @@ public String toString() {
* representation of the object, beginning
* with { (left brace) and ending
* with } (right brace) .
- * @throws JSONException If the object contains an invalid number.
+ * @throws RuntimeException If the object contains an invalid number.
*/
public String format(int indentFactor) {
StringWriter w = new StringWriter();
synchronized (w.getBuffer()) {
- return this.write(w, indentFactor, 0).toString();
+ return this.writeInternal(w, indentFactor, 0).toString();
}
}
@@ -1620,7 +1660,7 @@ public String format(int indentFactor) {
* representation of the object, beginning
* with { (left brace) and ending
* with } (right brace) .
- * @throws JSONException If the value is or contains an invalid number.
+ * @throws RuntimeException If the value is or contains an invalid number.
*/
static protected String valueToString(Object value) {
if (value == null || value.equals(null)) {
@@ -1646,10 +1686,10 @@ static protected String valueToString(Object value) {
return value.toString();
}
if (value instanceof Map) {
- return new JSONObject((Map)value).toString();
+ return new JSONObject(value).toString();
}
if (value instanceof Collection) {
- return new JSONArray((Collection)value).toString();
+ return new JSONArray(value).toString();
}
if (value.getClass().isArray()) {
return new JSONArray(value).toString();
@@ -1685,13 +1725,13 @@ static protected Object wrap(Object object) {
}
if (object instanceof Collection) {
- return new JSONArray((Collection)object);
+ return new JSONArray(object);
}
if (object.getClass().isArray()) {
return new JSONArray(object);
}
if (object instanceof Map) {
- return new JSONObject((Map)object);
+ return new JSONObject(object);
}
Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null
@@ -1730,16 +1770,16 @@ static final Writer writeValue(Writer writer, Object value,
if (value == null || value.equals(null)) {
writer.write("null");
} else if (value instanceof JSONObject) {
- ((JSONObject) value).write(writer, indentFactor, indent);
+ ((JSONObject) value).writeInternal(writer, indentFactor, indent);
} else if (value instanceof JSONArray) {
- ((JSONArray) value).write(writer, indentFactor, indent);
+ ((JSONArray) value).writeInternal(writer, indentFactor, indent);
} else if (value instanceof Map) {
- new JSONObject((Map) value).write(writer, indentFactor, indent);
+ new JSONObject(value).writeInternal(writer, indentFactor, indent);
} else if (value instanceof Collection) {
- new JSONArray((Collection) value).write(writer, indentFactor,
+ new JSONArray(value).writeInternal(writer, indentFactor,
indent);
} else if (value.getClass().isArray()) {
- new JSONArray(value).write(writer, indentFactor, indent);
+ new JSONArray(value).writeInternal(writer, indentFactor, indent);
} else if (value instanceof Number) {
writer.write(numberToString((Number) value));
} else if (value instanceof Boolean) {
@@ -1768,15 +1808,14 @@ static final void indent(Writer writer, int indent) throws IOException {
}
/**
- * Write the contents of the JSONObject as JSON text to a writer. For
- * compactness, no whitespace is added.
+ * Write the contents of the JSONObject as JSON text to a writer.
*
* Warning: This method assumes that the data structure is acyclical.
*
* @return The writer.
- * @throws JSONException
+ * @throws RuntimeException
*/
- protected Writer write(Writer writer, int indentFactor, int indent) {
+ protected Writer writeInternal(Writer writer, int indentFactor, int indent) {
try {
boolean commanate = false;
final int length = this.size();
@@ -1792,9 +1831,10 @@ protected Writer write(Writer writer, int indentFactor, int indent) {
if (actualFactor > 0) {
writer.write(' ');
}
- writeValue(writer, this.map.get(key), actualFactor, indent);
+ //writeValue(writer, this.map.get(key), actualFactor, indent);
+ writeValue(writer, this.map.get(key), indentFactor, indent);
} else if (length != 0) {
- final int newindent = indent + actualFactor;
+ final int newIndent = indent + actualFactor;
while (keys.hasNext()) {
Object key = keys.next();
if (commanate) {
@@ -1803,14 +1843,14 @@ protected Writer write(Writer writer, int indentFactor, int indent) {
if (indentFactor != -1) {
writer.write('\n');
}
- indent(writer, newindent);
+ indent(writer, newIndent);
writer.write(quote(key.toString()));
writer.write(':');
if (actualFactor > 0) {
writer.write(' ');
}
- writeValue(writer, this.map.get(key), actualFactor,
- newindent);
+ //writeValue(writer, this.map.get(key), actualFactor, newIndent);
+ writeValue(writer, this.map.get(key), indentFactor, newIndent);
commanate = true;
}
if (indentFactor != -1) {
diff --git a/core/src/processing/data/JSONTokener.java b/libs/processing-core/src/main/java/processing/data/JSONTokener.java
similarity index 98%
rename from core/src/processing/data/JSONTokener.java
rename to libs/processing-core/src/main/java/processing/data/JSONTokener.java
index 499f10a82..5301a5e3c 100644
--- a/core/src/processing/data/JSONTokener.java
+++ b/libs/processing-core/src/main/java/processing/data/JSONTokener.java
@@ -249,7 +249,7 @@ public char nextClean() {
*/
public String nextString(char quote) {
char c;
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
switch (c) {
@@ -305,7 +305,7 @@ public String nextString(char quote) {
* @return A string.
*/
public String nextTo(char delimiter) {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (;;) {
char c = this.next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
@@ -327,7 +327,7 @@ public String nextTo(char delimiter) {
*/
public String nextTo(String delimiters) {
char c;
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
if (delimiters.indexOf(c) >= 0 || c == 0 ||
@@ -374,7 +374,7 @@ public Object nextValue() {
* formatting character.
*/
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
diff --git a/libs/processing-core/src/main/java/processing/data/LongDict.java b/libs/processing-core/src/main/java/processing/data/LongDict.java
new file mode 100644
index 000000000..529246862
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/data/LongDict.java
@@ -0,0 +1,802 @@
+package processing.data;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import processing.core.PApplet;
+
+
+/**
+ * A simple class to use a String as a lookup for an int value.
+ *
+ * @webref data:composite
+ * @see FloatDict
+ * @see StringDict
+ */
+public class LongDict {
+
+ /** Number of elements in the table */
+ protected int count;
+
+ protected String[] keys;
+ protected long[] values;
+
+ /** Internal implementation for faster lookups */
+ private HashMap indices = new HashMap<>();
+
+
+ public LongDict() {
+ count = 0;
+ keys = new String[10];
+ values = new long[10];
+ }
+
+
+ /**
+ * Create a new lookup with a specific size. This is more efficient than not
+ * specifying a size. Use it when you know the rough size of the thing you're creating.
+ *
+ * @nowebref
+ */
+ public LongDict(int length) {
+ count = 0;
+ keys = new String[length];
+ values = new long[length];
+ }
+
+
+ /**
+ * Read a set of entries from a Reader that has each key/value pair on
+ * a single line, separated by a tab.
+ *
+ * @nowebref
+ */
+ public LongDict(BufferedReader reader) {
+ String[] lines = PApplet.loadStrings(reader);
+ keys = new String[lines.length];
+ values = new long[lines.length];
+
+ for (int i = 0; i < lines.length; i++) {
+ String[] pieces = PApplet.split(lines[i], '\t');
+ if (pieces.length == 2) {
+ keys[count] = pieces[0];
+ values[count] = PApplet.parseInt(pieces[1]);
+ indices.put(pieces[0], count);
+ count++;
+ }
+ }
+ }
+
+ /**
+ * @nowebref
+ */
+ public LongDict(String[] keys, long[] values) {
+ if (keys.length != values.length) {
+ throw new IllegalArgumentException("key and value arrays must be the same length");
+ }
+ this.keys = keys;
+ this.values = values;
+ count = keys.length;
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ /**
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
+ *
+ * new FloatDict(new Object[][] {
+ * { "key1", 1 },
+ * { "key2", 2 }
+ * });
+ *
+ */
+ public LongDict(Object[][] pairs) {
+ count = pairs.length;
+ this.keys = new String[count];
+ this.values = new long[count];
+ for (int i = 0; i < count; i++) {
+ keys[i] = (String) pairs[i][0];
+ values[i] = (Integer) pairs[i][1];
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ /**
+ * Returns the number of key/value pairs
+ *
+ * @webref intdict:method
+ * @brief Returns the number of key/value pairs
+ */
+ public int size() {
+ return count;
+ }
+
+
+ /**
+ * Resize the internal data, this can only be used to shrink the list.
+ * Helpful for situations like sorting and then grabbing the top 50 entries.
+ */
+ public void resize(int length) {
+ if (length > count) {
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
+ }
+ if (length < 1) {
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
+ }
+
+ String[] newKeys = new String[length];
+ long[] newValues = new long[length];
+ PApplet.arrayCopy(keys, newKeys, length);
+ PApplet.arrayCopy(values, newValues, length);
+ keys = newKeys;
+ values = newValues;
+ count = length;
+ resetIndices();
+ }
+
+
+ /**
+ * Remove all entries.
+ *
+ * @webref intdict:method
+ * @brief Remove all entries
+ */
+ public void clear() {
+ count = 0;
+ indices = new HashMap<>();
+ }
+
+
+ private void resetIndices() {
+ indices = new HashMap<>(count);
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public class Entry {
+ public String key;
+ public long value;
+
+ Entry(String key, long value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+
+ public Iterable entries() {
+ return new Iterable() {
+
+ public Iterator iterator() {
+ return entryIterator();
+ }
+ };
+ }
+
+
+ public Iterator entryIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Entry next() {
+ ++index;
+ Entry e = new Entry(keys[index], values[index]);
+ return e;
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public String key(int index) {
+ return keys[index];
+ }
+
+
+ protected void crop() {
+ if (count != keys.length) {
+ keys = PApplet.subset(keys, 0, count);
+ values = PApplet.subset(values, 0, count);
+ }
+ }
+
+
+ public Iterable keys() {
+ return new Iterable() {
+
+ @Override
+ public Iterator iterator() {
+ return keyIterator();
+ }
+ };
+ }
+
+
+ // Use this to iterate when you want to be able to remove elements along the way
+ public Iterator keyIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public String next() {
+ return key(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ /**
+ * Return a copy of the internal keys array. This array can be modified.
+ *
+ * @webref intdict:method
+ * @brief Return a copy of the internal keys array
+ */
+ public String[] keyArray() {
+ crop();
+ return keyArray(null);
+ }
+
+
+ public String[] keyArray(String[] outgoing) {
+ if (outgoing == null || outgoing.length != count) {
+ outgoing = new String[count];
+ }
+ System.arraycopy(keys, 0, outgoing, 0, count);
+ return outgoing;
+ }
+
+
+ public long value(int index) {
+ return values[index];
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Return the internal array being used to store the values
+ */
+ public Iterable values() {
+ return new Iterable() {
+
+ @Override
+ public Iterator iterator() {
+ return valueIterator();
+ }
+ };
+ }
+
+
+ public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Long next() {
+ return value(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ /**
+ * Create a new array and copy each of the values into it.
+ *
+ * @webref intdict:method
+ * @brief Create a new array and copy each of the values into it
+ */
+ public int[] valueArray() {
+ crop();
+ return valueArray(null);
+ }
+
+
+ /**
+ * Fill an already-allocated array with the values (more efficient than
+ * creating a new array each time). If 'array' is null, or not the same
+ * size as the number of values, a new array will be allocated and returned.
+ *
+ * @param array values to copy into the array
+ */
+ public int[] valueArray(int[] array) {
+ if (array == null || array.length != size()) {
+ array = new int[count];
+ }
+ System.arraycopy(values, 0, array, 0, count);
+ return array;
+ }
+
+
+ /**
+ * Return a value for the specified key.
+ *
+ * @webref intdict:method
+ * @brief Return a value for the specified key
+ */
+ public long get(String key) {
+ int index = index(key);
+ if (index == -1) {
+ throw new IllegalArgumentException("No key named '" + key + "'");
+ }
+ return values[index];
+ }
+
+
+ public long get(String key, long alternate) {
+ int index = index(key);
+ if (index == -1) return alternate;
+ return values[index];
+ }
+
+
+ /**
+ * Create a new key/value pair or change the value of one.
+ *
+ * @webref intdict:method
+ * @brief Create a new key/value pair or change the value of one
+ */
+ public void set(String key, long amount) {
+ int index = index(key);
+ if (index == -1) {
+ create(key, amount);
+ } else {
+ values[index] = amount;
+ }
+ }
+
+
+ public void setIndex(int index, String key, long value) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ keys[index] = key;
+ values[index] = value;
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Check if a key is a part of the data structure
+ */
+ public boolean hasKey(String key) {
+ return index(key) != -1;
+ }
+
+
+ /**
+ * Increase the value associated with a specific key by 1.
+ *
+ * @webref intdict:method
+ * @brief Increase the value of a specific key value by 1
+ */
+ public void increment(String key) {
+ add(key, 1);
+ }
+
+
+ /**
+ * Merge another dictionary into this one. Calling this increment()
+ * since it doesn't make sense in practice for the other dictionary types,
+ * even though it's technically an add().
+ */
+ public void increment(LongDict dict) {
+ for (int i = 0; i < dict.count; i++) {
+ add(dict.key(i), dict.value(i));
+ }
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Add to a value
+ */
+ public void add(String key, long amount) {
+ int index = index(key);
+ if (index == -1) {
+ create(key, amount);
+ } else {
+ values[index] += amount;
+ }
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Subtract from a value
+ */
+ public void sub(String key, long amount) {
+ add(key, -amount);
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Multiply a value
+ */
+ public void mult(String key, long amount) {
+ int index = index(key);
+ if (index != -1) {
+ values[index] *= amount;
+ }
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Divide a value
+ */
+ public void div(String key, long amount) {
+ int index = index(key);
+ if (index != -1) {
+ values[index] /= amount;
+ }
+ }
+
+
+ private void checkMinMax(String functionName) {
+ if (count == 0) {
+ String msg =
+ String.format("Cannot use %s() on an empty %s.",
+ functionName, getClass().getSimpleName());
+ throw new RuntimeException(msg);
+ }
+ }
+
+
+ // return the index of the minimum value
+ public int minIndex() {
+ //checkMinMax("minIndex");
+ if (count == 0) return -1;
+
+ int index = 0;
+ long value = values[0];
+ for (int i = 1; i < count; i++) {
+ if (values[i] < value) {
+ index = i;
+ value = values[i];
+ }
+ }
+ return index;
+ }
+
+
+ // return the key for the minimum value
+ public String minKey() {
+ checkMinMax("minKey");
+ int index = minIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ // return the minimum value, or throw an error if there are no values
+ public long minValue() {
+ checkMinMax("minValue");
+ return values[minIndex()];
+ }
+
+
+ // return the index of the max value
+ public int maxIndex() {
+ //checkMinMax("maxIndex");
+ if (count == 0) {
+ return -1;
+ }
+ int index = 0;
+ long value = values[0];
+ for (int i = 1; i < count; i++) {
+ if (values[i] > value) {
+ index = i;
+ value = values[i];
+ }
+ }
+ return index;
+ }
+
+
+ /** return the key corresponding to the maximum value or null if no entries */
+ public String maxKey() {
+ //checkMinMax("maxKey");
+ int index = maxIndex();
+ if (index == -1) {
+ return null;
+ }
+ return keys[index];
+ }
+
+
+ // return the maximum value or throw an error if zero length
+ public long maxValue() {
+ checkMinMax("maxIndex");
+ return values[maxIndex()];
+ }
+
+
+ public long sum() {
+ long sum = 0;
+ for (int i = 0; i < count; i++) {
+ sum += values[i];
+ }
+ return sum;
+ }
+
+
+ public int index(String what) {
+ Integer found = indices.get(what);
+ return (found == null) ? -1 : found.intValue();
+ }
+
+
+ protected void create(String what, long much) {
+ if (count == keys.length) {
+ keys = PApplet.expand(keys);
+ values = PApplet.expand(values);
+ }
+ indices.put(what, Integer.valueOf(count));
+ keys[count] = what;
+ values[count] = much;
+ count++;
+ }
+
+
+ /**
+ * @webref intdict:method
+ * @brief Remove a key/value pair
+ */
+ public long remove(String key) {
+ int index = index(key);
+ if (index == -1) {
+ throw new NoSuchElementException("'" + key + "' not found");
+ }
+ long value = values[index];
+ removeIndex(index);
+ return value;
+ }
+
+
+ public long removeIndex(int index) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ long value = values[index];
+ indices.remove(keys[index]);
+ for (int i = index; i < count-1; i++) {
+ keys[i] = keys[i+1];
+ values[i] = values[i+1];
+ indices.put(keys[i], i);
+ }
+ count--;
+ keys[count] = null;
+ values[count] = 0;
+ return value;
+ }
+
+
+ public void swap(int a, int b) {
+ String tkey = keys[a];
+ long tvalue = values[a];
+ keys[a] = keys[b];
+ values[a] = values[b];
+ keys[b] = tkey;
+ values[b] = tvalue;
+
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
+ }
+
+
+ /**
+ * Sort the keys alphabetically (ignoring case). Uses the value as a
+ * tie-breaker (only really possible with a key that has a case change).
+ *
+ * @webref intdict:method
+ * @brief Sort the keys alphabetically
+ */
+ public void sortKeys() {
+ sortImpl(true, false, true);
+ }
+
+ /**
+ * Sort the keys alphabetically in reverse (ignoring case). Uses the value as a
+ * tie-breaker (only really possible with a key that has a case change).
+ *
+ * @webref intdict:method
+ * @brief Sort the keys alphabetically in reverse
+ */
+ public void sortKeysReverse() {
+ sortImpl(true, true, true);
+ }
+
+
+ /**
+ * Sort by values in ascending order. The smallest value will be at [0].
+ *
+ * @webref intdict:method
+ * @brief Sort by values in ascending order
+ */
+ public void sortValues() {
+ sortValues(true);
+ }
+
+
+ /**
+ * Set true to ensure that the order returned is identical. Slightly
+ * slower because the tie-breaker for identical values compares the keys.
+ * @param stable
+ */
+ public void sortValues(boolean stable) {
+ sortImpl(false, false, stable);
+ }
+
+
+ /**
+ * Sort by values in descending order. The largest value will be at [0].
+ *
+ * @webref intdict:method
+ * @brief Sort by values in descending order
+ */
+ public void sortValuesReverse() {
+ sortValuesReverse(true);
+ }
+
+
+ public void sortValuesReverse(boolean stable) {
+ sortImpl(false, true, stable);
+ }
+
+
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
+ final boolean stable) {
+ Sort s = new Sort() {
+ @Override
+ public int size() {
+ return count;
+ }
+
+ @Override
+ public int compare(int a, int b) {
+ long diff = 0;
+ if (useKeys) {
+ diff = keys[a].compareToIgnoreCase(keys[b]);
+ if (diff == 0) {
+ diff = values[a] - values[b];
+ }
+ } else { // sort values
+ diff = values[a] - values[b];
+ if (diff == 0 && stable) {
+ diff = keys[a].compareToIgnoreCase(keys[b]);
+ }
+ }
+ if (diff == 0) {
+ return 0;
+ } else if (reverse) {
+ return diff < 0 ? 1 : -1;
+ } else {
+ return diff < 0 ? -1 : 1;
+ }
+ }
+
+ @Override
+ public void swap(int a, int b) {
+ LongDict.this.swap(a, b);
+ }
+ };
+ s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ resetIndices();
+ }
+
+
+ /**
+ * Sum all of the values in this dictionary, then return a new FloatDict of
+ * each key, divided by the total sum. The total for all values will be ~1.0.
+ * @return an IntDict with the original keys, mapped to their pct of the total
+ */
+ public FloatDict getPercent() {
+ double sum = sum(); // a little more accuracy
+ FloatDict outgoing = new FloatDict();
+ for (int i = 0; i < size(); i++) {
+ double percent = value(i) / sum;
+ outgoing.set(key(i), (float) percent);
+ }
+ return outgoing;
+ }
+
+
+ /** Returns a duplicate copy of this object. */
+ public LongDict copy() {
+ LongDict outgoing = new LongDict(count);
+ System.arraycopy(keys, 0, outgoing.keys, 0, count);
+ System.arraycopy(values, 0, outgoing.values, 0, count);
+ for (int i = 0; i < count; i++) {
+ outgoing.indices.put(keys[i], i);
+ }
+ outgoing.count = count;
+ return outgoing;
+ }
+
+
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write tab-delimited entries to a PrintWriter
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(keys[i] + "\t" + values[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ StringList items = new StringList();
+ for (int i = 0; i < count; i++) {
+ items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
+ }
+ return "{ " + items.join(", ") + " }";
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
+ }
+}
diff --git a/libs/processing-core/src/main/java/processing/data/LongList.java b/libs/processing-core/src/main/java/processing/data/LongList.java
new file mode 100644
index 000000000..77bbd8b15
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/data/LongList.java
@@ -0,0 +1,937 @@
+package processing.data;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Random;
+
+import processing.core.PApplet;
+
+
+// splice, slice, subset, concat, reverse
+
+// trim, join for String versions
+
+
+/**
+ * Helper class for a list of ints. Lists are designed to have some of the
+ * features of ArrayLists, but to maintain the simplicity and efficiency of
+ * working with arrays.
+ *
+ * Functions like sort() and shuffle() always act on the list itself. To get
+ * a sorted copy, use list.copy().sort().
+ *
+ * @webref data:composite
+ * @see FloatList
+ * @see StringList
+ */
+public class LongList implements Iterable {
+ protected int count;
+ protected long[] data;
+
+
+ public LongList() {
+ data = new long[10];
+ }
+
+
+ /**
+ * @nowebref
+ */
+ public LongList(int length) {
+ data = new long[length];
+ }
+
+
+ /**
+ * @nowebref
+ */
+ public LongList(int[] source) {
+ count = source.length;
+ data = new long[count];
+ System.arraycopy(source, 0, data, 0, count);
+ }
+
+
+ /**
+ * Construct an IntList from an iterable pile of objects.
+ * For instance, a float array, an array of strings, who knows).
+ * Un-parseable or null values will be set to 0.
+ * @nowebref
+ */
+ public LongList(Iterable iter) {
+ this(10);
+ for (Object o : iter) {
+ if (o == null) {
+ append(0); // missing value default
+ } else if (o instanceof Number) {
+ append(((Number) o).intValue());
+ } else {
+ append(PApplet.parseInt(o.toString().trim()));
+ }
+ }
+ crop();
+ }
+
+
+ /**
+ * Construct an IntList from a random pile of objects.
+ * Un-parseable or null values will be set to zero.
+ */
+ public LongList(Object... items) {
+ final int missingValue = 0; // nuts, can't be last/final/second arg
+
+ count = items.length;
+ data = new long[count];
+ int index = 0;
+ for (Object o : items) {
+ int value = missingValue;
+ if (o != null) {
+ if (o instanceof Number) {
+ value = ((Number) o).intValue();
+ } else {
+ value = PApplet.parseInt(o.toString().trim(), missingValue);
+ }
+ }
+ data[index++] = value;
+ }
+ }
+
+
+ static public LongList fromRange(int stop) {
+ return fromRange(0, stop);
+ }
+
+
+ static public LongList fromRange(int start, int stop) {
+ int count = stop - start;
+ LongList newbie = new LongList(count);
+ for (int i = 0; i < count; i++) {
+ newbie.set(i, start+i);
+ }
+ return newbie;
+ }
+
+
+ /**
+ * Improve efficiency by removing allocated but unused entries from the
+ * internal array used to store the data. Set to private, though it could
+ * be useful to have this public if lists are frequently making drastic
+ * size changes (from very large to very small).
+ */
+ private void crop() {
+ if (count != data.length) {
+ data = PApplet.subset(data, 0, count);
+ }
+ }
+
+
+ /**
+ * Get the length of the list.
+ *
+ * @webref intlist:method
+ * @brief Get the length of the list
+ */
+ public int size() {
+ return count;
+ }
+
+
+ public void resize(int length) {
+ if (length > data.length) {
+ long[] temp = new long[length];
+ System.arraycopy(data, 0, temp, 0, count);
+ data = temp;
+
+ } else if (length > count) {
+ Arrays.fill(data, count, length, 0);
+ }
+ count = length;
+ }
+
+
+ /**
+ * Remove all entries from the list.
+ *
+ * @webref intlist:method
+ * @brief Remove all entries from the list
+ */
+ public void clear() {
+ count = 0;
+ }
+
+
+ /**
+ * Get an entry at a particular index.
+ *
+ * @webref intlist:method
+ * @brief Get an entry at a particular index
+ */
+ public long get(int index) {
+ if (index >= this.count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return data[index];
+ }
+
+
+ /**
+ * Set the entry at a particular index. If the index is past the length of
+ * the list, it'll expand the list to accommodate, and fill the intermediate
+ * entries with 0s.
+ *
+ * @webref intlist:method
+ * @brief Set the entry at a particular index
+ */
+ public void set(int index, int what) {
+ if (index >= count) {
+ data = PApplet.expand(data, index+1);
+ for (int i = count; i < index; i++) {
+ data[i] = 0;
+ }
+ count = index+1;
+ }
+ data[index] = what;
+ }
+
+
+ /** Just an alias for append(), but matches pop() */
+ public void push(int value) {
+ append(value);
+ }
+
+
+ public long pop() {
+ if (count == 0) {
+ throw new RuntimeException("Can't call pop() on an empty list");
+ }
+ long value = get(count-1);
+ count--;
+ return value;
+ }
+
+
+ /**
+ * Remove an element from the specified index
+ *
+ * @webref intlist:method
+ * @brief Remove an element from the specified index
+ */
+ public long remove(int index) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ long entry = data[index];
+// int[] outgoing = new int[count - 1];
+// System.arraycopy(data, 0, outgoing, 0, index);
+// count--;
+// System.arraycopy(data, index + 1, outgoing, 0, count - index);
+// data = outgoing;
+ // For most cases, this actually appears to be faster
+ // than arraycopy() on an array copying into itself.
+ for (int i = index; i < count-1; i++) {
+ data[i] = data[i+1];
+ }
+ count--;
+ return entry;
+ }
+
+
+ // Remove the first instance of a particular value,
+ // and return the index at which it was found.
+ public int removeValue(int value) {
+ int index = index(value);
+ if (index != -1) {
+ remove(index);
+ return index;
+ }
+ return -1;
+ }
+
+
+ // Remove all instances of a particular value,
+ // and return the number of values found and removed
+ public int removeValues(int value) {
+ int ii = 0;
+ for (int i = 0; i < count; i++) {
+ if (data[i] != value) {
+ data[ii++] = data[i];
+ }
+ }
+ int removed = count - ii;
+ count = ii;
+ return removed;
+ }
+
+
+ /**
+ * Add a new entry to the list.
+ *
+ * @webref intlist:method
+ * @brief Add a new entry to the list
+ */
+ public void append(long value) {
+ if (count == data.length) {
+ data = PApplet.expand(data);
+ }
+ data[count++] = value;
+ }
+
+
+ public void append(int[] values) {
+ for (int v : values) {
+ append(v);
+ }
+ }
+
+
+ public void append(LongList list) {
+ for (long v : list.values()) { // will concat the list...
+ append(v);
+ }
+ }
+
+
+ /** Add this value, but only if it's not already in the list. */
+ public void appendUnique(int value) {
+ if (!hasValue(value)) {
+ append(value);
+ }
+ }
+
+
+// public void insert(int index, int value) {
+// if (index+1 > count) {
+// if (index+1 < data.length) {
+// }
+// }
+// if (index >= data.length) {
+// data = PApplet.expand(data, index+1);
+// data[index] = value;
+// count = index+1;
+//
+// } else if (count == data.length) {
+// if (index >= count) {
+// //int[] temp = new int[count << 1];
+// System.arraycopy(data, 0, temp, 0, index);
+// temp[index] = value;
+// System.arraycopy(data, index, temp, index+1, count - index);
+// data = temp;
+//
+// } else {
+// // data[] has room to grow
+// // for() loop believed to be faster than System.arraycopy over itself
+// for (int i = count; i > index; --i) {
+// data[i] = data[i-1];
+// }
+// data[index] = value;
+// count++;
+// }
+// }
+
+
+ public void insert(int index, long value) {
+ insert(index, new long[] { value });
+ }
+
+
+ // same as splice
+ public void insert(int index, long[] values) {
+ if (index < 0) {
+ throw new IllegalArgumentException("insert() index cannot be negative: it was " + index);
+ }
+ if (index >= data.length) {
+ throw new IllegalArgumentException("insert() index " + index + " is past the end of this list");
+ }
+
+ long[] temp = new long[count + values.length];
+
+ // Copy the old values, but not more than already exist
+ System.arraycopy(data, 0, temp, 0, Math.min(count, index));
+
+ // Copy the new values into the proper place
+ System.arraycopy(values, 0, temp, index, values.length);
+
+// if (index < count) {
+ // The index was inside count, so it's a true splice/insert
+ System.arraycopy(data, index, temp, index+values.length, count - index);
+ count = count + values.length;
+// } else {
+// // The index was past 'count', so the new count is weirder
+// count = index + values.length;
+// }
+ data = temp;
+ }
+
+
+ public void insert(int index, LongList list) {
+ insert(index, list.values());
+ }
+
+
+ // below are aborted attempts at more optimized versions of the code
+ // that are harder to read and debug...
+
+// if (index + values.length >= count) {
+// // We're past the current 'count', check to see if we're still allocated
+// // index 9, data.length = 10, values.length = 1
+// if (index + values.length < data.length) {
+// // There's still room for these entries, even though it's past 'count'.
+// // First clear out the entries leading up to it, however.
+// for (int i = count; i < index; i++) {
+// data[i] = 0;
+// }
+// data[index] =
+// }
+// if (index >= data.length) {
+// int length = index + values.length;
+// int[] temp = new int[length];
+// System.arraycopy(data, 0, temp, 0, count);
+// System.arraycopy(values, 0, temp, index, values.length);
+// data = temp;
+// count = data.length;
+// } else {
+//
+// }
+//
+// } else if (count == data.length) {
+// int[] temp = new int[count << 1];
+// System.arraycopy(data, 0, temp, 0, index);
+// temp[index] = value;
+// System.arraycopy(data, index, temp, index+1, count - index);
+// data = temp;
+//
+// } else {
+// // data[] has room to grow
+// // for() loop believed to be faster than System.arraycopy over itself
+// for (int i = count; i > index; --i) {
+// data[i] = data[i-1];
+// }
+// data[index] = value;
+// count++;
+// }
+
+
+ /** Return the first index of a particular value. */
+ public int index(int what) {
+ /*
+ if (indexCache != null) {
+ try {
+ return indexCache.get(what);
+ } catch (Exception e) { // not there
+ return -1;
+ }
+ }
+ */
+ for (int i = 0; i < count; i++) {
+ if (data[i] == what) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ // !!! TODO this is not yet correct, because it's not being reset when
+ // the rest of the entries are changed
+// protected void cacheIndices() {
+// indexCache = new HashMap();
+// for (int i = 0; i < count; i++) {
+// indexCache.put(data[i], i);
+// }
+// }
+
+ /**
+ * @webref intlist:method
+ * @brief Check if a number is a part of the list
+ */
+ public boolean hasValue(int value) {
+// if (indexCache == null) {
+// cacheIndices();
+// }
+// return index(what) != -1;
+ for (int i = 0; i < count; i++) {
+ if (data[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @webref intlist:method
+ * @brief Add one to a value
+ */
+ public void increment(int index) {
+ if (count <= index) {
+ resize(index + 1);
+ }
+ data[index]++;
+ }
+
+
+ private void boundsProblem(int index, String method) {
+ final String msg = String.format("The list size is %d. " +
+ "You cannot %s() to element %d.", count, method, index);
+ throw new ArrayIndexOutOfBoundsException(msg);
+ }
+
+
+ /**
+ * @webref intlist:method
+ * @brief Add to a value
+ */
+ public void add(int index, int amount) {
+ if (index < count) {
+ data[index] += amount;
+ } else {
+ boundsProblem(index, "add");
+ }
+ }
+
+ /**
+ * @webref intlist:method
+ * @brief Subtract from a value
+ */
+ public void sub(int index, int amount) {
+ if (index < count) {
+ data[index] -= amount;
+ } else {
+ boundsProblem(index, "sub");
+ }
+ }
+
+ /**
+ * @webref intlist:method
+ * @brief Multiply a value
+ */
+ public void mult(int index, int amount) {
+ if (index < count) {
+ data[index] *= amount;
+ } else {
+ boundsProblem(index, "mult");
+ }
+ }
+
+ /**
+ * @webref intlist:method
+ * @brief Divide a value
+ */
+ public void div(int index, int amount) {
+ if (index < count) {
+ data[index] /= amount;
+ } else {
+ boundsProblem(index, "div");
+ }
+ }
+
+
+ private void checkMinMax(String functionName) {
+ if (count == 0) {
+ String msg =
+ String.format("Cannot use %s() on an empty %s.",
+ functionName, getClass().getSimpleName());
+ throw new RuntimeException(msg);
+ }
+ }
+
+
+ /**
+ * @webref intlist:method
+ * @brief Return the smallest value
+ */
+ public long min() {
+ checkMinMax("min");
+ long outgoing = data[0];
+ for (int i = 1; i < count; i++) {
+ if (data[i] < outgoing) outgoing = data[i];
+ }
+ return outgoing;
+ }
+
+
+ // returns the index of the minimum value.
+ // if there are ties, it returns the first one found.
+ public int minIndex() {
+ checkMinMax("minIndex");
+ long value = data[0];
+ int index = 0;
+ for (int i = 1; i < count; i++) {
+ if (data[i] < value) {
+ value = data[i];
+ index = i;
+ }
+ }
+ return index;
+ }
+
+
+ /**
+ * @webref intlist:method
+ * @brief Return the largest value
+ */
+ public long max() {
+ checkMinMax("max");
+ long outgoing = data[0];
+ for (int i = 1; i < count; i++) {
+ if (data[i] > outgoing) outgoing = data[i];
+ }
+ return outgoing;
+ }
+
+
+ // returns the index of the maximum value.
+ // if there are ties, it returns the first one found.
+ public int maxIndex() {
+ checkMinMax("maxIndex");
+ long value = data[0];
+ int index = 0;
+ for (int i = 1; i < count; i++) {
+ if (data[i] > value) {
+ value = data[i];
+ index = i;
+ }
+ }
+ return index;
+ }
+
+
+ public int sum() {
+ long amount = sumLong();
+ if (amount > Integer.MAX_VALUE) {
+ throw new RuntimeException("sum() exceeds " + Integer.MAX_VALUE + ", use sumLong()");
+ }
+ if (amount < Integer.MIN_VALUE) {
+ throw new RuntimeException("sum() less than " + Integer.MIN_VALUE + ", use sumLong()");
+ }
+ return (int) amount;
+ }
+
+
+ public long sumLong() {
+ long sum = 0;
+ for (int i = 0; i < count; i++) {
+ sum += data[i];
+ }
+ return sum;
+ }
+
+
+ /**
+ * Sorts the array in place.
+ *
+ * @webref intlist:method
+ * @brief Sorts the array, lowest to highest
+ */
+ public void sort() {
+ Arrays.sort(data, 0, count);
+ }
+
+
+ /**
+ * Reverse sort, orders values from highest to lowest.
+ *
+ * @webref intlist:method
+ * @brief Reverse sort, orders values from highest to lowest
+ */
+ public void sortReverse() {
+ new Sort() {
+ @Override
+ public int size() {
+ return count;
+ }
+
+ @Override
+ public int compare(int a, int b) {
+ long diff = data[b] - data[a];
+ return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
+ }
+
+ @Override
+ public void swap(int a, int b) {
+ long temp = data[a];
+ data[a] = data[b];
+ data[b] = temp;
+ }
+ }.run();
+ }
+
+
+ // use insert()
+// public void splice(int index, int value) {
+// }
+
+
+// public void subset(int start) {
+// subset(start, count - start);
+// }
+//
+//
+// public void subset(int start, int num) {
+// for (int i = 0; i < num; i++) {
+// data[i] = data[i+start];
+// }
+// count = num;
+// }
+
+ /**
+ * @webref intlist:method
+ * @brief Reverse the order of the list elements
+ */
+ public void reverse() {
+ int ii = count - 1;
+ for (int i = 0; i < count/2; i++) {
+ long t = data[i];
+ data[i] = data[ii];
+ data[ii] = t;
+ --ii;
+ }
+ }
+
+
+ /**
+ * Randomize the order of the list elements. Note that this does not
+ * obey the randomSeed() function in PApplet.
+ *
+ * @webref intlist:method
+ * @brief Randomize the order of the list elements
+ */
+ public void shuffle() {
+ Random r = new Random();
+ int num = count;
+ while (num > 1) {
+ int value = r.nextInt(num);
+ num--;
+ long temp = data[num];
+ data[num] = data[value];
+ data[value] = temp;
+ }
+ }
+
+
+ /**
+ * Randomize the list order using the random() function from the specified
+ * sketch, allowing shuffle() to use its current randomSeed() setting.
+ */
+ public void shuffle(PApplet sketch) {
+ int num = count;
+ while (num > 1) {
+ int value = (int) sketch.random(num);
+ num--;
+ long temp = data[num];
+ data[num] = data[value];
+ data[value] = temp;
+ }
+ }
+
+
+ public LongList copy() {
+ LongList outgoing = new LongList(data);
+ outgoing.count = count;
+ return outgoing;
+ }
+
+
+ /**
+ * Returns the actual array being used to store the data. For advanced users,
+ * this is the fastest way to access a large list. Suitable for iterating
+ * with a for() loop, but modifying the list will have terrible consequences.
+ */
+ public long[] values() {
+ crop();
+ return data;
+ }
+
+
+ @Override
+ public Iterator iterator() {
+// public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ LongList.this.remove(index);
+ index--;
+ }
+
+ public Long next() {
+ return data[++index];
+ }
+
+ public boolean hasNext() {
+ return index+1 < count;
+ }
+ };
+ }
+
+
+ /**
+ * Create a new array with a copy of all the values.
+ *
+ * @return an array sized by the length of the list with each of the values.
+ * @webref intlist:method
+ * @brief Create a new array with a copy of all the values
+ */
+ public int[] array() {
+ return array(null);
+ }
+
+
+ /**
+ * Copy values into the specified array. If the specified array is null or
+ * not the same size, a new array will be allocated.
+ * @param array
+ */
+ public int[] array(int[] array) {
+ if (array == null || array.length != count) {
+ array = new int[count];
+ }
+ System.arraycopy(data, 0, array, 0, count);
+ return array;
+ }
+
+
+// public int[] toIntArray() {
+// int[] outgoing = new int[count];
+// for (int i = 0; i < count; i++) {
+// outgoing[i] = (int) data[i];
+// }
+// return outgoing;
+// }
+
+
+// public long[] toLongArray() {
+// long[] outgoing = new long[count];
+// for (int i = 0; i < count; i++) {
+// outgoing[i] = (long) data[i];
+// }
+// return outgoing;
+// }
+
+
+// public float[] toFloatArray() {
+// float[] outgoing = new float[count];
+// System.arraycopy(data, 0, outgoing, 0, count);
+// return outgoing;
+// }
+
+
+// public double[] toDoubleArray() {
+// double[] outgoing = new double[count];
+// for (int i = 0; i < count; i++) {
+// outgoing[i] = data[i];
+// }
+// return outgoing;
+// }
+
+
+// public String[] toStringArray() {
+// String[] outgoing = new String[count];
+// for (int i = 0; i < count; i++) {
+// outgoing[i] = String.valueOf(data[i]);
+// }
+// return outgoing;
+// }
+
+
+ /**
+ * Returns a normalized version of this array. Called getPercent() for
+ * consistency with the Dict classes. It's a getter method because it needs
+ * to returns a new list (because IntList/Dict can't do percentages or
+ * normalization in place on int values).
+ */
+ public FloatList getPercent() {
+ double sum = 0;
+ for (float value : array()) {
+ sum += value;
+ }
+ FloatList outgoing = new FloatList(count);
+ for (int i = 0; i < count; i++) {
+ double percent = data[i] / sum;
+ outgoing.set(i, (float) percent);
+ }
+ return outgoing;
+ }
+
+
+// /**
+// * Count the number of times each entry is found in this list.
+// * Converts each entry to a String so it can be used as a key.
+// */
+// public IntDict getTally() {
+// IntDict outgoing = new IntDict();
+// for (int i = 0; i < count; i++) {
+// outgoing.increment(String.valueOf(data[i]));
+// }
+// return outgoing;
+// }
+
+
+ public LongList getSubset(int start) {
+ return getSubset(start, count - start);
+ }
+
+
+ public LongList getSubset(int start, int num) {
+ int[] subset = new int[num];
+ System.arraycopy(data, start, subset, 0, num);
+ return new LongList(subset);
+ }
+
+
+ public String join(String separator) {
+ if (count == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(data[0]);
+ for (int i = 1; i < count; i++) {
+ sb.append(separator);
+ sb.append(data[i]);
+ }
+ return sb.toString();
+ }
+
+
+ public void print() {
+ for (int i = 0; i < count; i++) {
+ System.out.format("[%d] %d%n", i, data[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write entries to a PrintWriter, one per line
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(data[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ return "[ " + join(", ") + " ]";
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
+ }
+}
diff --git a/core/src/processing/data/Sort.java b/libs/processing-core/src/main/java/processing/data/Sort.java
similarity index 83%
rename from core/src/processing/data/Sort.java
rename to libs/processing-core/src/main/java/processing/data/Sort.java
index d205edb12..a83fea551 100644
--- a/core/src/processing/data/Sort.java
+++ b/libs/processing-core/src/main/java/processing/data/Sort.java
@@ -31,8 +31,8 @@ protected void sort(int i, int j) {
protected int partition(int left, int right) {
int pivot = right;
do {
- while (compare(++left, pivot) < 0) ;
- while ((right != 0) && (compare(--right, pivot) > 0)) ;
+ while (compare(++left, pivot) < 0) { }
+ while ((right != 0) && (compare(--right, pivot) > 0)) { }
swap(left, right);
} while (left < right);
swap(left, right);
@@ -41,6 +41,6 @@ protected int partition(int left, int right) {
abstract public int size();
- abstract public float compare(int a, int b);
+ abstract public int compare(int a, int b);
abstract public void swap(int a, int b);
}
\ No newline at end of file
diff --git a/core/src/processing/data/StringDict.java b/libs/processing-core/src/main/java/processing/data/StringDict.java
similarity index 57%
rename from core/src/processing/data/StringDict.java
rename to libs/processing-core/src/main/java/processing/data/StringDict.java
index 6f896dfac..c66a61e4d 100644
--- a/core/src/processing/data/StringDict.java
+++ b/libs/processing-core/src/main/java/processing/data/StringDict.java
@@ -3,6 +3,7 @@
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import processing.core.PApplet;
@@ -23,7 +24,7 @@ public class StringDict {
protected String[] values;
/** Internal implementation for faster lookups */
- private HashMap indices = new HashMap();
+ private HashMap indices = new HashMap<>();
public StringDict() {
@@ -63,11 +64,13 @@ public StringDict(BufferedReader reader) {
if (pieces.length == 2) {
keys[count] = pieces[0];
values[count] = pieces[1];
+ indices.put(keys[count], count);
count++;
}
}
}
+
/**
* @nowebref
*/
@@ -83,6 +86,49 @@ public StringDict(String[] keys, String[] values) {
}
}
+
+ /**
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
+ *
+ * new StringDict(new String[][] {
+ * { "key1", "value1" },
+ * { "key2", "value2" }
+ * });
+ *
+ * It's no Python, but beats a static { } block with HashMap.put() statements.
+ */
+ public StringDict(String[][] pairs) {
+ count = pairs.length;
+ this.keys = new String[count];
+ this.values = new String[count];
+ for (int i = 0; i < count; i++) {
+ keys[i] = pairs[i][0];
+ values[i] = pairs[i][1];
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ /**
+ * Create a dictionary that maps between column titles and cell entries
+ * in a TableRow. If two columns have the same name, the later column's
+ * values will override the earlier values.
+ */
+ public StringDict(TableRow row) {
+ this(row.getColumnCount());
+
+ String[] titles = row.getColumnTitles();
+ if (titles == null) {
+ titles = new StringList(IntList.fromRange(row.getColumnCount())).array();
+ }
+ for (int col = 0; col < row.getColumnCount(); col++) {
+ set(titles[col], row.getString(col));
+ }
+ // remove unused and overwritten entries
+ crop();
+ }
+
+
/**
* @webref stringdict:method
* @brief Returns the number of key/value pairs
@@ -92,6 +138,29 @@ public int size() {
}
+ /**
+ * Resize the internal data, this can only be used to shrink the list.
+ * Helpful for situations like sorting and then grabbing the top 50 entries.
+ */
+ public void resize(int length) {
+ if (length > count) {
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
+ }
+ if (length < 1) {
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
+ }
+
+ String[] newKeys = new String[length];
+ String[] newValues = new String[length];
+ PApplet.arrayCopy(keys, newKeys, length);
+ PApplet.arrayCopy(values, newValues, length);
+ keys = newKeys;
+ values = newValues;
+ count = length;
+ resetIndices();
+ }
+
+
/**
* Remove all entries.
*
@@ -100,10 +169,67 @@ public int size() {
*/
public void clear() {
count = 0;
- indices = new HashMap();
+ indices = new HashMap<>();
+ }
+
+
+ private void resetIndices() {
+ indices = new HashMap<>(count);
+ for (int i = 0; i < count; i++) {
+ indices.put(keys[i], i);
+ }
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public class Entry {
+ public String key;
+ public String value;
+
+ Entry(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+
+ public Iterable entries() {
+ return new Iterable() {
+
+ public Iterator iterator() {
+ return entryIterator();
+ }
+ };
}
+ public Iterator entryIterator() {
+ return new Iterator() {
+ int index = -1;
+
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public Entry next() {
+ ++index;
+ Entry e = new Entry(keys[index], values[index]);
+ return e;
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
+ }
+ };
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
public String key(int index) {
return keys[index];
}
@@ -117,38 +243,33 @@ protected void crop() {
}
-// /**
-// * Return the internal array being used to store the keys. Allocated but
-// * unused entries will be removed. This array should not be modified.
-// */
-// public String[] keys() {
-// crop();
-// return keys;
-// }
-
- /**
- * @webref stringdict:method
- * @brief Return the internal array being used to store the keys
- */
public Iterable keys() {
return new Iterable() {
+ @Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
+ return keyIterator();
+ }
+ };
+ }
- public void remove() {
- removeIndex(index);
- }
- public String next() {
- return key(++index);
- }
+ // Use this to iterate when you want to be able to remove elements along the way
+ public Iterator keyIterator() {
+ return new Iterator() {
+ int index = -1;
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public String next() {
+ return key(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
}
};
}
@@ -161,6 +282,7 @@ public boolean hasNext() {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
+ crop();
return keyArray(null);
}
@@ -185,22 +307,29 @@ public String value(int index) {
public Iterable values() {
return new Iterable() {
+ @Override
public Iterator iterator() {
- return new Iterator() {
- int index = -1;
+ return valueIterator();
+ }
+ };
+ }
- public void remove() {
- removeIndex(index);
- }
- public String next() {
- return value(++index);
- }
+ public Iterator valueIterator() {
+ return new Iterator() {
+ int index = -1;
- public boolean hasNext() {
- return index+1 < size();
- }
- };
+ public void remove() {
+ removeIndex(index);
+ index--;
+ }
+
+ public String next() {
+ return value(++index);
+ }
+
+ public boolean hasNext() {
+ return index+1 < size();
}
};
}
@@ -213,6 +342,7 @@ public boolean hasNext() {
* @brief Create a new array and copy each of the values into it
*/
public String[] valueArray() {
+ crop();
return valueArray(null);
}
@@ -243,25 +373,43 @@ public String get(String key) {
return values[index];
}
+
+ public String get(String key, String alternate) {
+ int index = index(key);
+ if (index == -1) return alternate;
+ return values[index];
+ }
+
+
/**
* @webref stringdict:method
* @brief Create a new key/value pair or change the value of one
*/
- public void set(String key, String amount) {
+ public void set(String key, String value) {
int index = index(key);
if (index == -1) {
- create(key, amount);
+ create(key, value);
} else {
- values[index] = amount;
+ values[index] = value;
}
}
+ public void setIndex(int index, String key, String value) {
+ if (index < 0 || index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ keys[index] = key;
+ values[index] = value;
+ }
+
+
public int index(String what) {
Integer found = indices.get(what);
return (found == null) ? -1 : found.intValue();
}
+
/**
* @webref stringdict:method
* @brief Check if a key is a part of the data structure
@@ -276,7 +424,7 @@ protected void create(String key, String value) {
keys = PApplet.expand(keys);
values = PApplet.expand(values);
}
- indices.put(key, new Integer(count));
+ indices.put(key, Integer.valueOf(count));
keys[count] = key;
values[count] = value;
count++;
@@ -286,12 +434,14 @@ protected void create(String key, String value) {
* @webref stringdict:method
* @brief Remove a key/value pair
*/
- public int remove(String key) {
+ public String remove(String key) {
int index = index(key);
- if (index != -1) {
- removeIndex(index);
+ if (index == -1) {
+ throw new NoSuchElementException("'" + key + "' not found");
}
- return index;
+ String value = values[index];
+ removeIndex(index);
+ return value;
}
@@ -299,9 +449,8 @@ public String removeIndex(int index) {
if (index < 0 || index >= count) {
throw new ArrayIndexOutOfBoundsException(index);
}
- //System.out.println("index is " + which + " and " + keys[which]);
- String key = keys[index];
- indices.remove(key);
+ String value = values[index];
+ indices.remove(keys[index]);
for (int i = index; i < count-1; i++) {
keys[i] = keys[i+1];
values[i] = values[i+1];
@@ -310,11 +459,12 @@ public String removeIndex(int index) {
count--;
keys[count] = null;
values[count] = null;
- return key;
+ return value;
}
- protected void swap(int a, int b) {
+
+ public void swap(int a, int b) {
String tkey = keys[a];
String tvalue = values[a];
keys[a] = keys[b];
@@ -322,8 +472,8 @@ protected void swap(int a, int b) {
keys[b] = tkey;
values[b] = tvalue;
- indices.put(keys[a], new Integer(a));
- indices.put(keys[b], new Integer(b));
+// indices.put(keys[a], Integer.valueOf(a));
+// indices.put(keys[b], Integer.valueOf(b));
}
@@ -340,7 +490,7 @@ public void sortKeys() {
/**
* @webref stringdict:method
- * @brief Sort the keys alphabetially in reverse
+ * @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
sortImpl(true, true);
@@ -375,7 +525,7 @@ public int size() {
}
@Override
- public float compare(int a, int b) {
+ public int compare(int a, int b) {
int diff = 0;
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
@@ -397,6 +547,9 @@ public void swap(int a, int b) {
}
};
s.run();
+
+ // Set the indices after sort/swaps (performance fix 160411)
+ resetIndices();
}
@@ -413,9 +566,25 @@ public StringDict copy() {
}
+ public void print() {
+ for (int i = 0; i < size(); i++) {
+ System.out.println(keys[i] + " = " + values[i]);
+ }
+ }
+
+
/**
- * Write tab-delimited entries out to
- * @param writer
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write tab-delimited entries to a PrintWriter
*/
public void write(PrintWriter writer) {
for (int i = 0; i < count; i++) {
@@ -425,17 +594,20 @@ public void write(PrintWriter writer) {
}
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ StringList items = new StringList();
+ for (int i = 0; i < count; i++) {
+ items.append(JSONObject.quote(keys[i])+ ": " + JSONObject.quote(values[i]));
+ }
+ return "{ " + items.join(", ") + " }";
+ }
+
+
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName() + " size=" + size() + " { ");
- for (int i = 0; i < size(); i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append("\"" + keys[i] + "\": \"" + values[i] + "\"");
- }
- sb.append(" }");
- return sb.toString();
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
}
}
diff --git a/core/src/processing/data/StringList.java b/libs/processing-core/src/main/java/processing/data/StringList.java
similarity index 85%
rename from core/src/processing/data/StringList.java
rename to libs/processing-core/src/main/java/processing/data/StringList.java
index 5533747a3..c4de6d33c 100644
--- a/core/src/processing/data/StringList.java
+++ b/libs/processing-core/src/main/java/processing/data/StringList.java
@@ -1,5 +1,7 @@
package processing.data;
+import java.io.File;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
@@ -44,6 +46,27 @@ public StringList(String[] list) {
}
+ /**
+ * Construct a StringList from a random pile of objects. Null values will
+ * stay null, but all the others will be converted to String values.
+ */
+ public StringList(Object... items) {
+ count = items.length;
+ data = new String[count];
+ int index = 0;
+ for (Object o : items) {
+// // Not gonna go with null values staying that way because perhaps
+// // the most common case here is to immediately call join() or similar.
+// data[index++] = String.valueOf(o);
+ // Keep null values null (because join() will make non-null anyway)
+ if (o != null) { // leave null values null
+ data[index] = o.toString();
+ }
+ index++;
+ }
+ }
+
+
/**
* Create from something iterable, for instance:
* StringList list = new StringList(hashMap.keySet());
@@ -113,6 +136,9 @@ public void clear() {
* @brief Get an entry at a particular index
*/
public String get(int index) {
+ if (index >= count) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
return data[index];
}
@@ -137,6 +163,22 @@ public void set(int index, String what) {
}
+ /** Just an alias for append(), but matches pop() */
+ public void push(String value) {
+ append(value);
+ }
+
+
+ public String pop() {
+ if (count == 0) {
+ throw new RuntimeException("Can't call pop() on an empty list");
+ }
+ String value = get(count-1);
+ data[--count] = null; // avoid leak
+ return value;
+ }
+
+
/**
* Remove an element from the specified index.
*
@@ -274,6 +316,14 @@ public void append(StringList list) {
}
+ /** Add this value, but only if it's not already in the list. */
+ public void appendUnique(String value) {
+ if (!hasValue(value)) {
+ append(value);
+ }
+ }
+
+
// public void insert(int index, int value) {
// if (index+1 > count) {
// if (index+1 < data.length) {
@@ -304,12 +354,17 @@ public void append(StringList list) {
// }
+ public void insert(int index, String value) {
+ insert(index, new String[] { value });
+ }
+
+
// same as splice
public void insert(int index, String[] values) {
if (index < 0) {
throw new IllegalArgumentException("insert() index cannot be negative: it was " + index);
}
- if (index >= values.length) {
+ if (index >= data.length) {
throw new IllegalArgumentException("insert() index " + index + " is past the end of this list");
}
@@ -461,8 +516,8 @@ public int size() {
}
@Override
- public float compare(int a, int b) {
- float diff = data[a].compareToIgnoreCase(data[b]);
+ public int compare(int a, int b) {
+ int diff = data[a].compareToIgnoreCase(data[b]);
return reverse ? -diff : diff;
}
@@ -495,7 +550,7 @@ public void swap(int a, int b) {
/**
* @webref stringlist:method
- * @brief To come...
+ * @brief Reverse the order of the list elements
*/
public void reverse() {
int ii = count - 1;
@@ -592,6 +647,7 @@ public String[] values() {
}
+ @Override
public Iterator iterator() {
// return valueIterator();
// }
@@ -603,6 +659,7 @@ public Iterator iterator() {
public void remove() {
StringList.this.remove(index);
+ index--;
}
public String next() {
@@ -629,7 +686,8 @@ public String[] array() {
/**
- * Copy as many values as possible into the specified array.
+ * Copy values into the specified array. If the specified array is null or
+ * not the same size, a new array will be allocated.
* @param array
*/
public String[] array(String[] array) {
@@ -679,14 +737,6 @@ public IntDict getOrder() {
}
-// public void println() {
-// for (int i = 0; i < count; i++) {
-// System.out.println("[" + i + "] " + data[i]);
-// }
-// System.out.flush();
-// }
-
-
public String join(String separator) {
if (count == 0) {
return "";
@@ -701,23 +751,48 @@ public String join(String separator) {
}
-// static public StringList split(String value, char delim) {
-// String[] array = PApplet.split(value, delim);
-// return new StringList(array);
-// }
+ public void print() {
+ for (int i = 0; i < count; i++) {
+ System.out.format("[%d] %s%n", i, data[i]);
+ }
+ }
+
+
+ /**
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
+ */
+ public void save(File file) {
+ PrintWriter writer = PApplet.createWriter(file);
+ write(writer);
+ writer.close();
+ }
+
+
+ /**
+ * Write entries to a PrintWriter, one per line
+ */
+ public void write(PrintWriter writer) {
+ for (int i = 0; i < count; i++) {
+ writer.println(data[i]);
+ }
+ writer.flush();
+ }
+
+
+ /**
+ * Return this dictionary as a String in JSON format.
+ */
+ public String toJSON() {
+ StringList temp = new StringList();
+ for (String item : this) {
+ temp.append(JSONObject.quote(item));
+ }
+ return "[ " + temp.join(", ") + " ]";
+ }
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName() + " size=" + size() + " [ ");
- for (int i = 0; i < size(); i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append(i + ": \"" + data[i] + "\"");
- }
- sb.append(" ]");
- return sb.toString();
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
}
-}
\ No newline at end of file
+}
diff --git a/core/src/processing/data/Table.java b/libs/processing-core/src/main/java/processing/data/Table.java
similarity index 80%
rename from core/src/processing/data/Table.java
rename to libs/processing-core/src/main/java/processing/data/Table.java
index 2a3c4f857..2048376cf 100644
--- a/core/src/processing/data/Table.java
+++ b/libs/processing-core/src/main/java/processing/data/Table.java
@@ -3,7 +3,8 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2011-12 Ben Fry and Casey Reas
Copyright (c) 2006-11 Ben Fry
This library is free software; you can redistribute it and/or
@@ -25,6 +26,7 @@
import java.io.*;
import java.lang.reflect.*;
+import java.nio.charset.Charset;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
@@ -34,6 +36,7 @@
import java.util.concurrent.Executors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
import javax.xml.parsers.ParserConfigurationException;
@@ -60,6 +63,7 @@
*/
public class Table {
protected int rowCount;
+ protected int allocCount;
// protected boolean skipEmptyRows = true;
// protected boolean skipCommentLines = true;
@@ -153,12 +157,40 @@ public Table(InputStream input, String options) throws IOException {
public Table(Iterable rows) {
- boolean typed = false;
- for (TableRow row : rows) {
- if (!typed) {
- setColumnTypes(row.getColumnTypes());
+ init();
+
+ int row = 0;
+ int alloc = 10;
+
+ for (TableRow incoming : rows) {
+ if (row == 0) {
+ setColumnTypes(incoming.getColumnTypes());
+ setColumnTitles(incoming.getColumnTitles());
+ // Do this after setting types, otherwise it'll attempt to parse the
+ // allocated but empty rows, and drive CATEGORY columns nutso.
+ setRowCount(alloc);
+ // sometimes more columns than titles (and types?)
+ setColumnCount(incoming.getColumnCount());
+
+ } else if (row == alloc) {
+ // Far more efficient than re-allocating all columns and doing a copy
+ alloc *= 2;
+ setRowCount(alloc);
}
- addRow(row);
+
+ //addRow(row);
+// try {
+ setRow(row++, incoming);
+// } catch (ArrayIndexOutOfBoundsException aioobe) {
+// for (int i = 0; i < incoming.getColumnCount(); i++) {
+// System.out.format("[%d] %s%n", i, incoming.getString(i));
+// }
+// throw aioobe;
+// }
+ }
+ // Shrink the table to only the rows that were used
+ if (row != alloc) {
+ setRowCount(row);
}
}
@@ -272,7 +304,7 @@ protected String checkOptions(File file, String options) throws IOException {
static final String[] loadExtensions = { "csv", "tsv", "ods", "bin" };
- static final String[] saveExtensions = { "csv", "tsv", "html", "bin" };
+ static final String[] saveExtensions = { "csv", "tsv", "ods", "bin", "html" };
static public String extensionOptions(boolean loading, String filename, String options) {
String extension = PApplet.checkExtension(filename);
@@ -294,19 +326,18 @@ static public String extensionOptions(boolean loading, String filename, String o
protected void parse(InputStream input, String options) throws IOException {
- //init();
-
- boolean awfulCSV = false;
+// boolean awfulCSV = false;
boolean header = false;
String extension = null;
boolean binary = false;
+ String encoding = "UTF-8";
String worksheet = null;
final String sheetParam = "worksheet=";
String[] opts = null;
if (options != null) {
- opts = PApplet.splitTokens(options, " ,");
+ opts = PApplet.trim(PApplet.split(options, ','));
for (String opt : opts) {
if (opt.equals("tsv")) {
extension = "tsv";
@@ -315,8 +346,9 @@ protected void parse(InputStream input, String options) throws IOException {
} else if (opt.equals("ods")) {
extension = "ods";
} else if (opt.equals("newlines")) {
- awfulCSV = true;
- extension = "csv";
+ //awfulCSV = true;
+ //extension = "csv";
+ throw new IllegalArgumentException("The 'newlines' option is no longer necessary.");
} else if (opt.equals("bin")) {
binary = true;
extension = "bin";
@@ -326,6 +358,8 @@ protected void parse(InputStream input, String options) throws IOException {
worksheet = opt.substring(sheetParam.length());
} else if (opt.startsWith("dictionary=")) {
// ignore option, this is only handled by PApplet
+ } else if (opt.startsWith("encoding=")) {
+ encoding = opt.substring(9);
} else {
throw new IllegalArgumentException("'" + opt + "' is not a valid option for loading a Table");
}
@@ -340,17 +374,30 @@ protected void parse(InputStream input, String options) throws IOException {
loadBinary(input);
} else if (extension.equals("ods")) {
- odsParse(input, worksheet);
+ odsParse(input, worksheet, header);
} else {
- BufferedReader reader = PApplet.createReader(input);
- if (awfulCSV) {
+ InputStreamReader isr = new InputStreamReader(input, encoding);
+ BufferedReader reader = new BufferedReader(isr);
+
+ // strip out the Unicode BOM, if present
+ reader.mark(1);
+ int c = reader.read();
+ // if not the BOM, back up to the beginning again
+ if (c != '\uFEFF') {
+ reader.reset();
+ }
+
+ /*
+ if (awfulCSV) {
parseAwfulCSV(reader, header);
} else if ("tsv".equals(extension)) {
parseBasic(reader, header, true);
} else if ("csv".equals(extension)) {
parseBasic(reader, header, false);
}
+ */
+ parseBasic(reader, header, "tsv".equals(extension));
}
}
@@ -369,16 +416,16 @@ protected void parseBasic(BufferedReader reader,
setRowCount(row << 1);
}
if (row == 0 && header) {
- setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line));
+ setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader));
header = false;
} else {
- setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line));
+ setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader));
row++;
}
- // this is problematic unless we're going to calculate rowCount first
if (row % 10000 == 0) {
/*
+ // this is problematic unless we're going to calculate rowCount first
if (row < rowCount) {
int pct = (100 * row) / rowCount;
if (pct != prev) { // also prevents "0%" from showing up
@@ -410,11 +457,16 @@ protected void parseBasic(BufferedReader reader,
// }
+ /*
protected void parseAwfulCSV(BufferedReader reader,
boolean header) throws IOException {
char[] c = new char[100];
int count = 0;
boolean insideQuote = false;
+
+ int alloc = 100;
+ setRowCount(100);
+
int row = 0;
int col = 0;
int ch;
@@ -460,14 +512,23 @@ protected void parseAwfulCSV(BufferedReader reader,
}
setString(row, col, new String(c, 0, count));
count = 0;
- if (row == 0 && header) {
+ row++;
+ if (row == 1 && header) {
// Use internal row removal (efficient because only one row).
removeTitleRow();
// Un-set the header variable so that next time around, we don't
// just get stuck into a loop, removing the 0th row repeatedly.
header = false;
+ // Reset the number of rows (removeTitleRow() won't reset our local 'row' counter)
+ row = 0;
+ }
+// if (row % 1000 == 0) {
+// PApplet.println(PApplet.nfc(row));
+// }
+ if (row == alloc) {
+ alloc *= 2;
+ setRowCount(alloc);
}
- row++;
col = 0;
} else if (ch == ',') {
@@ -489,73 +550,276 @@ protected void parseAwfulCSV(BufferedReader reader,
if (count > 0) {
setString(row, col, new String(c, 0, count));
}
+ row++; // set row to row count (the current row index + 1)
+ if (alloc != row) {
+ setRowCount(row); // shrink to the actual size
+ }
}
+ */
- /**
- * Parse a line of text as comma-separated values, returning each value as
- * one entry in an array of String objects. Remove quotes from entries that
- * begin and end with them, and convert 'escaped' quotes to actual quotes.
- * @param line line of text to be parsed
- * @return an array of the individual values formerly separated by commas
- */
- static protected String[] splitLineCSV(String line) {
- char[] c = line.toCharArray();
- int rough = 1; // at least one
- boolean quote = false;
- for (int i = 0; i < c.length; i++) {
- if (!quote && (c[i] == ',')) {
- rough++;
- } else if (c[i] == '\"') {
- quote = !quote;
+ static class CommaSeparatedLine {
+ char[] c;
+ String[] pieces;
+ int pieceCount;
+
+// int offset;
+ int start; //, stop;
+
+ String[] handle(String line, BufferedReader reader) throws IOException {
+// PApplet.println("handle() called for: " + line);
+ start = 0;
+ pieceCount = 0;
+ c = line.toCharArray();
+
+ // get tally of number of columns and allocate the array
+ int cols = 1; // the first comma indicates the second column
+ boolean quote = false;
+ for (int i = 0; i < c.length; i++) {
+ if (!quote && (c[i] == ',')) {
+ cols++;
+ } else if (c[i] == '\"') {
+ // double double quotes (escaped quotes like "") will simply toggle
+ // this back and forth, so it should remain accurate
+ quote = !quote;
+ }
+ }
+ pieces = new String[cols];
+
+// while (offset < c.length) {
+// start = offset;
+ while (start < c.length) {
+ boolean enough = ingest();
+ while (!enough) {
+ // found a newline inside the quote, grab another line
+ String nextLine = reader.readLine();
+// System.out.println("extending to " + nextLine);
+ if (nextLine == null) {
+// System.err.println(line);
+ throw new IOException("Found a quoted line that wasn't terminated properly.");
+ }
+ // for simplicity, not bothering to skip what's already been read
+ // from c (and reset the offset to 0), opting to make a bigger array
+ // with both lines.
+ char[] temp = new char[c.length + 1 + nextLine.length()];
+ PApplet.arrayCopy(c, temp, c.length);
+ // NOTE: we're converting to \n here, which isn't perfect
+ temp[c.length] = '\n';
+ nextLine.getChars(0, nextLine.length(), temp, c.length + 1);
+// c = temp;
+ return handle(new String(temp), reader);
+ //System.out.println(" full line is now " + new String(c));
+ //stop = nextComma(c, offset);
+ //System.out.println("stop is now " + stop);
+ //enough = ingest();
+ }
+ }
+
+ // Make any remaining entries blanks instead of nulls. Empty columns from
+ // CSV are always "" not null, so this handles successive commas in a line
+ for (int i = pieceCount; i < pieces.length; i++) {
+ pieces[i] = "";
+ }
+// PApplet.printArray(pieces);
+ return pieces;
+ }
+
+ protected void addPiece(int start, int stop, boolean quotes) {
+ if (quotes) {
+ int dest = start;
+ for (int i = start; i < stop; i++) {
+ if (c[i] == '\"') {
+ ++i; // step over the quote
+ }
+ if (i != dest) {
+ c[dest] = c[i];
+ }
+ dest++;
+ }
+ pieces[pieceCount++] = new String(c, start, dest - start);
+
+ } else {
+ pieces[pieceCount++] = new String(c, start, stop - start);
}
}
- String[] pieces = new String[rough];
- int pieceCount = 0;
- int offset = 0;
- while (offset < c.length) {
- int start = offset;
- int stop = nextComma(c, offset);
- offset = stop + 1; // next time around, need to step over the comment
- if (c[start] == '\"' && c[stop-1] == '\"') {
- start++;
- stop--;
+
+ /**
+ * Returns the next comma (not inside a quote) in the specified array.
+ * @param c array to search
+ * @param index offset at which to start looking
+ * @return index of the comma, or -1 if line ended inside an unclosed quote
+ */
+ protected boolean ingest() {
+ boolean hasEscapedQuotes = false;
+ // not possible
+// if (index == c.length) { // we're already at the end
+// return c.length;
+// }
+ boolean quoted = c[start] == '\"';
+ if (quoted) {
+ start++; // step over the quote
}
int i = start;
- int ii = start;
- while (i < stop) {
+ while (i < c.length) {
+// PApplet.println(c[i] + " i=" + i);
if (c[i] == '\"') {
- i++; // skip over pairs of double quotes become one
- }
- if (i != ii) {
- c[ii] = c[i];
+ // if this fella started with a quote
+ if (quoted) {
+ if (i == c.length-1) {
+ // closing quote for field; last field on the line
+ addPiece(start, i, hasEscapedQuotes);
+ start = c.length;
+ return true;
+
+ } else if (c[i+1] == '\"') {
+ // an escaped quote inside a quoted field, step over it
+ hasEscapedQuotes = true;
+ i += 2;
+
+ } else if (c[i+1] == ',') {
+ // that was our closing quote, get outta here
+ addPiece(start, i, hasEscapedQuotes);
+ start = i+2;
+ return true;
+
+ } else {
+ // This is a lone-wolf quote, occasionally seen in exports.
+ // It's a single quote in the middle of some other text,
+ // and not escaped properly. Pray for the best!
+ i++;
+ }
+
+ } else { // not a quoted line
+ if (i == c.length-1) {
+ // we're at the end of the line, can't have an unescaped quote
+ throw new RuntimeException("Unterminated quote at end of line");
+
+ } else if (c[i+1] == '\"') {
+ // step over this crummy quote escape
+ hasEscapedQuotes = true;
+ i += 2;
+
+ } else {
+ throw new RuntimeException("Unterminated quoted field mid-line");
+ }
+ }
+ } else if (!quoted && c[i] == ',') {
+ addPiece(start, i, hasEscapedQuotes);
+ start = i+1;
+ return true;
+
+ } else if (!quoted && i == c.length-1) {
+ addPiece(start, c.length, hasEscapedQuotes);
+ start = c.length;
+ return true;
+
+ } else { // nothing all that interesting
+ i++;
}
- i++;
- ii++;
}
- String s = new String(c, start, ii - start);
- pieces[pieceCount++] = s;
+// if (!quote && (c[i] == ',')) {
+// // found a comma, return this location
+// return i;
+// } else if (c[i] == '\"') {
+// // if it's a quote, then either the next char is another quote,
+// // or if this is a quoted entry, it better be a comma
+// quote = !quote;
+// }
+// }
+
+ // if still inside a quote, indicate that another line should be read
+ if (quoted) {
+ return false;
+ }
+
+// // made it to the end of the array with no new comma
+// return c.length;
+
+ throw new RuntimeException("not sure how...");
}
- // make any remaining entries blanks instead of nulls
- for (int i = pieceCount; i < pieces.length; i++) {
- pieces[i] = "";
+ }
+
+ CommaSeparatedLine csl;
+
+ /**
+ * Parse a line of text as comma-separated values, returning each value as
+ * one entry in an array of String objects. Remove quotes from entries that
+ * begin and end with them, and convert 'escaped' quotes to actual quotes.
+ * @param line line of text to be parsed
+ * @return an array of the individual values formerly separated by commas
+ */
+ protected String[] splitLineCSV(String line, BufferedReader reader) throws IOException {
+ if (csl == null) {
+ csl = new CommaSeparatedLine();
}
- return pieces;
+ return csl.handle(line, reader);
}
+ /**
+ * Returns the next comma (not inside a quote) in the specified array.
+ * @param c array to search
+ * @param index offset at which to start looking
+ * @return index of the comma, or -1 if line ended inside an unclosed quote
+ */
+ /*
static protected int nextComma(char[] c, int index) {
- boolean quote = false;
+ if (index == c.length) { // we're already at the end
+ return c.length;
+ }
+ boolean quoted = c[index] == '\"';
+ if (quoted) {
+ index++; // step over the quote
+ }
for (int i = index; i < c.length; i++) {
+ if (c[i] == '\"') {
+ // if this fella started with a quote
+ if (quoted) {
+ if (i == c.length-1) {
+ //return -1; // ran out of chars
+ // closing quote for field; last field on the line
+ return c.length;
+ } else if (c[i+1] == '\"') {
+ // an escaped quote inside a quoted field, step over it
+ i++;
+ } else if (c[i+1] == ',') {
+ // that's our closing quote, get outta here
+ return i+1;
+ }
+
+ } else { // not a quoted line
+ if (i == c.length-1) {
+ // we're at the end of the line, can't have an unescaped quote
+ //return -1; // ran out of chars
+ throw new RuntimeException("Unterminated quoted field at end of line");
+ } else if (c[i+1] == '\"') {
+ // step over this crummy quote escape
+ ++i;
+ } else {
+ throw new RuntimeException("Unterminated quoted field mid-line");
+ }
+ }
+ } else if (!quoted && c[i] == ',') {
+ return i;
+ }
if (!quote && (c[i] == ',')) {
+ // found a comma, return this location
return i;
} else if (c[i] == '\"') {
+ // if it's a quote, then either the next char is another quote,
+ // or if this is a quoted entry, it better be a comma
quote = !quote;
}
}
+ // if still inside a quote, indicate that another line should be read
+ if (quote) {
+ return -1;
+ }
+ // made it to the end of the array with no new comma
return c.length;
}
+ */
/**
@@ -578,7 +842,7 @@ private InputStream odsFindContentXML(InputStream input) {
}
- protected void odsParse(InputStream input, String worksheet) {
+ protected void odsParse(InputStream input, String worksheet, boolean header) {
try {
InputStream contentStream = odsFindContentXML(input);
XML xml = new XML(contentStream);
@@ -594,7 +858,7 @@ protected void odsParse(InputStream input, String worksheet) {
for (XML sheet : sheets) {
// System.out.println(sheet.getAttribute("table:name"));
if (worksheet == null || worksheet.equals(sheet.getString("table:name"))) {
- odsParseSheet(sheet);
+ odsParseSheet(sheet, header);
found = true;
if (worksheet == null) {
break; // only read the first sheet
@@ -625,7 +889,7 @@ protected void odsParse(InputStream input, String worksheet) {
* Parses a single sheet of XML from this file.
* @param The XML object for a single worksheet from the ODS file
*/
- private void odsParseSheet(XML sheet) {
+ private void odsParseSheet(XML sheet, boolean header) {
// Extra or tags inside the text tag for the cell will be stripped.
// Different from showing formulas, and not quite the same as 'save as
// displayed' option when saving from inside OpenOffice. Only time we
@@ -674,7 +938,7 @@ private void odsParseSheet(XML sheet) {
cellData = textpContent; // nothing fancy, the text is in the text:p element
} else {
XML[] textpKids = textp.getChildren();
- StringBuffer cellBuffer = new StringBuffer();
+ StringBuilder cellBuffer = new StringBuilder();
for (XML kid : textpKids) {
String kidName = kid.getName();
if (kidName == null) {
@@ -722,18 +986,24 @@ private void odsParseSheet(XML sheet) {
}
}
}
- if (rowNotNull && rowRepeat > 1) {
- String[] rowStrings = getStringRow(rowIndex);
- for (int r = 1; r < rowRepeat; r++) {
- addRow(rowStrings);
+ if (header) {
+ removeTitleRow(); // efficient enough on the first row
+ header = false; // avoid infinite loop
+
+ } else {
+ if (rowNotNull && rowRepeat > 1) {
+ String[] rowStrings = getStringRow(rowIndex);
+ for (int r = 1; r < rowRepeat; r++) {
+ addRow(rowStrings);
+ }
}
+ rowIndex += rowRepeat;
}
- rowIndex += rowRepeat;
}
}
- private void odsAppendNotNull(XML kid, StringBuffer buffer) {
+ private void odsAppendNotNull(XML kid, StringBuilder buffer) {
String content = kid.getContent();
if (content != null) {
buffer.append(content);
@@ -807,7 +1077,7 @@ public void parseInto(Object enclosingObject, String fieldName) {
}
Field[] fields = target.getDeclaredFields();
- ArrayList inuse = new ArrayList();
+ ArrayList inuse = new ArrayList<>();
for (Field field : fields) {
String name = field.getName();
if (getColumnIndex(name, false) != -1) {
@@ -915,7 +1185,7 @@ public boolean save(OutputStream output, String options) {
throw new IllegalArgumentException("No extension specified for saving this Table");
}
- String[] opts = PApplet.splitTokens(options, ", ");
+ String[] opts = PApplet.trim(PApplet.split(options, ','));
// Only option for save is the extension, so we can safely grab the last
extension = opts[opts.length - 1];
boolean found = false;
@@ -934,6 +1204,13 @@ public boolean save(OutputStream output, String options) {
writeCSV(writer);
} else if (extension.equals("tsv")) {
writeTSV(writer);
+ } else if (extension.equals("ods")) {
+ try {
+ saveODS(output);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
} else if (extension.equals("html")) {
writeHTML(writer);
} else if (extension.equals("bin")) {
@@ -982,12 +1259,18 @@ protected void writeTSV(PrintWriter writer) {
protected void writeCSV(PrintWriter writer) {
if (columnTitles != null) {
- for (int col = 0; col < columns.length; col++) {
+ for (int col = 0; col < getColumnCount(); col++) {
if (col != 0) {
writer.print(',');
}
- if (columnTitles[col] != null) {
- writeEntryCSV(writer, columnTitles[col]);
+ try {
+ if (columnTitles[col] != null) { // col < columnTitles.length &&
+ writeEntryCSV(writer, columnTitles[col]);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ PApplet.printArray(columnTitles);
+ PApplet.printArray(columns);
+ throw e;
}
}
writer.println();
@@ -1050,28 +1333,51 @@ protected void writeEntryCSV(PrintWriter writer, String entry) {
protected void writeHTML(PrintWriter writer) {
- writer.println("");
+ writer.println("");
+// writer.println("");
+// writer.println(" ");
+ writer.println("");
writer.println("");
writer.println(" ");
writer.println("");
writer.println("");
writer.println(" ");
+
+ if (hasColumnTitles()) {
+ writer.println(" ");
+ for (String entry : getColumnTitles()) {
+ writer.print(" ");
+ if (entry != null) {
+ writeEntryHTML(writer, entry);
+ }
+ writer.println(" ");
+ }
+ writer.println(" ");
+ }
+
for (int row = 0; row < getRowCount(); row++) {
writer.println(" ");
for (int col = 0; col < getColumnCount(); col++) {
String entry = getString(row, col);
writer.print(" ");
- writeEntryHTML(writer, entry);
- writer.println(" ");
+ if (entry != null) {
+ // probably not a great idea to mess w/ the export
+// if (entry.startsWith("<") && entry.endsWith(">")) {
+// writer.print(entry);
+// } else {
+ writeEntryHTML(writer, entry);
+// }
+ }
+ writer.println("");
}
writer.println(" ");
}
writer.println("
");
writer.println("");
- writer.println("");
+ writer.println("");
writer.flush();
}
@@ -1085,16 +1391,15 @@ protected void writeEntryHTML(PrintWriter writer, String entry) {
writer.print(">");
} else if (c == '&') {
writer.print("&");
- } else if (c == '\'') {
- writer.print("'");
+// } else if (c == '\'') { // only in XML
+// writer.print("'");
} else if (c == '"') {
writer.print(""");
- // not necessary with UTF-8?
-// } else if (c < 32 || c > 127) {
-// writer.print("");
-// writer.print((int) c);
-// writer.print(';');
+ } else if (c < 32 || c > 127) { // keep in ASCII or Tidy complains
+ writer.print("");
+ writer.print((int) c);
+ writer.print(';');
} else {
writer.print(c);
@@ -1103,6 +1408,207 @@ protected void writeEntryHTML(PrintWriter writer, String entry) {
}
+ protected void saveODS(OutputStream os) throws IOException {
+ ZipOutputStream zos = new ZipOutputStream(os);
+
+ final String xmlHeader = "";
+
+ ZipEntry entry = new ZipEntry("META-INF/manifest.xml");
+ String[] lines = new String[] {
+ xmlHeader,
+ "",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ };
+ zos.putNextEntry(entry);
+ zos.write(PApplet.join(lines, "\n").getBytes());
+ zos.closeEntry();
+
+ /*
+ entry = new ZipEntry("meta.xml");
+ lines = new String[] {
+ xmlHeader,
+ " "
+ };
+ zos.putNextEntry(entry);
+ zos.write(PApplet.join(lines, "\n").getBytes());
+ zos.closeEntry();
+
+ entry = new ZipEntry("meta.xml");
+ lines = new String[] {
+ xmlHeader,
+ " "
+ };
+ zos.putNextEntry(entry);
+ zos.write(PApplet.join(lines, "\n").getBytes());
+ zos.closeEntry();
+
+ entry = new ZipEntry("settings.xml");
+ lines = new String[] {
+ xmlHeader,
+ " "
+ };
+ zos.putNextEntry(entry);
+ zos.write(PApplet.join(lines, "\n").getBytes());
+ zos.closeEntry();
+
+ entry = new ZipEntry("styles.xml");
+ lines = new String[] {
+ xmlHeader,
+ " "
+ };
+ zos.putNextEntry(entry);
+ zos.write(PApplet.join(lines, "\n").getBytes());
+ zos.closeEntry();
+ */
+
+ final String[] dummyFiles = new String[] {
+ "meta.xml", "settings.xml", "styles.xml"
+ };
+ lines = new String[] {
+ xmlHeader,
+ " "
+ };
+ byte[] dummyBytes = PApplet.join(lines, "\n").getBytes();
+ for (String filename : dummyFiles) {
+ entry = new ZipEntry(filename);
+ zos.putNextEntry(entry);
+ zos.write(dummyBytes);
+ zos.closeEntry();
+ }
+
+ //
+
+ entry = new ZipEntry("mimetype");
+ zos.putNextEntry(entry);
+ zos.write("application/vnd.oasis.opendocument.spreadsheet".getBytes());
+ zos.closeEntry();
+
+ //
+
+ entry = new ZipEntry("content.xml");
+ zos.putNextEntry(entry);
+ //lines = new String[] {
+ writeUTF(zos, new String[] {
+ xmlHeader,
+ "",
+ " ",
+ " ",
+ " "
+ });
+ //zos.write(PApplet.join(lines, "\n").getBytes());
+
+ byte[] rowStart = " \n".getBytes();
+ byte[] rowStop = " \n".getBytes();
+
+ if (hasColumnTitles()) {
+ zos.write(rowStart);
+ for (int i = 0; i < getColumnCount(); i++) {
+ saveStringODS(zos, columnTitles[i]);
+ }
+ zos.write(rowStop);
+ }
+
+ for (TableRow row : rows()) {
+ zos.write(rowStart);
+ for (int i = 0; i < getColumnCount(); i++) {
+ if (columnTypes[i] == STRING || columnTypes[i] == CATEGORY) {
+ saveStringODS(zos, row.getString(i));
+ } else {
+ saveNumberODS(zos, row.getString(i));
+ }
+ }
+ zos.write(rowStop);
+ }
+
+ //lines = new String[] {
+ writeUTF(zos, new String[] {
+ " ",
+ " ",
+ " ",
+ " "
+ });
+ //zos.write(PApplet.join(lines, "\n").getBytes());
+ zos.closeEntry();
+
+ zos.flush();
+ zos.close();
+ }
+
+
+ void saveStringODS(OutputStream output, String text) throws IOException {
+ // At this point, I should have just used the XML library. But this does
+ // save us from having to create the entire document in memory again before
+ // writing to the file. So while it's dorky, the outcome is still useful.
+ StringBuilder sanitized = new StringBuilder();
+ if (text != null) {
+ char[] array = text.toCharArray();
+ for (char c : array) {
+ if (c == '&') {
+ sanitized.append("&");
+ } else if (c == '\'') {
+ sanitized.append("'");
+ } else if (c == '"') {
+ sanitized.append(""");
+ } else if (c == '<') {
+ sanitized.append("<");
+ } else if (c == '>') {
+ sanitized.append("&rt;");
+ } else if (c < 32 || c > 127) {
+ sanitized.append("" + ((int) c) + ";");
+ } else {
+ sanitized.append(c);
+ }
+ }
+ }
+
+ writeUTF(output,
+ " ",
+ " " + sanitized + " ",
+ " ");
+ }
+
+
+ void saveNumberODS(OutputStream output, String text) throws IOException {
+ writeUTF(output,
+ " ",
+ " " + text + " ",
+ " ");
+ }
+
+
+ static Charset utf8;
+
+ static void writeUTF(OutputStream output, String... lines) throws IOException {
+ if (utf8 == null) {
+ utf8 = Charset.forName("UTF-8");
+ }
+ for (String str : lines) {
+ output.write(str.getBytes(utf8));
+ output.write('\n');
+ }
+ }
+
+
protected void saveBinary(OutputStream os) throws IOException {
DataOutputStream output = new DataOutputStream(new BufferedOutputStream(os));
output.writeInt(0x9007AB1E); // version
@@ -1163,7 +1669,12 @@ protected void saveBinary(OutputStream os) throws IOException {
output.writeDouble(row.getDouble(col));
break;
case CATEGORY:
- output.writeInt(columnCategories[col].index(row.getString(col)));
+ String peace = row.getString(col);
+ if (peace.equals(missingString)) {
+ output.writeInt(missingCategory);
+ } else {
+ output.writeInt(columnCategories[col].index(peace));
+ }
break;
}
}
@@ -1295,7 +1806,7 @@ public void addColumn(String title) {
/**
- * @param type the type to be used for the new column: INT, LONG, FLOAT, DOUBLE, STRING, or CATEGORY
+ * @param type the type to be used for the new column: INT, LONG, FLOAT, DOUBLE, or STRING
*/
public void addColumn(String title, int type) {
insertColumn(columns.length, title, type);
@@ -1394,6 +1905,7 @@ public void removeColumn(int column) {
}
}
+
/**
* @webref table:method
* @brief Gets the number of columns in a table
@@ -1434,14 +1946,10 @@ public void setColumnType(String columnName, String columnType) {
}
- /**
- * Set the data type for a column so that using it is more efficient.
- * @param column the column to change
- * @param columnType One of int, long, float, double, or String.
- */
- public void setColumnType(int column, String columnType) {
+ static int parseColumnType(String columnType) {
+ columnType = columnType.toLowerCase();
int type = -1;
- if (columnType.equals("String")) {
+ if (columnType.equals("string")) {
type = STRING;
} else if (columnType.equals("int")) {
type = INT;
@@ -1456,7 +1964,17 @@ public void setColumnType(int column, String columnType) {
} else {
throw new IllegalArgumentException("'" + columnType + "' is not a valid column type.");
}
- setColumnType(column, type);
+ return type;
+ }
+
+
+ /**
+ * Set the data type for a column so that using it is more efficient.
+ * @param column the column to change
+ * @param columnType One of int, long, float, double, string, or category.
+ */
+ public void setColumnType(int column, String columnType) {
+ setColumnType(column, parseColumnType(columnType));
}
@@ -1477,7 +1995,7 @@ public void setColumnType(int column, int newType) {
int[] intData = new int[rowCount];
for (int row = 0; row < rowCount; row++) {
String s = getString(row, column);
- intData[row] = PApplet.parseInt(s, missingInt);
+ intData[row] = (s == null) ? missingInt : PApplet.parseInt(s, missingInt);
}
columns[column] = intData;
break;
@@ -1487,7 +2005,7 @@ public void setColumnType(int column, int newType) {
for (int row = 0; row < rowCount; row++) {
String s = getString(row, column);
try {
- longData[row] = Long.parseLong(s);
+ longData[row] = (s == null) ? missingLong : Long.parseLong(s);
} catch (NumberFormatException nfe) {
longData[row] = missingLong;
}
@@ -1499,7 +2017,7 @@ public void setColumnType(int column, int newType) {
float[] floatData = new float[rowCount];
for (int row = 0; row < rowCount; row++) {
String s = getString(row, column);
- floatData[row] = PApplet.parseFloat(s, missingFloat);
+ floatData[row] = (s == null) ? missingFloat : PApplet.parseFloat(s, missingFloat);
}
columns[column] = floatData;
break;
@@ -1509,7 +2027,7 @@ public void setColumnType(int column, int newType) {
for (int row = 0; row < rowCount; row++) {
String s = getString(row, column);
try {
- doubleData[row] = Double.parseDouble(s);
+ doubleData[row] = (s == null) ? missingDouble : Double.parseDouble(s);
} catch (NumberFormatException nfe) {
doubleData[row] = missingDouble;
}
@@ -1558,6 +2076,7 @@ public void setTableType(String type) {
public void setColumnTypes(int[] types) {
+ ensureColumn(types.length - 1);
for (int col = 0; col < types.length; col++) {
setColumnType(col, types[col]);
}
@@ -1573,6 +2092,7 @@ public void setColumnTypes(int[] types) {
* @param dictionary
*/
public void setColumnTypes(final Table dictionary) {
+ ensureColumn(dictionary.getRowCount() - 1);
int titleCol = 0;
int typeCol = 1;
if (dictionary.hasColumnTitles()) {
@@ -1696,7 +2216,7 @@ protected int getColumnIndex(String name, boolean report) {
// only create this on first get(). subsequent calls to set the title will
// also update this array, but only if it exists.
if (columnIndices == null) {
- columnIndices = new HashMap();
+ columnIndices = new HashMap<>();
for (int col = 0; col < columns.length; col++) {
columnIndices.put(columnTitles[col], col);
}
@@ -1809,13 +2329,16 @@ public TableRow addRow() {
* @param source a reference to the original row to be duplicated
*/
public TableRow addRow(TableRow source) {
- int row = rowCount;
+ return setRow(rowCount, source);
+ }
+
+
+ public TableRow setRow(int row, TableRow source) {
// Make sure there are enough columns to add this data
ensureBounds(row, source.getColumnCount() - 1);
- for (int col = 0; col < columns.length; col++) {
+ for (int col = 0; col < Math.min(source.getColumnCount(), columns.length); col++) {
switch (columnTypes[col]) {
- case CATEGORY:
case INT:
setInt(row, col, source.getInt(col));
break;
@@ -1831,6 +2354,14 @@ public TableRow addRow(TableRow source) {
case STRING:
setString(row, col, source.getString(col));
break;
+ case CATEGORY:
+ int index = source.getInt(col);
+ setInt(row, col, index);
+ if (!columnCategories[col].hasCategory(index)) {
+ columnCategories[col].setCategory(index, source.getString(col));
+ }
+ break;
+
default:
throw new RuntimeException("no types");
}
@@ -1848,6 +2379,15 @@ public TableRow addRow(Object[] columnData) {
}
+ public void addRows(Table source) {
+ int index = getRowCount();
+ setRowCount(index + source.getRowCount());
+ for (TableRow row : source.rows()) {
+ setRow(index++, row);
+ }
+ }
+
+
public void insertRow(int insert, Object[] columnData) {
for (int col = 0; col < columns.length; col++) {
switch (columnTypes[col]) {
@@ -1855,44 +2395,47 @@ public void insertRow(int insert, Object[] columnData) {
case INT: {
int[] intTemp = new int[rowCount+1];
System.arraycopy(columns[col], 0, intTemp, 0, insert);
- System.arraycopy(columns[col], insert, intTemp, insert+1, (rowCount - insert) + 1);
+ System.arraycopy(columns[col], insert, intTemp, insert+1, rowCount - insert);
columns[col] = intTemp;
break;
}
case LONG: {
long[] longTemp = new long[rowCount+1];
System.arraycopy(columns[col], 0, longTemp, 0, insert);
- System.arraycopy(columns[col], insert, longTemp, insert+1, (rowCount - insert) + 1);
+ System.arraycopy(columns[col], insert, longTemp, insert+1, rowCount - insert);
columns[col] = longTemp;
break;
}
case FLOAT: {
float[] floatTemp = new float[rowCount+1];
System.arraycopy(columns[col], 0, floatTemp, 0, insert);
- System.arraycopy(columns[col], insert, floatTemp, insert+1, (rowCount - insert) + 1);
+ System.arraycopy(columns[col], insert, floatTemp, insert+1, rowCount - insert);
columns[col] = floatTemp;
break;
}
case DOUBLE: {
double[] doubleTemp = new double[rowCount+1];
System.arraycopy(columns[col], 0, doubleTemp, 0, insert);
- System.arraycopy(columns[col], insert, doubleTemp, insert+1, (rowCount - insert) + 1);
+ System.arraycopy(columns[col], insert, doubleTemp, insert+1, rowCount - insert);
columns[col] = doubleTemp;
break;
}
case STRING: {
String[] stringTemp = new String[rowCount+1];
System.arraycopy(columns[col], 0, stringTemp, 0, insert);
- System.arraycopy(columns[col], insert, stringTemp, insert+1, (rowCount - insert) + 1);
+ System.arraycopy(columns[col], insert, stringTemp, insert+1, rowCount - insert);
columns[col] = stringTemp;
break;
}
}
}
+ // Need to increment before setRow(), because it calls ensureBounds()
+ // https://github.com/processing/processing/issues/5406
+ ++rowCount;
setRow(insert, columnData);
- rowCount++;
}
+
/**
* @webref table:method
* @brief Removes a row from a table
@@ -2083,7 +2626,12 @@ protected void setRowCol(int row, int col, Object piece) {
if (piece == null) {
indexData[row] = missingCategory;
} else {
- indexData[row] = columnCategories[col].index(String.valueOf(piece));
+ String peace = String.valueOf(piece);
+ if (peace.equals(missingString)) { // missingString might be null
+ indexData[row] = missingCategory;
+ } else {
+ indexData[row] = columnCategories[col].index(peace);
+ }
}
break;
default:
@@ -2258,6 +2806,27 @@ public int getColumnType(int column) {
public int[] getColumnTypes() {
return table.getColumnTypes();
}
+
+ public String getColumnTitle(int column) {
+ return table.getColumnTitle(column);
+ }
+
+ public String[] getColumnTitles() {
+ return table.getColumnTitles();
+ }
+
+ public void print() {
+ write(new PrintWriter(System.out));
+ }
+
+ public void write(PrintWriter writer) {
+ for (int i = 0 ; i < getColumnCount(); i++) {
+ if (i != 0) {
+ writer.print('\t');
+ }
+ writer.print(getString(i));
+ }
+ }
}
@@ -2916,11 +3485,19 @@ public String getString(int row, int column) {
return missingString;
}
return columnCategories[column].key(cat);
- } else {
- return String.valueOf(Array.get(columns[column], row));
+ } else if (columnTypes[column] == FLOAT) {
+ if (Float.isNaN(getFloat(row, column))) {
+ return null;
+ }
+ } else if (columnTypes[column] == DOUBLE) {
+ if (Double.isNaN(getFloat(row, column))) {
+ return null;
+ }
}
+ return String.valueOf(Array.get(columns[column], row));
}
+
/**
* @param columnName title of the column to reference
*/
@@ -2929,6 +3506,9 @@ public String getString(int row, String columnName) {
}
+ /**
+ * Treat entries with this string as "missing". Also used for categorial.
+ */
public void setMissingString(String value) {
missingString = value;
}
@@ -3221,7 +3801,7 @@ public int matchRowIndex(String what, String columnName) {
/**
* Return a list of rows that contain the String passed in. If there are no
* matches, a zero length array will be returned (not a null array).
- * @param what the String to match
+ * @param regexp the String to match
* @param column ID number of the column to search
*/
public int[] matchRowIndices(String regexp, int column) {
@@ -3355,9 +3935,18 @@ public void replace(String orig, String replacement) {
public void replace(String orig, String replacement, int col) {
if (columnTypes[col] == STRING) {
String[] stringData = (String[]) columns[col];
- for (int row = 0; row < rowCount; row++) {
- if (stringData[row].equals(orig)) {
- stringData[row] = replacement;
+
+ if (orig != null) {
+ for (int row = 0; row < rowCount; row++) {
+ if (orig.equals(stringData[row])) {
+ stringData[row] = replacement;
+ }
+ }
+ } else { // null is a special case (and faster anyway)
+ for (int row = 0; row < rowCount; row++) {
+ if (stringData[row] == null) {
+ stringData[row] = replacement;
+ }
}
}
}
@@ -3372,9 +3961,9 @@ public void replace(String orig, String replacement, String colName) {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
- public void replaceAll(String orig, String replacement) {
+ public void replaceAll(String regex, String replacement) {
for (int col = 0; col < columns.length; col++) {
- replaceAll(orig, replacement, col);
+ replaceAll(regex, replacement, col);
}
}
@@ -3397,7 +3986,7 @@ public void replaceAll(String regex, String replacement, int column) {
/**
* Run String.replaceAll() on all entries in a column.
* Only works with columns that are already String values.
- * @param what the String to match
+ * @param regex the String to match
* @param columnName title of the column to search
*/
public void replaceAll(String regex, String replacement, String columnName) {
@@ -3463,16 +4052,77 @@ public void removeTokens(String tokens, String columnName) {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
/**
* @webref table:method
* @brief Trims whitespace from values
* @see Table#removeTokens(String)
*/
public void trim() {
+ columnTitles = PApplet.trim(columnTitles);
for (int col = 0; col < getColumnCount(); col++) {
trim(col);
}
+ // remove empty columns
+ int lastColumn = getColumnCount() - 1;
+ //while (isEmptyColumn(lastColumn) && lastColumn >= 0) {
+ while (isEmptyArray(getStringColumn(lastColumn)) && lastColumn >= 0) {
+ lastColumn--;
+ }
+ setColumnCount(lastColumn + 1);
+
+ // trim() works from both sides
+ while (getColumnCount() > 0 && isEmptyArray(getStringColumn(0))) {
+ removeColumn(0);
+ }
+
+ // remove empty rows (starting from the end)
+ int lastRow = lastRowIndex();
+ //while (isEmptyRow(lastRow) && lastRow >= 0) {
+ while (isEmptyArray(getStringRow(lastRow)) && lastRow >= 0) {
+ lastRow--;
+ }
+ setRowCount(lastRow + 1);
+
+ while (getRowCount() > 0 && isEmptyArray(getStringRow(0))) {
+ removeRow(0);
+ }
+ }
+
+
+ protected boolean isEmptyArray(String[] contents) {
+ for (String entry : contents) {
+ if (entry != null && entry.length() > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /*
+ protected boolean isEmptyColumn(int column) {
+ String[] contents = getStringColumn(column);
+ for (String entry : contents) {
+ if (entry != null && entry.length() > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ protected boolean isEmptyRow(int row) {
+ String[] contents = getStringRow(row);
+ for (String entry : contents) {
+ if (entry != null && entry.length() > 0) {
+ return false;
+ }
+ }
+ return true;
}
+ */
+
/**
* @param column ID number of the column to trim
@@ -3548,9 +4198,9 @@ protected void checkBounds(int row, int column) {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
- class HashMapBlows {
- HashMap dataToIndex = new HashMap();
- ArrayList indexToData = new ArrayList();
+ static class HashMapBlows {
+ HashMap dataToIndex = new HashMap<>();
+ ArrayList indexToData = new ArrayList<>();
HashMapBlows() { }
@@ -3558,6 +4208,7 @@ class HashMapBlows {
read(input);
}
+ /** gets the index, and creates one if it doesn't already exist. */
int index(String key) {
Integer value = dataToIndex.get(key);
if (value != null) {
@@ -3574,6 +4225,18 @@ String key(int index) {
return indexToData.get(index);
}
+ boolean hasCategory(int index) {
+ return index < size() && indexToData.get(index) != null;
+ }
+
+ void setCategory(int index, String name) {
+ while (indexToData.size() <= index) {
+ indexToData.add(null);
+ }
+ indexToData.set(index, name);
+ dataToIndex.put(name, index);
+ }
+
int size() {
return dataToIndex.size();
}
@@ -3595,9 +4258,11 @@ private void writeln(PrintWriter writer) throws IOException {
void read(DataInputStream input) throws IOException {
int count = input.readInt();
- dataToIndex = new HashMap(count);
+ //System.out.println("found " + count + " entries in category map");
+ dataToIndex = new HashMap<>(count);
for (int i = 0; i < count; i++) {
String str = input.readUTF();
+ //System.out.println(i + " " + str);
dataToIndex.put(str, i);
indexToData.add(str);
}
@@ -3629,12 +4294,21 @@ void read(DataInputStream input) throws IOException {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
+ /**
+ * Sorts (orders) a table based on the values in a column.
+ *
+ * @webref table:method
+ * @brief Orders a table based on the values in a column
+ * @param columnName the name of the column to sort
+ * @see Table#trim()
+ */
public void sort(String columnName) {
sort(getColumnIndex(columnName), false);
}
-
+ /**
+ * @param column the column ID, e.g. 0, 1, 2
+ */
public void sort(int column) {
sort(column, false);
}
@@ -3660,7 +4334,7 @@ public int size() {
}
@Override
- public float compare(int index1, int index2) {
+ public int compare(int index1, int index2) {
int a = reverse ? order[index2] : order[index1];
int b = reverse ? order[index1] : order[index2];
@@ -3668,13 +4342,24 @@ public float compare(int index1, int index2) {
case INT:
return getInt(a, column) - getInt(b, column);
case LONG:
- return getLong(a, column) - getLong(b, column);
+ long diffl = getLong(a, column) - getLong(b, column);
+ return diffl == 0 ? 0 : (diffl < 0 ? -1 : 1);
case FLOAT:
- return getFloat(a, column) - getFloat(b, column);
+ float difff = getFloat(a, column) - getFloat(b, column);
+ return difff == 0 ? 0 : (difff < 0 ? -1 : 1);
case DOUBLE:
- return (float) (getDouble(a, column) - getDouble(b, column));
+ double diffd = getDouble(a, column) - getDouble(b, column);
+ return diffd == 0 ? 0 : (diffd < 0 ? -1 : 1);
case STRING:
- return getString(a, column).compareToIgnoreCase(getString(b, column));
+ String string1 = getString(a, column);
+ if (string1 == null) {
+ string1 = ""; // avoid NPE when cells are left empty
+ }
+ String string2 = getString(b, column);
+ if (string2 == null) {
+ string2 = "";
+ }
+ return string1.compareToIgnoreCase(string2);
case CATEGORY:
return getInt(a, column) - getInt(b, column);
default:
@@ -3833,13 +4518,13 @@ public FloatDict getFloatDict(String keyColumnName, String valueColumnName) {
public FloatDict getFloatDict(int keyColumn, int valueColumn) {
return new FloatDict(getStringColumn(keyColumn),
- getFloatColumn(valueColumn));
+ getFloatColumn(valueColumn));
}
public StringDict getStringDict(String keyColumnName, String valueColumnName) {
return new StringDict(getStringColumn(keyColumnName),
- getStringColumn(valueColumnName));
+ getStringColumn(valueColumnName));
}
@@ -3849,6 +4534,39 @@ public StringDict getStringDict(int keyColumn, int valueColumn) {
}
+ public Map getRowMap(String columnName) {
+ int col = getColumnIndex(columnName);
+ return (col == -1) ? null : getRowMap(col);
+ }
+
+
+ /**
+ * Return a mapping that connects the entry from a column back to the row
+ * from which it came. For instance:
+ *
+ * Table t = loadTable("country-data.tsv", "header");
+ * // use the contents of the 'country' column to index the table
+ * Map lookup = t.getRowMap("country");
+ * // get the row that has "us" in the "country" column:
+ * TableRow usRow = lookup.get("us");
+ * // get an entry from the 'population' column
+ * int population = usRow.getInt("population");
+ *
+ */
+ public Map getRowMap(int column) {
+ Map outgoing = new HashMap<>();
+ for (int row = 0; row < getRowCount(); row++) {
+ String id = getString(row, column);
+ outgoing.put(id, new RowPointer(this, row));
+ }
+// for (TableRow row : rows()) {
+// String id = row.getString(column);
+// outgoing.put(id, row);
+// }
+ return outgoing;
+ }
+
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -4045,7 +4763,7 @@ protected void convertBasic(BufferedReader reader, boolean tsv,
int prev = -1;
int row = 0;
while ((line = reader.readLine()) != null) {
- convertRow(output, tsv ? PApplet.split(line, '\t') : splitLineCSV(line));
+ convertRow(output, tsv ? PApplet.split(line, '\t') : splitLineCSV(line, reader));
row++;
if (row % 10000 == 0) {
@@ -4127,7 +4845,12 @@ protected void convertRow(DataOutputStream output, String[] pieces) throws IOExc
}
break;
case CATEGORY:
- output.writeInt(columnCategories[col].index(pieces[col]));
+ String peace = pieces[col];
+ if (peace.equals(missingString)) {
+ output.writeInt(missingCategory);
+ } else {
+ output.writeInt(columnCategories[col].index(peace));
+ }
break;
}
}
@@ -4193,4 +4916,20 @@ private void convertRowCol(DataOutputStream output, int row, int col, String pie
}
}
*/
+
+
+ /** Make a copy of the current table */
+ public Table copy() {
+ return new Table(rows());
+ }
+
+
+ public void write(PrintWriter writer) {
+ writeTSV(writer);
+ }
+
+
+ public void print() {
+ writeTSV(new PrintWriter(System.out));
+ }
}
diff --git a/core/src/processing/data/TableRow.java b/libs/processing-core/src/main/java/processing/data/TableRow.java
similarity index 61%
rename from core/src/processing/data/TableRow.java
rename to libs/processing-core/src/main/java/processing/data/TableRow.java
index 7107ee693..3ac59fe4c 100644
--- a/core/src/processing/data/TableRow.java
+++ b/libs/processing-core/src/main/java/processing/data/TableRow.java
@@ -1,5 +1,7 @@
package processing.data;
+import java.io.PrintWriter;
+
/**
* @webref data:composite
* @see Table
@@ -19,6 +21,7 @@ public interface TableRow {
* @see TableRow#getFloat(int)
*/
public String getString(int column);
+
/**
* @param columnName title of the column to reference
*/
@@ -32,12 +35,24 @@ public interface TableRow {
* @see TableRow#getString(int)
*/
public int getInt(int column);
+
/**
* @param columnName title of the column to reference
*/
public int getInt(String columnName);
+ /**
+ * @brief Get a long value from the specified column
+ * @param column ID number of the column to reference
+ * @see TableRow#getFloat(int)
+ * @see TableRow#getString(int)
+ */
+
public long getLong(int column);
+
+ /**
+ * @param columnName title of the column to reference
+ */
public long getLong(String columnName);
/**
@@ -48,12 +63,23 @@ public interface TableRow {
* @see TableRow#getString(int)
*/
public float getFloat(int column);
+
/**
* @param columnName title of the column to reference
*/
public float getFloat(String columnName);
-
+
+ /**
+ * @brief Get a double value from the specified column
+ * @param column ID number of the column to reference
+ * @see TableRow#getInt(int)
+ * @see TableRow#getString(int)
+ */
public double getDouble(int column);
+
+ /**
+ * @param columnName title of the column to reference
+ */
public double getDouble(String columnName);
/**
@@ -79,12 +105,24 @@ public interface TableRow {
* @see TableRow#setString(int, String)
*/
public void setInt(int column, int value);
+
/**
* @param columnName title of the target column
*/
public void setInt(String columnName, int value);
-
+
+ /**
+ * @brief Store a long value in the specified column
+ * @param column ID number of the target column
+ * @param value value to assign
+ * @see TableRow#setFloat(int, float)
+ * @see TableRow#setString(int, String)
+ */
public void setLong(int column, long value);
+
+ /**
+ * @param columnName title of the target column
+ */
public void setLong(String columnName, long value);
/**
@@ -96,17 +134,65 @@ public interface TableRow {
* @see TableRow#setString(int, String)
*/
public void setFloat(int column, float value);
+
/**
* @param columnName title of the target column
*/
public void setFloat(String columnName, float value);
+ /**
+ * @brief Store a double value in the specified column
+ * @param column ID number of the target column
+ * @param value value to assign
+ * @see TableRow#setFloat(int, float)
+ * @see TableRow#setString(int, String)
+ */
public void setDouble(int column, double value);
+
+ /**
+ * @param columnName title of the target column
+ */
public void setDouble(String columnName, double value);
+ /**
+ * @webref tablerow:method
+ * @brief Get the column count.
+ * @return count of all columns
+ */
public int getColumnCount();
+
+ /**
+ * @brief Get the column type.
+ * @param columnName title of the target column
+ * @return type of the column
+ */
public int getColumnType(String columnName);
+
+ /**
+ * @param column ID number of the target column
+ */
public int getColumnType(int column);
-
+
+ /**
+ * @brief Get the all column types
+ * @return list of all column types
+ */
public int[] getColumnTypes();
+
+ /**
+ * @webref tablerow:method
+ * @brief Get the column title.
+ * @param column ID number of the target column
+ * @return title of the column
+ */
+ public String getColumnTitle(int column);
+
+ /**
+ * @brief Get the all column titles
+ * @return list of all column titles
+ */
+ public String[] getColumnTitles();
+
+ public void write(PrintWriter writer);
+ public void print();
}
diff --git a/core/src/processing/data/XML.java b/libs/processing-core/src/main/java/processing/data/XML.java
similarity index 86%
rename from core/src/processing/data/XML.java
rename to libs/processing-core/src/main/java/processing/data/XML.java
index 28023b768..f2c2e32cc 100644
--- a/core/src/processing/data/XML.java
+++ b/libs/processing-core/src/main/java/processing/data/XML.java
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012 The Processing Foundation
+ Copyright (c) 2012-21 The Processing Foundation
Copyright (c) 2009-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
@@ -33,6 +33,9 @@
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathFactory;
import processing.core.PApplet;
@@ -42,7 +45,6 @@
* representing a single node of an XML tree.
*
* @webref data:composite
- * @see PApplet#createXML(String)
* @see PApplet#loadXML(String)
* @see PApplet#parseXML(String)
* @see PApplet#saveXML(XML, String)
@@ -82,7 +84,9 @@ protected XML() { }
/**
- * Advanced users only; see loadXML() in PApplet.
+ * Advanced users only; use loadXML() in PApplet. This is not a supported
+ * function and is subject to change. It is available simply for users that
+ * would like to handle the exceptions in a particular way.
*
* @nowebref
*/
@@ -92,7 +96,7 @@ public XML(File file) throws IOException, ParserConfigurationException, SAXExcep
/**
- * Advanced users only; see loadXML() in PApplet.
+ * Advanced users only; use loadXML() in PApplet.
*
* @nowebref
*/
@@ -109,19 +113,31 @@ public XML(InputStream input) throws IOException, ParserConfigurationException,
/**
- * Shouldn't be part of main p5 reference, this is for advanced users.
- * Note that while it doesn't accept anything but UTF-8, this is preserved
- * so that we have some chance of implementing that in the future.
+ * Unlike the loadXML() method in PApplet, this version works with files
+ * that are not in UTF-8 format.
*
* @nowebref
*/
public XML(InputStream input, String options) throws IOException, ParserConfigurationException, SAXException {
- this(PApplet.createReader(input), options);
+ //this(PApplet.createReader(input), options); // won't handle non-UTF8
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+ try {
+ // Prevent 503 errors from www.w3.org
+ factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ } catch (IllegalArgumentException e) {
+ // ignore this; Android doesn't like it
+ }
+
+ factory.setExpandEntityReferences(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(new InputSource(input));
+ node = document.getDocumentElement();
}
/**
- * Advanced users only; see loadXML() in PApplet.
+ * Advanced users only; use loadXML() in PApplet.
*
* @nowebref
*/
@@ -131,11 +147,17 @@ public XML(Reader reader) throws IOException, ParserConfigurationException, SAXE
/**
- * Advanced users only; see loadXML() in PApplet.
+ * Advanced users only; use loadXML() in PApplet.
+ *
+ * Added extra code to handle \u2028 (Unicode NLF), which is sometimes
+ * inserted by web browsers (Safari?) and not distinguishable from a "real"
+ * LF (or CRLF) in some text editors (i.e. TextEdit on OS X). Only doing
+ * this for XML (and not all Reader objects) because LFs are essential.
+ * https://github.com/processing/processing/issues/2100
*
* @nowebref
*/
- public XML(Reader reader, String options) throws IOException, ParserConfigurationException, SAXException {
+ public XML(final Reader reader, String options) throws IOException, ParserConfigurationException, SAXException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Prevent 503 errors from www.w3.org
@@ -164,17 +186,24 @@ public XML(Reader reader, String options) throws IOException, ParserConfiguratio
// builder = new SAXBuilder();
// builder.setValidation(validating);
-// print(dataPath("1broke.html"), System.out);
+ Document document = builder.parse(new InputSource(new Reader() {
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int count = reader.read(cbuf, off, len);
+ for (int i = 0; i < count; i++) {
+ if (cbuf[off+i] == '\u2028') {
+ cbuf[off+i] = '\n';
+ }
+ }
+ return count;
+ }
-// Document document = builder.parse(dataPath("1_alt.html"));
- Document document = builder.parse(new InputSource(reader));
+ @Override
+ public void close() throws IOException {
+ reader.close();
+ }
+ }));
node = document.getDocumentElement();
-// name = node.getNodeName();
-
-// NodeList nodeList = document.getDocumentElement().getChildNodes();
-// for (int i = 0; i < nodeList.getLength(); i++) {
-// }
-// print(createWriter("data/1_alt_reparse.html"), document.getDocumentElement(), 0);
}
@@ -202,7 +231,18 @@ public XML(String name) {
protected XML(XML parent, Node node) {
this.node = node;
this.parent = parent;
-// this.name = node.getNodeName();
+
+ for (String attr : parent.listAttributes()) {
+ if (attr.startsWith("xmlns")) {
+ // Copy namespace attributes to the kids, otherwise this XML
+ // can no longer be printed (or manipulated in most ways).
+ // Only do this when it's an Element, otherwise it's trying to set
+ // attributes on text notes (interstitial content).
+ if (node instanceof Element) {
+ setString(attr, parent.getString(attr));
+ }
+ }
+ }
}
@@ -233,6 +273,11 @@ static public XML parse(String data, String options) throws IOException, ParserC
// }
+ public boolean save(File file) {
+ return save(file, null);
+ }
+
+
public boolean save(File file, String options) {
PrintWriter writer = PApplet.createWriter(file);
boolean result = write(writer);
@@ -408,7 +453,7 @@ public XML getChild(int index) {
* Get a child by its name or path.
*
* @param name element name or path/to/element
- * @return the first matching element
+ * @return the first matching element or null if no match
*/
public XML getChild(String name) {
if (name.length() > 0 && name.charAt(0) == '/') {
@@ -549,6 +594,32 @@ public void removeChild(XML kid) {
children = null; // TODO not efficient
}
+ /**
+ * Removes whitespace nodes.
+ * Those whitespace nodes are required to reconstruct the original XML's spacing and indentation.
+ * If you call this and use saveXML() your original spacing will be gone.
+ *
+ * @nowebref
+ * @brief Removes whitespace nodes
+ */
+ public void trim() {
+ try {
+ XPathFactory xpathFactory = XPathFactory.newInstance();
+ XPathExpression xpathExp =
+ xpathFactory.newXPath().compile("//text()[normalize-space(.) = '']");
+ NodeList emptyTextNodes = (NodeList)
+ xpathExp.evaluate(node, XPathConstants.NODESET);
+
+ // Remove each empty text node from document.
+ for (int i = 0; i < emptyTextNodes.getLength(); i++) {
+ Node emptyTextNode = emptyTextNodes.item(i);
+ emptyTextNode.getParentNode().removeChild(emptyTextNode);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
// /** Remove whitespace nodes. */
// public void trim() {
@@ -670,8 +741,14 @@ public String getString(String name) {
public String getString(String name, String defaultValue) {
- Node attr = node.getAttributes().getNamedItem(name);
- return (attr == null) ? defaultValue : attr.getNodeValue();
+ NamedNodeMap attrs = node.getAttributes();
+ if (attrs != null) {
+ Node attr = attrs.getNamedItem(name);
+ if (attr != null) {
+ return attr.getNodeValue();
+ }
+ }
+ return defaultValue;
}
@@ -1035,10 +1112,18 @@ public String format(int indent) {
String outgoing = stringWriter.toString();
// Add the XML declaration to the top if it's not there already
- if (!outgoing.startsWith(decl)) {
- return decl + sep + outgoing;
- } else {
+ if (outgoing.startsWith(decl)) {
+ int declen = decl.length();
+ int seplen = sep.length();
+ if (outgoing.length() > declen + seplen &&
+ !outgoing.substring(declen, declen + seplen).equals(sep)) {
+ // make sure there's a line break between the XML decl and the code
+ return outgoing.substring(0, decl.length()) +
+ sep + outgoing.substring(decl.length());
+ }
return outgoing;
+ } else {
+ return decl + sep + outgoing;
}
} catch (Exception e) {
@@ -1048,6 +1133,11 @@ public String format(int indent) {
}
+ public void print() {
+ PApplet.println(format(2));
+ }
+
+
/**
* Return the XML document formatted with two spaces for indents.
* Chosen to do this since it's the most common case (e.g. with println()).
diff --git a/core/src/processing/event/Event.java b/libs/processing-core/src/main/java/processing/event/Event.java
similarity index 98%
rename from core/src/processing/event/Event.java
rename to libs/processing-core/src/main/java/processing/event/Event.java
index 32bf175c2..109dbbee7 100644
--- a/core/src/processing/event/Event.java
+++ b/libs/processing-core/src/main/java/processing/event/Event.java
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012 The Processing Foundation
+ Copyright (c) 2012-21 The Processing Foundation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
diff --git a/core/src/processing/event/KeyEvent.java b/libs/processing-core/src/main/java/processing/event/KeyEvent.java
similarity index 74%
rename from core/src/processing/event/KeyEvent.java
rename to libs/processing-core/src/main/java/processing/event/KeyEvent.java
index 10bbba92a..4a9bdf44d 100644
--- a/core/src/processing/event/KeyEvent.java
+++ b/libs/processing-core/src/main/java/processing/event/KeyEvent.java
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012 The Processing Foundation
+ Copyright (c) 2012-21 The Processing Foundation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -31,6 +31,7 @@ public class KeyEvent extends Event {
char key;
int keyCode;
+ boolean isAutoRepeat;
public KeyEvent(Object nativeObject,
long millis, int action, int modifiers,
@@ -42,6 +43,17 @@ public KeyEvent(Object nativeObject,
}
+ public KeyEvent(Object nativeObject,
+ long millis, int action, int modifiers,
+ char key, int keyCode, boolean isAutoRepeat) {
+ super(nativeObject, millis, action, modifiers);
+ this.flavor = KEY;
+ this.key = key;
+ this.keyCode = keyCode;
+ this.isAutoRepeat = isAutoRepeat;
+ }
+
+
public char getKey() {
return key;
}
@@ -50,4 +62,8 @@ public char getKey() {
public int getKeyCode() {
return keyCode;
}
+
+ public boolean isAutoRepeat() {
+ return isAutoRepeat;
+ }
}
\ No newline at end of file
diff --git a/core/src/processing/event/MouseEvent.java b/libs/processing-core/src/main/java/processing/event/MouseEvent.java
similarity index 71%
rename from core/src/processing/event/MouseEvent.java
rename to libs/processing-core/src/main/java/processing/event/MouseEvent.java
index 408ef2e95..012f4e29e 100644
--- a/core/src/processing/event/MouseEvent.java
+++ b/libs/processing-core/src/main/java/processing/event/MouseEvent.java
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012 The Processing Foundation
+ Copyright (c) 2012-21 The Processing Foundation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -36,7 +36,7 @@ public class MouseEvent extends Event {
protected int x, y;
protected int button;
- protected int clickCount;
+ protected int count;
// public MouseEvent(int x, int y) {
@@ -48,13 +48,13 @@ public class MouseEvent extends Event {
public MouseEvent(Object nativeObject,
long millis, int action, int modifiers,
- int x, int y, int button, int clickCount) {
+ int x, int y, int button, int count) {
super(nativeObject, millis, action, modifiers);
this.flavor = MOUSE;
this.x = x;
this.y = y;
this.button = button;
- this.clickCount = clickCount;
+ this.count = count;
}
@@ -79,8 +79,23 @@ public int getButton() {
// }
+ @Deprecated
public int getClickCount() {
- return clickCount;
+ return count;
+ }
+
+
+ /**
+ * Number of clicks for mouse button events, or the number of steps (positive
+ * or negative depending on direction) for a mouse wheel event.
+ * Wheel events follow Java (see here ), so
+ * getAmount() will return "negative values if the mouse wheel was rotated
+ * up or away from the user" and positive values in the other direction.
+ * On Mac OS X, this will be reversed when "natural" scrolling is enabled
+ * in System Preferences &rarr Mouse.
+ */
+ public int getCount() {
+ return count;
}
diff --git a/libs/processing-core/src/main/java/processing/event/TouchEvent.java b/libs/processing-core/src/main/java/processing/event/TouchEvent.java
new file mode 100644
index 000000000..440a7824e
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/event/TouchEvent.java
@@ -0,0 +1,142 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-21 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.event;
+
+/*
+ * IMPORTANT NOTICE: None of the methods and inner classes in TouchEvent are
+ * part of the Processing API. Don't use them! They might be changed or removed
+ * without notice.
+*/
+public class TouchEvent extends Event {
+ static public final int START = 1;
+ static public final int END = 2;
+ static public final int CANCEL = 3;
+ static public final int MOVE = 4;
+
+ protected int action;
+
+ protected int button;
+ protected int numPointers;
+ protected int[] pointerId;
+ protected float[] pointerX;
+ protected float[] pointerY;
+ protected float[] pointerArea;
+ protected float[] pointerPressure;
+
+ public TouchEvent(Object nativeObject, long millis, int action, int modifiers,
+ int button) {
+ super(nativeObject, millis, action, modifiers);
+ this.flavor = TOUCH;
+ this.button = button;
+ }
+
+ public void setNumPointers(int n) {
+ numPointers = n;
+ pointerId = new int[n];
+ pointerX = new float[n];
+ pointerY = new float[n];
+ pointerArea = new float[n];
+ pointerPressure = new float[n];
+ }
+
+
+ public void setPointer(int idx, int id, float x, float y, float a, float p) {
+ pointerId[idx] = id;
+ pointerX[idx] = x;
+ pointerY[idx] = y;
+ pointerArea[idx] = a;
+ pointerPressure[idx] = p;
+ }
+
+
+ public int getNumPointers() {
+ return numPointers;
+ }
+
+
+ public Pointer getPointer(int idx) {
+ Pointer pt = new Pointer();
+ pt.id = pointerId[idx];
+ pt.x = pointerX[idx];
+ pt.y = pointerY[idx];
+ pt.area = pointerArea[idx];
+ pt.pressure = pointerPressure[idx];
+ return pt;
+ }
+
+
+ public int getPointerId(int idx) {
+ return pointerId[idx];
+ }
+
+
+ public float getPointerX(int idx) {
+ return pointerX[idx];
+ }
+
+
+ public float getPointerY(int idx) {
+ return pointerY[idx];
+ }
+
+
+ public float getPointerArea(int idx) {
+ return pointerArea[idx];
+ }
+
+
+ public float getPointerPressure(int idx) {
+ return pointerPressure[idx];
+ }
+
+
+ public int getButton() {
+ return button;
+ }
+
+
+ public Pointer[] getTouches(Pointer[] touches) {
+ if (touches == null || touches.length != numPointers) {
+ touches = new Pointer[numPointers];
+ for (int idx = 0; idx < numPointers; idx++) {
+ touches[idx] = new Pointer();
+ }
+ }
+ for (int idx = 0; idx < numPointers; idx++) {
+ touches[idx].id = pointerId[idx];
+ touches[idx].x = pointerX[idx];
+ touches[idx].y = pointerY[idx];
+ touches[idx].area = pointerArea[idx];
+ touches[idx].pressure = pointerPressure[idx];
+ }
+ return touches;
+ }
+
+
+ public class Pointer {
+ public int id;
+ public float x, y;
+ public float area;
+ public float pressure;
+ }
+}
\ No newline at end of file
diff --git a/core/src/processing/opengl/FontTexture.java b/libs/processing-core/src/main/java/processing/opengl/FontTexture.java
similarity index 84%
rename from core/src/processing/opengl/FontTexture.java
rename to libs/processing-core/src/main/java/processing/opengl/FontTexture.java
index 8c1c66f5d..cc67259bb 100644
--- a/core/src/processing/opengl/FontTexture.java
+++ b/libs/processing-core/src/main/java/processing/opengl/FontTexture.java
@@ -3,12 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-12 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -58,12 +59,10 @@ class FontTexture implements PConstants {
protected int lineHeight;
protected Texture[] textures = null;
protected PImage[] images = null;
- protected int currentTex;
protected int lastTex;
protected TextureInfo[] glyphTexinfos;
protected HashMap texinfoMap;
-
public FontTexture(PGraphicsOpenGL pg, PFont font, boolean is3D) {
pgl = pg.pgl;
this.is3D = is3D;
@@ -86,7 +85,6 @@ protected void dispose() {
protected void initTexture(PGraphicsOpenGL pg, PFont font) {
- currentTex = -1;
lastTex = -1;
int spow = PGL.nextPowerOfTwo(font.getSize());
@@ -117,10 +115,10 @@ public boolean addTexture(PGraphicsOpenGL pg) {
boolean resize;
w = maxSize;
- if (-1 < currentTex && textures[currentTex].glHeight < maxSize) {
+ if (-1 < lastTex && textures[lastTex].glHeight < maxSize) {
// The height of the current texture is less than the maximum, this
// means we can replace it with a larger texture.
- h = PApplet.min(2 * textures[currentTex].glHeight, maxSize);
+ h = PApplet.min(2 * textures[lastTex].glHeight, maxSize);
resize = true;
} else {
h = minSize;
@@ -147,32 +145,31 @@ public boolean addTexture(PGraphicsOpenGL pg) {
textures[0] = tex;
images = new PImage[1];
images[0] = pg.wrapTexture(tex);
- currentTex = 0;
+ lastTex = 0;
} else if (resize) {
// Replacing old smaller texture with larger one.
// But first we must copy the contents of the older
// texture into the new one.
- Texture tex0 = textures[currentTex];
+ Texture tex0 = textures[lastTex];
tex.put(tex0);
- textures[currentTex] = tex;
+ textures[lastTex] = tex;
- pg.setCache(images[currentTex], tex);
- images[currentTex].width = tex.width;
- images[currentTex].height = tex.height;
+ pg.setCache(images[lastTex], tex);
+ images[lastTex].width = tex.width;
+ images[lastTex].height = tex.height;
} else {
// Adding new texture to the list.
- Texture[] tempTex = textures;
- textures = new Texture[textures.length + 1];
- PApplet.arrayCopy(tempTex, textures, tempTex.length);
- textures[tempTex.length] = tex;
- currentTex = textures.length - 1;
-
- PImage[] tempImg = images;
- images = new PImage[textures.length];
- PApplet.arrayCopy(tempImg, images, tempImg.length);
- images[tempImg.length] = pg.wrapTexture(tex);
+ lastTex = textures.length;
+ Texture[] tempTex = new Texture[lastTex + 1];
+ PApplet.arrayCopy(textures, tempTex, textures.length);
+ tempTex[lastTex] = tex;
+ textures = tempTex;
+
+ PImage[] tempImg = new PImage[textures.length];
+ PApplet.arrayCopy(images, tempImg, images.length);
+ tempImg[lastTex] = pg.wrapTexture(tex);
+ images = tempImg;
}
- lastTex = currentTex;
// Make sure that the current texture is bound.
tex.bind();
@@ -182,7 +179,6 @@ public boolean addTexture(PGraphicsOpenGL pg) {
public void begin() {
- setTexture(0);
}
@@ -193,23 +189,8 @@ public void end() {
}
- public void setTexture(int idx) {
- if (0 <= idx && idx < textures.length) {
- currentTex = idx;
- }
- }
-
-
- public PImage getTexture(int idx) {
- if (0 <= idx && idx < images.length) {
- return images[idx];
- }
- return null;
- }
-
-
- public PImage getCurrentTexture() {
- return getTexture(currentTex);
+ public PImage getTexture(TextureInfo info) {
+ return images[info.texIndex];
}
@@ -226,7 +207,7 @@ public void updateGlyphsTexCoords() {
// loop over current glyphs.
for (int i = 0; i < glyphTexinfos.length; i++) {
TextureInfo tinfo = glyphTexinfos[i];
- if (tinfo != null && tinfo.texIndex == currentTex) {
+ if (tinfo != null && tinfo.texIndex == lastTex) {
tinfo.updateUV();
}
}
@@ -258,14 +239,20 @@ public boolean contextIsOutdated() {
}
if (outdated) {
for (int i = 0; i < textures.length; i++) {
- PGraphicsOpenGL.removeTextureObject(textures[i].glName,
- textures[i].context);
- textures[i].glName = 0;
+ textures[i].dispose();
}
}
return outdated;
}
+// public void draw() {
+// Texture tex = textures[lastTex];
+// pgl.drawTexture(tex.glTarget, tex.glName,
+// tex.glWidth, tex.glHeight,
+// 0, 0, tex.glWidth, tex.glHeight);
+// }
+
+
// Adds this glyph to the opengl texture in PFont.
protected void addToTexture(PGraphicsOpenGL pg, int idx, PFont.Glyph glyph) {
// We add one pixel to avoid issues when sampling the font texture at
@@ -309,16 +296,15 @@ protected void addToTexture(PGraphicsOpenGL pg, int idx, PFont.Glyph glyph) {
}
// Is there room for this glyph in the current line?
- if (offsetX + w > textures[currentTex].glWidth) {
+ if (offsetX + w > textures[lastTex].glWidth) {
// No room, go to the next line:
offsetX = 0;
offsetY += lineHeight;
- lineHeight = 0;
}
lineHeight = Math.max(lineHeight, h);
boolean resized = false;
- if (offsetY + lineHeight > textures[currentTex].glHeight) {
+ if (offsetY + lineHeight > textures[lastTex].glHeight) {
// We run out of space in the current texture, so we add a new texture:
resized = addTexture(pg);
if (resized) {
@@ -334,8 +320,7 @@ protected void addToTexture(PGraphicsOpenGL pg, int idx, PFont.Glyph glyph) {
}
}
- TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY,
- w, h, rgba);
+ TextureInfo tinfo = new TextureInfo(lastTex, offsetX, offsetY, w, h, rgba);
offsetX += w;
if (idx == glyphTexinfos.length) {
@@ -378,6 +363,7 @@ class TextureInfo {
void updateUV() {
width = textures[texIndex].glWidth;
height = textures[texIndex].glHeight;
+
u0 = (float)crop[0] / (float)width;
u1 = u0 + (float)crop[2] / (float)width;
v0 = (float)(crop[1] + crop[3]) / (float)height;
@@ -390,4 +376,4 @@ void updateTex() {
crop[2] + 2, -crop[3] + 2);
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/processing/opengl/FrameBuffer.java b/libs/processing-core/src/main/java/processing/opengl/FrameBuffer.java
similarity index 82%
rename from core/src/processing/opengl/FrameBuffer.java
rename to libs/processing-core/src/main/java/processing/opengl/FrameBuffer.java
index 31d50f559..a5426c52d 100644
--- a/core/src/processing/opengl/FrameBuffer.java
+++ b/libs/processing-core/src/main/java/processing/opengl/FrameBuffer.java
@@ -3,12 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-12 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,6 +26,7 @@
import processing.core.PApplet;
import processing.core.PConstants;
+import processing.opengl.PGraphicsOpenGL.GLResourceFrameBuffer;
import java.nio.IntBuffer;
@@ -51,6 +53,7 @@ public class FrameBuffer implements PConstants {
public int glMultisample;
public int width;
public int height;
+ private GLResourceFrameBuffer glres;
protected int depthBits;
protected int stencilBits;
@@ -148,31 +151,6 @@ public class FrameBuffer implements PConstants {
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (!screenFb) {
- if (glFbo != 0) {
- PGraphicsOpenGL.finalizeFrameBufferObject(glFbo, context);
- }
- if (glDepth != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glDepth, context);
- }
- if (glStencil != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glStencil, context);
- }
- if (glMultisample != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glMultisample, context);
- }
- if (glDepthStencil != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glDepthStencil, context);
- }
- }
- } finally {
- super.finalize();
- }
- }
-
public void clear() {
pg.pushFramebuffer();
pg.setFramebuffer(this);
@@ -353,26 +331,23 @@ protected void allocate() {
dispose(); // Just in the case this object is being re-allocated.
context = pgl.getCurrentContext();
+ glres = new GLResourceFrameBuffer(this); // create the FBO resources...
if (screenFb) {
glFbo = 0;
} else {
- //create the FBO object...
- glFbo = PGraphicsOpenGL.createFrameBufferObject(context, pgl);
-
- // ... and then create the rest of the stuff.
if (multisample) {
- createColorBufferMultisample();
+ initColorBufferMultisample();
}
if (packedDepthStencil) {
- createPackedDepthStencilBuffer();
+ initPackedDepthStencilBuffer();
} else {
if (0 < depthBits) {
- createDepthBuffer();
+ initDepthBuffer();
}
if (0 < stencilBits) {
- createStencilBuffer();
+ initStencilBuffer();
}
}
}
@@ -381,26 +356,14 @@ protected void allocate() {
protected void dispose() {
if (screenFb) return;
-
- if (glFbo != 0) {
- PGraphicsOpenGL.finalizeFrameBufferObject(glFbo, context);
+ if (glres != null) {
+ glres.dispose();
glFbo = 0;
- }
- if (glDepth != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glDepth, context);
glDepth = 0;
- }
- if (glStencil != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glStencil, context);
glStencil = 0;
- }
- if (glMultisample != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glMultisample, context);
glMultisample = 0;
- }
- if (glDepthStencil != 0) {
- PGraphicsOpenGL.finalizeRenderBufferObject(glDepthStencil, context);
glDepthStencil = 0;
+ glres = null;
}
}
@@ -410,18 +373,7 @@ protected boolean contextIsOutdated() {
boolean outdated = !pgl.contextIsCurrent(context);
if (outdated) {
- PGraphicsOpenGL.removeFrameBufferObject(glFbo, context);
- PGraphicsOpenGL.removeRenderBufferObject(glDepth, context);
- PGraphicsOpenGL.removeRenderBufferObject(glStencil, context);
- PGraphicsOpenGL.removeRenderBufferObject(glDepthStencil, context);
- PGraphicsOpenGL.removeRenderBufferObject(glMultisample, context);
-
- glFbo = 0;
- glDepth = 0;
- glStencil = 0;
- glDepthStencil = 0;
- glMultisample = 0;
-
+ dispose();
for (int i = 0; i < numColorBuffers; i++) {
colorBufferTex[i] = null;
}
@@ -430,13 +382,12 @@ protected boolean contextIsOutdated() {
}
- protected void createColorBufferMultisample() {
+ protected void initColorBufferMultisample() {
if (screenFb) return;
pg.pushFramebuffer();
pg.setFramebuffer(this);
- glMultisample = PGraphicsOpenGL.createRenderBufferObject(context, pgl);
pgl.bindRenderbuffer(PGL.RENDERBUFFER, glMultisample);
pgl.renderbufferStorageMultisample(PGL.RENDERBUFFER, nsamples,
PGL.RGBA8, width, height);
@@ -447,7 +398,7 @@ protected void createColorBufferMultisample() {
}
- protected void createPackedDepthStencilBuffer() {
+ protected void initPackedDepthStencilBuffer() {
if (screenFb) return;
if (width == 0 || height == 0) {
@@ -457,7 +408,6 @@ protected void createPackedDepthStencilBuffer() {
pg.pushFramebuffer();
pg.setFramebuffer(this);
- glDepthStencil = PGraphicsOpenGL.createRenderBufferObject(context, pgl);
pgl.bindRenderbuffer(PGL.RENDERBUFFER, glDepthStencil);
if (multisample) {
@@ -477,7 +427,7 @@ protected void createPackedDepthStencilBuffer() {
}
- protected void createDepthBuffer() {
+ protected void initDepthBuffer() {
if (screenFb) return;
if (width == 0 || height == 0) {
@@ -487,7 +437,6 @@ protected void createDepthBuffer() {
pg.pushFramebuffer();
pg.setFramebuffer(this);
- glDepth = PGraphicsOpenGL.createRenderBufferObject(context, pgl);
pgl.bindRenderbuffer(PGL.RENDERBUFFER, glDepth);
int glConst = PGL.DEPTH_COMPONENT16;
@@ -513,7 +462,7 @@ protected void createDepthBuffer() {
}
- protected void createStencilBuffer() {
+ protected void initStencilBuffer() {
if (screenFb) return;
if (width == 0 || height == 0) {
@@ -523,7 +472,6 @@ protected void createStencilBuffer() {
pg.pushFramebuffer();
pg.setFramebuffer(this);
- glStencil = PGraphicsOpenGL.createRenderBufferObject(context, pgl);
pgl.bindRenderbuffer(PGL.RENDERBUFFER, glStencil);
int glConst = PGL.STENCIL_INDEX1;
diff --git a/core/src/processing/opengl/LinePath.java b/libs/processing-core/src/main/java/processing/opengl/LinePath.java
similarity index 100%
rename from core/src/processing/opengl/LinePath.java
rename to libs/processing-core/src/main/java/processing/opengl/LinePath.java
diff --git a/core/src/processing/opengl/LineStroker.java b/libs/processing-core/src/main/java/processing/opengl/LineStroker.java
similarity index 100%
rename from core/src/processing/opengl/LineStroker.java
rename to libs/processing-core/src/main/java/processing/opengl/LineStroker.java
diff --git a/core/src/processing/opengl/PGL.java b/libs/processing-core/src/main/java/processing/opengl/PGL.java
similarity index 69%
rename from core/src/processing/opengl/PGL.java
rename to libs/processing-core/src/main/java/processing/opengl/PGL.java
index c0946b903..18e4d8bb8 100644
--- a/core/src/processing/opengl/PGL.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PGL.java
@@ -3,12 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -23,9 +24,6 @@
package processing.opengl;
-import processing.core.PApplet;
-import processing.core.PGraphics;
-
import java.io.IOException;
import java.net.URL;
import java.nio.Buffer;
@@ -35,6 +33,12 @@
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Arrays;
+import java.util.regex.Pattern;
+
+import processing.core.PApplet;
+import processing.core.PConstants;
+import processing.core.PGraphics;
+
/**
* Processing-OpenGL abstraction layer. Needs to be implemented by subclasses
@@ -48,8 +52,9 @@ public abstract class PGL {
// Basic fields
- /** The PGraphics object using this interface */
- protected PGraphicsOpenGL pg;
+ /** The PGraphics and PApplet objects using this interface */
+ protected PGraphicsOpenGL graphics;
+ protected PApplet sketch;
/** OpenGL thread */
protected Thread glThread;
@@ -64,22 +69,14 @@ public abstract class PGL {
// Parameters
- protected static boolean USE_FBOLAYER_BY_DEFAULT = false;
- protected static int REQUESTED_DEPTH_BITS = 24;
- protected static int REQUESTED_STENCIL_BITS = 8;
- protected static int REQUESTED_ALPHA_BITS = 8;
+ public static int REQUESTED_DEPTH_BITS = 24;
+ public static int REQUESTED_STENCIL_BITS = 8;
+ public static int REQUESTED_ALPHA_BITS = 8;
/** Switches between the use of regular and direct buffers. */
protected static boolean USE_DIRECT_BUFFERS = true;
protected static int MIN_DIRECT_BUFFER_SIZE = 1;
- /** This flag enables/disables a hack to make sure that anything drawn
- * in setup will be maintained even a renderer restart (e.g.: smooth change).
- * See the code and comments involving this constant in
- * PGraphicsOpenGL.endDraw().
- */
- protected static boolean SAVE_SURFACE_TO_PIXELS_HACK = true;
-
/** Enables/disables mipmap use. */
protected static boolean MIPMAPS_ENABLED = true;
@@ -132,21 +129,35 @@ public abstract class PGL {
// ........................................................
+ // Variables to handle single-buffered situations (i.e.: Android)
+
+ protected IntBuffer firstFrame;
+ protected static boolean SINGLE_BUFFERED = false;
+
+ // ........................................................
+
// FBO layer
- protected boolean fboLayerRequested = false;
+ protected boolean fboLayerEnabled = false;
protected boolean fboLayerCreated = false;
- protected boolean fboLayerInUse = false;
- protected boolean firstFrame = true;
- protected int reqNumSamples;
+ protected boolean fboLayerEnabledReq = false;
+ protected boolean fboLayerDisableReq = false;
+ protected boolean fbolayerResetReq = false;
+ public int reqNumSamples;
protected int numSamples;
+
protected IntBuffer glColorFbo;
- protected IntBuffer glMultiFbo;
- protected IntBuffer glColorBuf;
protected IntBuffer glColorTex;
protected IntBuffer glDepthStencil;
protected IntBuffer glDepth;
protected IntBuffer glStencil;
+
+ protected IntBuffer glMultiFbo;
+ protected IntBuffer glMultiColor;
+ protected IntBuffer glMultiDepthStencil;
+ protected IntBuffer glMultiDepth;
+ protected IntBuffer glMultiStencil;
+
protected int fboWidth, fboHeight;
protected int backTex, frontTex;
@@ -249,38 +260,62 @@ public abstract class PGL {
protected FloatBuffer depthBuffer;
protected ByteBuffer stencilBuffer;
+ //........................................................
+
+ // Rendering information
+
+ /** Used to register amount of geometry rendered in each frame. */
+ protected int geomCount = 0;
+ protected int pgeomCount;
+
+ /** Used to register calls to background. */
+ protected boolean clearColor = false;
+ protected boolean pclearColor;
+
+ protected boolean clearDepth = false;
+ protected boolean pclearDepth;
+
+ protected boolean clearStencil = false;
+ protected boolean pclearStencil;
+
+
// ........................................................
// Error messages
- protected static final String WIKI =
+ public static final String WIKI =
" Read http://wiki.processing.org/w/OpenGL_Issues for help.";
- protected static final String FRAMEBUFFER_ERROR =
+ public static final String FRAMEBUFFER_ERROR =
"Framebuffer error (%1$s), rendering will probably not work as expected" + WIKI;
- protected static final String MISSING_FBO_ERROR =
+ public static final String MISSING_FBO_ERROR =
"Framebuffer objects are not supported by this hardware (or driver)" + WIKI;
- protected static final String MISSING_GLSL_ERROR =
+ public static final String MISSING_GLSL_ERROR =
"GLSL shaders are not supported by this hardware (or driver)" + WIKI;
- protected static final String MISSING_GLFUNC_ERROR =
+ public static final String MISSING_GLFUNC_ERROR =
"GL function %1$s is not available on this hardware (or driver)" + WIKI;
- protected static final String UNSUPPORTED_GLPROF_ERROR =
+ public static final String UNSUPPORTED_GLPROF_ERROR =
"Unsupported OpenGL profile.";
- protected static final String TEXUNIT_ERROR =
+ public static final String TEXUNIT_ERROR =
"Number of texture units not supported by this hardware (or driver)" + WIKI;
- protected static final String NONPRIMARY_ERROR =
+ public static final String NONPRIMARY_ERROR =
"The renderer is trying to call a PGL function that can only be called on a primary PGL. " +
"This is most likely due to a bug in the renderer's code, please report it with an " +
"issue on Processing's github page https://github.com/processing/processing/issues?state=open " +
"if using any of the built-in OpenGL renderers. If you are using a contributed " +
"library, contact the library's developers.";
+ protected static final String DEPTH_READING_NOT_ENABLED_ERROR =
+ "Reading depth and stencil values from this multisampled buffer is not enabled. " +
+ "You can enable it by calling hint(ENABLE_DEPTH_READING) once. " +
+ "If your sketch becomes too slow, disable multisampling with noSmooth() instead.";
+
// ........................................................
// Constants
@@ -314,6 +349,40 @@ public abstract class PGL {
protected static boolean BIG_ENDIAN =
ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
+ // ........................................................
+
+ // Present mode
+
+ // ........................................................
+
+ // Present mode
+
+ protected boolean presentMode = false;
+ protected boolean showStopButton = true;
+ public float presentX;
+ public float presentY;
+ protected IntBuffer closeButtonTex;
+ protected int stopButtonColor;
+ protected int stopButtonWidth = 28;
+ protected int stopButtonHeight = 12;
+ protected int stopButtonX = 21; // The position of the close button is relative to the
+ protected int closeButtonY = 21; // lower left corner
+ protected static int[] closeButtonPix = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, -1,
+ -1, -1, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0,
+ 0, 0, 0, -1, -1, 0, -1, -1, 0, 0, -1, -1, 0, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
+ -1, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1,
+ 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1,
+ 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, -1, 0, -1,
+ -1, 0, 0, -1, -1, 0, -1, -1, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1,
+ 0, 0, 0, -1, -1, -1, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0};
+
///////////////////////////////////////////////////////////////
@@ -324,19 +393,19 @@ public PGL() { }
public PGL(PGraphicsOpenGL pg) {
- this.pg = pg;
+ this.graphics = pg;
if (glColorTex == null) {
- glColorTex = allocateIntBuffer(2);
glColorFbo = allocateIntBuffer(1);
- glMultiFbo = allocateIntBuffer(1);
- glColorBuf = allocateIntBuffer(1);
+ glColorTex = allocateIntBuffer(2);
glDepthStencil = allocateIntBuffer(1);
glDepth = allocateIntBuffer(1);
glStencil = allocateIntBuffer(1);
- fboLayerCreated = false;
- fboLayerInUse = false;
- firstFrame = false;
+ glMultiFbo = allocateIntBuffer(1);
+ glMultiColor = allocateIntBuffer(1);
+ glMultiDepthStencil = allocateIntBuffer(1);
+ glMultiDepth = allocateIntBuffer(1);
+ glMultiStencil = allocateIntBuffer(1);
}
byteBuffer = allocateByteBuffer(1);
@@ -345,98 +414,117 @@ public PGL(PGraphicsOpenGL pg) {
}
+ public void dispose() {
+ destroyFBOLayer();
+ graphics = null;
+ sketch = null;
+ }
+
+
public void setPrimary(boolean primary) {
primaryPGL = primary;
}
- /**
- * Return the native canvas the OpenGL context associated to this PGL object
- * is rendering to (if any).
- */
- public abstract Object getCanvas();
+ static public int smoothToSamples(int smooth) {
+ if (smooth == 0) {
+ // smooth(0) is noSmooth(), which is 1x sampling
+ return 1;
+ } else if (smooth == 1) {
+ // smooth(1) means "default smoothing", which is 2x for OpenGL
+ return 2;
+ } else {
+ // smooth(N) can be used for 4x, 8x, etc
+ return smooth;
+ }
+ }
- protected abstract void setFps(float fps);
+ abstract public Object getNative();
- protected abstract void initSurface(int antialias);
+ public void setFrameRate(float fps) {
+ targetFps = fps;
+ currentFps = fps;
+ setFps = true;
+ }
- protected abstract void reinitSurface();
+ public float getFrameRate() {
+ return currentFps;
+ }
- protected abstract void registerListeners();
+ abstract protected void initSurface(int antialias);
- protected void deleteSurface() {
- if (threadIsCurrent() && fboLayerCreated) {
- deleteTextures(2, glColorTex);
- deleteFramebuffers(1, glColorFbo);
- deleteFramebuffers(1, glMultiFbo);
- deleteRenderbuffers(1, glColorBuf);
- deleteRenderbuffers(1, glDepthStencil);
- deleteRenderbuffers(1, glDepth);
- deleteRenderbuffers(1, glStencil);
- }
+ abstract protected void reinitSurface();
- fboLayerCreated = false;
- fboLayerInUse = false;
- firstFrame = false;
- }
+
+ abstract protected void registerListeners();
protected int getReadFramebuffer() {
- return fboLayerInUse ? glColorFbo.get(0) : 0;
+ return fboLayerEnabled ? glColorFbo.get(0) : 0;
}
protected int getDrawFramebuffer() {
- if (fboLayerInUse) return 1 < numSamples ? glMultiFbo.get(0) :
- glColorFbo.get(0);
+ if (fboLayerEnabled) return 1 < numSamples ? glMultiFbo.get(0) :
+ glColorFbo.get(0);
else return 0;
}
protected int getDefaultDrawBuffer() {
- return fboLayerInUse ? COLOR_ATTACHMENT0 : FRONT;
+ return fboLayerEnabled ? COLOR_ATTACHMENT0 : BACK;
}
protected int getDefaultReadBuffer() {
- return fboLayerInUse ? COLOR_ATTACHMENT0 : FRONT;
+ return fboLayerEnabled ? COLOR_ATTACHMENT0 : FRONT;
}
protected boolean isFBOBacked() {;
- return fboLayerInUse;
+ return fboLayerEnabled;
}
- protected void requestFBOLayer() {
- fboLayerRequested = true;
+ @Deprecated
+ public void requestFBOLayer() {
+ enableFBOLayer();
}
- protected boolean isMultisampled() {
- return 1 < numSamples;
+ public void enableFBOLayer() {
+ fboLayerEnabledReq = true;
}
- protected int getDepthBits() {
- intBuffer.rewind();
- getIntegerv(DEPTH_BITS, intBuffer);
- return intBuffer.get(0);
+ public void disableFBOLayer() {
+ fboLayerDisableReq = true;
}
- protected int getStencilBits() {
- intBuffer.rewind();
- getIntegerv(STENCIL_BITS, intBuffer);
- return intBuffer.get(0);
+ public void resetFBOLayer() {
+ fbolayerResetReq = true;
+ }
+
+
+ abstract public void queueEvent(Runnable runnable);
+
+ protected boolean isMultisampled() {
+ return 1 < numSamples;
}
+ abstract protected int getDepthBits();
+
+
+ abstract protected int getStencilBits();
+
+
protected boolean getDepthTest() {
intBuffer.rewind();
getBooleanv(DEPTH_TEST, intBuffer);
@@ -453,14 +541,14 @@ protected boolean getDepthWriteMask() {
protected Texture wrapBackTexture(Texture texture) {
if (texture == null) {
- texture = new Texture(pg);
- texture.init(pg.width, pg.height,
+ texture = new Texture(graphics);
+ texture.init(graphics.width, graphics.height,
glColorTex.get(backTex), TEXTURE_2D, RGBA,
fboWidth, fboHeight, NEAREST, NEAREST,
CLAMP_TO_EDGE, CLAMP_TO_EDGE);
- texture.invertedY(true);
+ texture.invertedY(!graphics.cameraUp);
texture.colorBuffer(true);
- pg.setCache(pg, texture);
+ graphics.setCache(graphics, texture);
} else {
texture.glName = glColorTex.get(backTex);
}
@@ -470,12 +558,12 @@ protected Texture wrapBackTexture(Texture texture) {
protected Texture wrapFrontTexture(Texture texture) {
if (texture == null) {
- texture = new Texture(pg);
- texture.init(pg.width, pg.height,
+ texture = new Texture(graphics);
+ texture.init(graphics.width, graphics.height,
glColorTex.get(frontTex), TEXTURE_2D, RGBA,
fboWidth, fboHeight, NEAREST, NEAREST,
CLAMP_TO_EDGE, CLAMP_TO_EDGE);
- texture.invertedY(true);
+ texture.invertedY(!graphics.cameraUp);
texture.colorBuffer(true);
} else {
texture.glName = glColorTex.get(frontTex);
@@ -513,22 +601,148 @@ protected void syncBackTexture() {
if (1 < numSamples) {
bindFramebufferImpl(READ_FRAMEBUFFER, glMultiFbo.get(0));
bindFramebufferImpl(DRAW_FRAMEBUFFER, glColorFbo.get(0));
+ int mask = COLOR_BUFFER_BIT;
+ if (graphics.getHint(PConstants.ENABLE_BUFFER_READING)) {
+ mask |= DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT;
+ }
blitFramebuffer(0, 0, fboWidth, fboHeight,
0, 0, fboWidth, fboHeight,
- COLOR_BUFFER_BIT, NEAREST);
+ mask, NEAREST);
}
}
+ abstract protected float getPixelScale();
+
+ ///////////////////////////////////////////////////////////
+
+ // Present mode
+
+
+ public void initPresentMode(float x, float y, int stopColor) {
+ presentMode = true;
+ showStopButton = stopColor != 0;
+ stopButtonColor = stopColor;
+ presentX = x;
+ presentY = y;
+ enableFBOLayer();
+ }
+
+
+ public boolean presentMode() {
+ return presentMode;
+ }
+
+
+ public float presentX() {
+ return presentX;
+ }
+
+
+ public float presentY() {
+ return presentY;
+ }
+
+
+ public boolean insideStopButton(float x, float y) {
+ if (!showStopButton) return false;
+ return stopButtonX < x && x < stopButtonX + stopButtonWidth &&
+ -(closeButtonY + stopButtonHeight) < y && y < -closeButtonY;
+ }
+
+
///////////////////////////////////////////////////////////
// Frame rendering
- protected void beginDraw(boolean clear0) {
- if (needFBOLayer(clear0)) {
- if (!fboLayerCreated) createFBOLayer();
+ protected void clearDepthStencil() {
+ if (!pclearDepth && !pclearStencil) {
+ depthMask(true);
+ clearDepth(1);
+ clearStencil(0);
+ clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT);
+ } else if (!pclearDepth) {
+ depthMask(true);
+ clearDepth(1);
+ clear(DEPTH_BUFFER_BIT);
+ } else if (!pclearStencil) {
+ clearStencil(0);
+ clear(STENCIL_BUFFER_BIT);
+ }
+ }
+
+
+ protected void clearBackground(float r, float g, float b, float a,
+ boolean depth, boolean stencil) {
+ clearColor(r, g, b, a);
+ if (depth && stencil) {
+ clearDepth(1);
+ clearStencil(0);
+ clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT);
+ if (0 < sketch.frameCount) {
+ clearColor = true;
+ clearDepth = true;
+ clearStencil = true;
+ }
+ } else if (depth) {
+ clearDepth(1);
+ clear(DEPTH_BUFFER_BIT | COLOR_BUFFER_BIT);
+ if (0 < sketch.frameCount) {
+ clearColor = true;
+ clearDepth = true;
+ }
+ } else if (stencil) {
+ clearStencil(0);
+ clear(STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT);
+ if (0 < sketch.frameCount) {
+ clearColor = true;
+ clearStencil = true;
+ }
+ } else {
+ clear(PGL.COLOR_BUFFER_BIT);
+ if (0 < sketch.frameCount) {
+ clearColor = true;
+ }
+ }
+ if (fboLayerEnabled) {
+ clearFrontColorBuffer();
+ }
+ }
+
+ protected void beginRender() {
+ if (sketch == null) {
+ sketch = graphics.parent;
+ }
+
+ pgeomCount = geomCount;
+ geomCount = 0;
+
+ pclearColor = clearColor;
+ clearColor = false;
+
+ pclearDepth = clearDepth;
+ clearDepth = false;
+
+ pclearStencil = clearStencil;
+ clearStencil = false;
+
+ if (fboLayerEnabledReq) {
+ fboLayerEnabled = true;
+ fboLayerEnabledReq = false;
+ }
+
+ if (fboLayerEnabled) {
+ if (fbolayerResetReq) {
+ destroyFBOLayer();
+ fbolayerResetReq = false;
+ }
+ if (!fboLayerCreated) {
+ createFBOLayer();
+ }
+
+ // Draw to the back texture
bindFramebufferImpl(FRAMEBUFFER, glColorFbo.get(0));
framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0,
TEXTURE_2D, glColorTex.get(backTex), 0);
@@ -537,65 +751,130 @@ protected void beginDraw(boolean clear0) {
bindFramebufferImpl(FRAMEBUFFER, glMultiFbo.get(0));
}
- if (firstFrame) {
+ if (sketch.frameCount == 0) {
// No need to draw back color buffer because we are in the first frame.
- int argb = pg.backgroundColor;
- float a = ((argb >> 24) & 0xff) / 255.0f;
- float r = ((argb >> 16) & 0xff) / 255.0f;
- float g = ((argb >> 8) & 0xff) / 255.0f;
- float b = ((argb) & 0xff) / 255.0f;
- clearColor(r, g, b, a);
+ int argb = graphics.backgroundColor;
+ float ba = ((argb >> 24) & 0xff) / 255.0f;
+ float br = ((argb >> 16) & 0xff) / 255.0f;
+ float bg = ((argb >> 8) & 0xff) / 255.0f;
+ float bb = ((argb) & 0xff) / 255.0f;
+ clearColor(br, bg, bb, ba);
clear(COLOR_BUFFER_BIT);
- } else if (!clear0) {
- // Render previous back texture (now is the front) as background,
- // because no background() is being used ("incremental drawing")
- drawTexture(TEXTURE_2D, glColorTex.get(frontTex),
- fboWidth, fboHeight, pg.width, pg.height,
- 0, 0, pg.width, pg.height,
- 0, 0, pg.width, pg.height);
+ } else if (!pclearColor || !graphics.isLooping()) {
+ // Render previous back texture (now is the front) as background, because no background()
+ // is being used ("incremental drawing")
+ int x = 0;
+ int y = 0;
+ if (presentMode) {
+ x = (int)presentX;
+ y = (int)presentY;
+ }
+ float scale = getPixelScale();
+ drawTexture(TEXTURE_2D, glColorTex.get(frontTex), fboWidth, fboHeight,
+ x, y, graphics.width, graphics.height,
+ 0, 0, (int)(scale * graphics.width), (int)(scale * graphics.height),
+ 0, 0, graphics.width, graphics.height);
}
-
- fboLayerInUse = true;
- } else {
- fboLayerInUse = false;
- }
-
- if (firstFrame) {
- firstFrame = false;
- }
-
- if (!USE_FBOLAYER_BY_DEFAULT) {
- // The result of this assignment is the following: if the user requested
- // at some point the use of the FBO layer, but subsequently didn't
- // request it again, then the rendering won't render to the FBO layer if
- // not needed by the condif, since it is slower than simple onscreen
- // rendering.
- fboLayerRequested = false;
+ } else if (SINGLE_BUFFERED && sketch.frameCount == 1) {
+ restoreFirstFrame();
}
}
- protected void endDraw(boolean clear0) {
- if (fboLayerInUse) {
+ protected void endRender(int windowColor) {
+ if (fboLayerEnabled) {
syncBackTexture();
// Draw the contents of the back texture to the screen framebuffer.
bindFramebufferImpl(FRAMEBUFFER, 0);
- clearDepth(1);
- clearColor(0, 0, 0, 0);
- clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT);
+ if (presentMode) {
+ float wa = ((windowColor >> 24) & 0xff) / 255.0f;
+ float wr = ((windowColor >> 16) & 0xff) / 255.0f;
+ float wg = ((windowColor >> 8) & 0xff) / 255.0f;
+ float wb = (windowColor & 0xff) / 255.0f;
+ clearDepth(1);
+ clearColor(wr, wg, wb, wa);
+ clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT);
+
+ if (showStopButton) {
+ if (closeButtonTex == null) {
+ closeButtonTex = allocateIntBuffer(1);
+ genTextures(1, closeButtonTex);
+ bindTexture(TEXTURE_2D, closeButtonTex.get(0));
+ texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST);
+ texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST);
+ texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE);
+ texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE);
+ texImage2D(TEXTURE_2D, 0, RGBA, stopButtonWidth, stopButtonHeight, 0, RGBA, UNSIGNED_BYTE, null);
+
+ int[] color = new int[closeButtonPix.length];
+ PApplet.arrayCopy(closeButtonPix, color);
+
+
+ // Multiply the texture by the button color
+ float ba = ((stopButtonColor >> 24) & 0xFF) / 255f;
+ float br = ((stopButtonColor >> 16) & 0xFF) / 255f;
+ float bg = ((stopButtonColor >> 8) & 0xFF) / 255f;
+ float bb = ((stopButtonColor >> 0) & 0xFF) / 255f;
+ for (int i = 0; i < color.length; i++) {
+ int c = closeButtonPix[i];
+ int a = (int)(ba * ((c >> 24) & 0xFF));
+ int r = (int)(br * ((c >> 16) & 0xFF));
+ int g = (int)(bg * ((c >> 8) & 0xFF));
+ int b = (int)(bb * ((c >> 0) & 0xFF));
+ color[i] = javaToNativeARGB((a << 24) | (r << 16) | (g << 8) | b);
+ }
+ IntBuffer buf = allocateIntBuffer(color);
+ copyToTexture(TEXTURE_2D, RGBA, closeButtonTex.get(0), 0, 0, stopButtonWidth, stopButtonHeight, buf);
+ bindTexture(TEXTURE_2D, 0);
+ }
+ drawTexture(TEXTURE_2D, closeButtonTex.get(0), stopButtonWidth, stopButtonHeight,
+ 0, 0, stopButtonX + stopButtonWidth, closeButtonY + stopButtonHeight,
+ 0, stopButtonHeight, stopButtonWidth, 0,
+ stopButtonX, closeButtonY, stopButtonX + stopButtonWidth, closeButtonY + stopButtonHeight);
+ }
+ } else {
+ clearDepth(1);
+ clearColor(0, 0, 0, 0);
+ clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT);
+ }
// Render current back texture to screen, without blending.
disable(BLEND);
+ int x = 0;
+ int y = 0;
+ if (presentMode) {
+ x = (int)presentX;
+ y = (int)presentY;
+ }
+ float scale = getPixelScale();
drawTexture(TEXTURE_2D, glColorTex.get(backTex),
- fboWidth, fboHeight, pg.width, pg.height,
- 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height);
+ fboWidth, fboHeight,
+ x, y, graphics.width, graphics.height,
+ 0, 0, (int)(scale * graphics.width), (int)(scale * graphics.height),
+ 0, 0, graphics.width, graphics.height);
// Swapping front and back textures.
int temp = frontTex;
frontTex = backTex;
backTex = temp;
+
+ if (fboLayerDisableReq) {
+ fboLayerEnabled = false;
+ fboLayerDisableReq = false;
+ }
+ } else {
+ if (SINGLE_BUFFERED && sketch.frameCount == 0) {
+ saveFirstFrame();
+ }
+
+ if (!clearColor && 0 < sketch.frameCount || !graphics.isLooping()) {
+ enableFBOLayer();
+ if (SINGLE_BUFFERED) {
+ createFBOLayer();
+ }
+ }
}
}
@@ -615,46 +894,49 @@ protected void endDraw(boolean clear0) {
protected abstract void swapBuffers();
- protected boolean threadIsCurrent() {
+ public boolean threadIsCurrent() {
return Thread.currentThread() == glThread;
}
- protected void beginGL() { }
+ public void setThread(Thread thread) {
+ glThread = thread;
+ }
- protected void endGL() { }
+ protected void beginGL() { }
- private boolean needFBOLayer(boolean clear0) {
- // TODO: need to revise this, on windows we might not want to use FBO layer
- // even with anti-aliasing enabled...
- return !clear0 || fboLayerRequested || 1 < numSamples;
- }
+ protected void endGL() { }
private void createFBOLayer() {
- String ext = getString(EXTENSIONS);
- if (-1 < ext.indexOf("texture_non_power_of_two")) {
- fboWidth = pg.width;
- fboHeight = pg.height;
+ float scale = getPixelScale();
+
+ if (hasNpotTexSupport()) {
+ fboWidth = (int)(scale * graphics.width);
+ fboHeight = (int)(scale * graphics.height);
} else {
- fboWidth = nextPowerOfTwo(pg.width);
- fboHeight = nextPowerOfTwo(pg.height);
+ fboWidth = nextPowerOfTwo((int)(scale * graphics.width));
+ fboHeight = nextPowerOfTwo((int)(scale * graphics.height));
}
- int maxs = maxSamples();
- if (-1 < ext.indexOf("_framebuffer_multisample") && 1 < maxs) {
+ if (hasFboMultisampleSupport()) {
+ int maxs = maxSamples();
numSamples = PApplet.min(reqNumSamples, maxs);
} else {
numSamples = 1;
}
boolean multisample = 1 < numSamples;
- boolean packed = ext.indexOf("packed_depth_stencil") != -1;
+ boolean packed = hasPackedDepthStencilSupport();
int depthBits = PApplet.min(REQUESTED_DEPTH_BITS, getDepthBits());
int stencilBits = PApplet.min(REQUESTED_STENCIL_BITS, getStencilBits());
+ backTex = 0;
+ frontTex = 1;
+ boolean savedFirstFrame = SINGLE_BUFFERED && sketch.frameCount == 0 && firstFrame != null;
+
genTextures(2, glColorTex);
for (int i = 0; i < 2; i++) {
bindTexture(TEXTURE_2D, glColorTex.get(i));
@@ -664,37 +946,136 @@ private void createFBOLayer() {
texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE);
texImage2D(TEXTURE_2D, 0, RGBA, fboWidth, fboHeight, 0,
RGBA, UNSIGNED_BYTE, null);
- initTexture(TEXTURE_2D, RGBA, fboWidth, fboHeight, pg.backgroundColor);
+ if (i == frontTex && savedFirstFrame) {
+ // Copy first frame to front texture (will be drawn as background in next frame)
+ texSubImage2D(TEXTURE_2D, 0, 0, 0, graphics.width, graphics.height,
+ RGBA, UNSIGNED_BYTE, firstFrame);
+ } else {
+ // Intitialize texture with background color
+ initTexture(TEXTURE_2D, RGBA, fboWidth, fboHeight, graphics.backgroundColor);
+ }
}
bindTexture(TEXTURE_2D, 0);
- backTex = 0;
- frontTex = 1;
-
genFramebuffers(1, glColorFbo);
bindFramebufferImpl(FRAMEBUFFER, glColorFbo.get(0));
framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D,
glColorTex.get(backTex), 0);
+ if (!multisample || graphics.getHint(PConstants.ENABLE_BUFFER_READING)) {
+ // If not multisampled, this is the only depth and stencil buffer.
+ // If multisampled and depth reading enabled, these are going to
+ // hold downsampled depth and stencil buffers.
+ createDepthAndStencilBuffer(false, depthBits, stencilBits, packed);
+ }
+
if (multisample) {
// Creating multisampled FBO
genFramebuffers(1, glMultiFbo);
bindFramebufferImpl(FRAMEBUFFER, glMultiFbo.get(0));
// color render buffer...
- genRenderbuffers(1, glColorBuf);
- bindRenderbuffer(RENDERBUFFER, glColorBuf.get(0));
+ genRenderbuffers(1, glMultiColor);
+ bindRenderbuffer(RENDERBUFFER, glMultiColor.get(0));
renderbufferStorageMultisample(RENDERBUFFER, numSamples,
RGBA8, fboWidth, fboHeight);
framebufferRenderbuffer(FRAMEBUFFER, COLOR_ATTACHMENT0,
- RENDERBUFFER, glColorBuf.get(0));
+ RENDERBUFFER, glMultiColor.get(0));
+
+ // Creating multisampled depth and stencil buffers
+ createDepthAndStencilBuffer(true, depthBits, stencilBits, packed);
+ }
+
+ validateFramebuffer();
+
+ // Clear all buffers.
+ clearDepth(1);
+ clearStencil(0);
+ int argb = graphics.backgroundColor;
+ float ba = ((argb >> 24) & 0xff) / 255.0f;
+ float br = ((argb >> 16) & 0xff) / 255.0f;
+ float bg = ((argb >> 8) & 0xff) / 255.0f;
+ float bb = ((argb) & 0xff) / 255.0f;
+ clearColor(br, bg, bb, ba);
+ clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT);
+
+ bindFramebufferImpl(FRAMEBUFFER, 0);
+ initFBOLayer();
+
+ fboLayerCreated = true;
+ }
+
+ protected abstract void initFBOLayer();
+
+
+ protected void saveFirstFrame() {
+ firstFrame = allocateDirectIntBuffer(graphics.width * graphics.height);
+ if (hasReadBuffer()) readBuffer(BACK);
+ readPixelsImpl(0, 0, graphics.width, graphics.height, RGBA, UNSIGNED_BYTE, firstFrame);
+ }
+
+
+ protected void restoreFirstFrame() {
+ if (firstFrame == null) return;
+
+ IntBuffer tex = allocateIntBuffer(1);
+ genTextures(1, tex);
+
+ int w, h;
+ float scale = getPixelScale();
+ if (hasNpotTexSupport()) {
+ w = (int)(scale * graphics.width);
+ h = (int)(scale * graphics.height);
+ } else {
+ w = nextPowerOfTwo((int)(scale * graphics.width));
+ h = nextPowerOfTwo((int)(scale * graphics.height));
}
+ bindTexture(TEXTURE_2D, tex.get(0));
+ texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST);
+ texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST);
+ texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE);
+ texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE);
+ texImage2D(TEXTURE_2D, 0, RGBA, w, h, 0, RGBA, UNSIGNED_BYTE, null);
+ texSubImage2D(TEXTURE_2D, 0, 0, 0, graphics.width, graphics.height, RGBA, UNSIGNED_BYTE, firstFrame);
+
+ drawTexture(TEXTURE_2D, tex.get(0), w, h,
+ 0, 0, graphics.width, graphics.height,
+ 0, 0, (int)(scale * graphics.width), (int)(scale * graphics.height),
+ 0, 0, graphics.width, graphics.height);
+
+ deleteTextures(1, tex);
+ firstFrame.clear();
+ firstFrame = null;
+ }
+
+ protected void destroyFBOLayer() {
+ if (threadIsCurrent() && fboLayerCreated) {
+ deleteFramebuffers(1, glColorFbo);
+ deleteTextures(2, glColorTex);
+ deleteRenderbuffers(1, glDepthStencil);
+ deleteRenderbuffers(1, glDepth);
+ deleteRenderbuffers(1, glStencil);
+
+ deleteFramebuffers(1, glMultiFbo);
+ deleteRenderbuffers(1, glMultiColor);
+ deleteRenderbuffers(1, glMultiDepthStencil);
+ deleteRenderbuffers(1, glMultiDepth);
+ deleteRenderbuffers(1, glMultiStencil);
+ }
+ fboLayerCreated = false;
+ }
+
+
+ private void createDepthAndStencilBuffer(boolean multisample, int depthBits,
+ int stencilBits, boolean packed) {
// Creating depth and stencil buffers
if (packed && depthBits == 24 && stencilBits == 8) {
// packed depth+stencil buffer
- genRenderbuffers(1, glDepthStencil);
- bindRenderbuffer(RENDERBUFFER, glDepthStencil.get(0));
+ IntBuffer depthStencilBuf =
+ multisample ? glMultiDepthStencil : glDepthStencil;
+ genRenderbuffers(1, depthStencilBuf);
+ bindRenderbuffer(RENDERBUFFER, depthStencilBuf.get(0));
if (multisample) {
renderbufferStorageMultisample(RENDERBUFFER, numSamples,
DEPTH24_STENCIL8, fboWidth, fboHeight);
@@ -703,9 +1084,9 @@ private void createFBOLayer() {
fboWidth, fboHeight);
}
framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER,
- glDepthStencil.get(0));
+ depthStencilBuf.get(0));
framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, RENDERBUFFER,
- glDepthStencil.get(0));
+ depthStencilBuf.get(0));
} else {
// separate depth and stencil buffers
if (0 < depthBits) {
@@ -718,8 +1099,9 @@ private void createFBOLayer() {
depthComponent = DEPTH_COMPONENT16;
}
- genRenderbuffers(1, glDepth);
- bindRenderbuffer(RENDERBUFFER, glDepth.get(0));
+ IntBuffer depthBuf = multisample ? glMultiDepth : glDepth;
+ genRenderbuffers(1, depthBuf);
+ bindRenderbuffer(RENDERBUFFER, depthBuf.get(0));
if (multisample) {
renderbufferStorageMultisample(RENDERBUFFER, numSamples,
depthComponent, fboWidth, fboHeight);
@@ -728,7 +1110,7 @@ private void createFBOLayer() {
fboWidth, fboHeight);
}
framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT,
- RENDERBUFFER, glDepth.get(0));
+ RENDERBUFFER, depthBuf.get(0));
}
if (0 < stencilBits) {
@@ -741,8 +1123,9 @@ private void createFBOLayer() {
stencilIndex = STENCIL_INDEX1;
}
- genRenderbuffers(1, glStencil);
- bindRenderbuffer(RENDERBUFFER, glStencil.get(0));
+ IntBuffer stencilBuf = multisample ? glMultiStencil : glStencil;
+ genRenderbuffers(1, stencilBuf);
+ bindRenderbuffer(RENDERBUFFER, stencilBuf.get(0));
if (multisample) {
renderbufferStorageMultisample(RENDERBUFFER, numSamples,
stencilIndex, fboWidth, fboHeight);
@@ -751,26 +1134,14 @@ private void createFBOLayer() {
fboWidth, fboHeight);
}
framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT,
- RENDERBUFFER, glStencil.get(0));
+ RENDERBUFFER, stencilBuf.get(0));
}
}
+ }
- validateFramebuffer();
- // Clear all buffers.
- clearDepth(1);
- clearStencil(0);
- int argb = pg.backgroundColor;
- float a = ((argb >> 24) & 0xff) / 255.0f;
- float r = ((argb >> 16) & 0xff) / 255.0f;
- float g = ((argb >> 8) & 0xff) / 255.0f;
- float b = ((argb) & 0xff) / 255.0f;
- clearColor(r, g, b, a);
- clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT);
+ protected void clearFrontColorBuffer() {
- bindFramebufferImpl(FRAMEBUFFER, 0);
-
- fboLayerCreated = true;
}
@@ -847,7 +1218,7 @@ protected void initTexture(int target, int format, int width, int height) {
protected void initTexture(int target, int format, int width, int height,
- int initColor) {
+ int initColor) {
int[] glcolor = new int[16 * 16];
Arrays.fill(glcolor, javaToNativeARGB(initColor));
IntBuffer texels = allocateDirectIntBuffer(16 * 16);
@@ -863,6 +1234,12 @@ protected void initTexture(int target, int format, int width, int height,
}
+ protected void copyToTexture(int target, int format, int id, int x, int y,
+ int w, int h, int[] buffer) {
+ copyToTexture(target, format, id, x, y, w, h, IntBuffer.wrap(buffer));
+
+ }
+
protected void copyToTexture(int target, int format, int id, int x, int y,
int w, int h, IntBuffer buffer) {
activeTexture(TEXTURE0);
@@ -885,24 +1262,42 @@ protected void copyToTexture(int target, int format, int id, int x, int y,
*/
public void drawTexture(int target, int id, int width, int height,
int X0, int Y0, int X1, int Y1) {
- drawTexture(target, id, width, height, width, height,
- X0, Y0, X1, Y1, X0, Y0, X1, Y1);
+ // If a texture is drawing on a viewport of the same size as its resolution,
+ // the pixel factor is 1:1, so we override the surface's pixel factor.
+ drawTexture(target, id, width, height,
+ 0, 0, width, height, 1,
+ X0, Y0, X1, Y1,
+ X0, Y0, X1, Y1);
}
/**
* Not an approved function, this will change or be removed in the future.
*/
- public void drawTexture(int target, int id,
- int texW, int texH, int scrW, int scrH,
+ public void drawTexture(int target, int id,int texW, int texH,
+ int viewX, int viewY, int viewW, int viewH,
+ int texX0, int texY0, int texX1, int texY1,
+ int scrX0, int scrY0, int scrX1, int scrY1) {
+ int viewF = (int)getPixelScale();
+ drawTexture(target, id, texW, texH,
+ viewX, viewY, viewW, viewH, viewF,
+ texX0, texY0, texX1, texY1,
+ scrX0, scrY0, scrX1, scrY1);
+ }
+
+
+ public void drawTexture(int target, int id,int texW, int texH,
+ int viewX, int viewY, int viewW, int viewH, int viewF,
int texX0, int texY0, int texX1, int texY1,
int scrX0, int scrY0, int scrX1, int scrY1) {
if (target == TEXTURE_2D) {
- drawTexture2D(id, texW, texH, scrW, scrH,
+ drawTexture2D(id, texW, texH,
+ viewX, viewY, viewW, viewH, viewF,
texX0, texY0, texX1, texY1,
scrX0, scrY0, scrX1, scrY1);
} else if (target == TEXTURE_RECTANGLE) {
- drawTextureRect(id, texW, texH, scrW, scrH,
+ drawTextureRect(id, texW, texH,
+ viewX, viewY, viewW, viewH, viewF,
texX0, texY0, texX1, texY1,
scrX0, scrY0, scrX1, scrY1);
}
@@ -910,11 +1305,13 @@ public void drawTexture(int target, int id,
protected PGL initTex2DShader() {
- PGL ppgl = primaryPGL ? this : pg.getPrimaryPGL();
+ PGL ppgl = primaryPGL ? this : graphics.getPrimaryPGL();
if (!ppgl.loadedTex2DShader || ppgl.tex2DShaderContext != ppgl.glContext) {
- String vertSource = PApplet.join(texVertShaderSource, "\n");
- String fragSource = PApplet.join(tex2DFragShaderSource, "\n");
+ String[] preprocVertSrc = preprocessVertexSource(texVertShaderSource, getGLSLVersion());
+ String vertSource = PApplet.join(preprocVertSrc, "\n");
+ String[] preprocFragSrc = preprocessFragmentSource(tex2DFragShaderSource, getGLSLVersion());
+ String fragSource = PApplet.join(preprocFragSrc, "\n");
ppgl.tex2DVertShader = createShader(VERTEX_SHADER, vertSource);
ppgl.tex2DFragShader = createShader(FRAGMENT_SHADER, fragSource);
if (0 < ppgl.tex2DVertShader && 0 < ppgl.tex2DFragShader) {
@@ -942,7 +1339,8 @@ protected PGL initTex2DShader() {
}
- protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH,
+ protected void drawTexture2D(int id, int texW, int texH,
+ int viewX, int viewY, int viewW, int viewH, int viewF,
int texX0, int texY0, int texX1, int texY1,
int scrX0, int scrY0, int scrX1, int scrY1) {
PGL ppgl = initTex2DShader();
@@ -962,7 +1360,7 @@ protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH,
// Making sure that the viewport matches the provided screen dimensions
viewBuffer.rewind();
getIntegerv(VIEWPORT, viewBuffer);
- viewport(0, 0, scrW, scrH);
+ viewportImpl(viewF * viewX, viewF * viewY, viewF * viewW, viewF * viewH);
useProgram(ppgl.tex2DShaderProgram);
@@ -972,23 +1370,23 @@ protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH,
// Vertex coordinates of the textured quad are specified
// in normalized screen space (-1, 1):
// Corner 1
- texCoords[ 0] = 2 * (float)scrX0 / scrW - 1;
- texCoords[ 1] = 2 * (float)scrY0 / scrH - 1;
+ texCoords[ 0] = 2 * (float)scrX0 / viewW - 1;
+ texCoords[ 1] = 2 * (float)scrY0 / viewH - 1;
texCoords[ 2] = (float)texX0 / texW;
texCoords[ 3] = (float)texY0 / texH;
// Corner 2
- texCoords[ 4] = 2 * (float)scrX1 / scrW - 1;
- texCoords[ 5] = 2 * (float)scrY0 / scrH - 1;
+ texCoords[ 4] = 2 * (float)scrX1 / viewW - 1;
+ texCoords[ 5] = 2 * (float)scrY0 / viewH - 1;
texCoords[ 6] = (float)texX1 / texW;
texCoords[ 7] = (float)texY0 / texH;
// Corner 3
- texCoords[ 8] = 2 * (float)scrX0 / scrW - 1;
- texCoords[ 9] = 2 * (float)scrY1 / scrH - 1;
+ texCoords[ 8] = 2 * (float)scrX0 / viewW - 1;
+ texCoords[ 9] = 2 * (float)scrY1 / viewH - 1;
texCoords[10] = (float)texX0 / texW;
texCoords[11] = (float)texY1 / texH;
// Corner 4
- texCoords[12] = 2 * (float)scrX1 / scrW - 1;
- texCoords[13] = 2 * (float)scrY1 / scrH - 1;
+ texCoords[12] = 2 * (float)scrX1 / viewW - 1;
+ texCoords[13] = 2 * (float)scrY1 / viewH - 1;
texCoords[14] = (float)texX1 / texW;
texCoords[15] = (float)texY1 / texH;
@@ -1032,18 +1430,20 @@ protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH,
}
depthMask(depthMask);
- viewport(viewBuffer.get(0), viewBuffer.get(1),
- viewBuffer.get(2), viewBuffer.get(3));
+ viewportImpl(viewBuffer.get(0), viewBuffer.get(1),
+ viewBuffer.get(2), viewBuffer.get(3));
}
}
protected PGL initTexRectShader() {
- PGL ppgl = primaryPGL ? this : pg.getPrimaryPGL();
+ PGL ppgl = primaryPGL ? this : graphics.getPrimaryPGL();
if (!ppgl.loadedTexRectShader || ppgl.texRectShaderContext != ppgl.glContext) {
- String vertSource = PApplet.join(texVertShaderSource, "\n");
- String fragSource = PApplet.join(texRectFragShaderSource, "\n");
+ String[] preprocVertSrc = preprocessVertexSource(texVertShaderSource, getGLSLVersion());
+ String vertSource = PApplet.join(preprocVertSrc, "\n");
+ String[] preprocFragSrc = preprocessFragmentSource(texRectFragShaderSource, getGLSLVersion());
+ String fragSource = PApplet.join(preprocFragSrc, "\n");
ppgl.texRectVertShader = createShader(VERTEX_SHADER, vertSource);
ppgl.texRectFragShader = createShader(FRAGMENT_SHADER, fragSource);
if (0 < ppgl.texRectVertShader && 0 < ppgl.texRectFragShader) {
@@ -1068,7 +1468,8 @@ protected PGL initTexRectShader() {
}
- protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH,
+ protected void drawTextureRect(int id, int texW, int texH,
+ int viewX, int viewY, int viewW, int viewH, int viewF,
int texX0, int texY0, int texX1, int texY1,
int scrX0, int scrY0, int scrX1, int scrY1) {
PGL ppgl = initTexRectShader();
@@ -1092,7 +1493,7 @@ protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH,
// Making sure that the viewport matches the provided screen dimensions
viewBuffer.rewind();
getIntegerv(VIEWPORT, viewBuffer);
- viewport(0, 0, scrW, scrH);
+ viewportImpl(viewF * viewX, viewF * viewY, viewF * viewW, viewF * viewH);
useProgram(ppgl.texRectShaderProgram);
@@ -1102,23 +1503,23 @@ protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH,
// Vertex coordinates of the textured quad are specified
// in normalized screen space (-1, 1):
// Corner 1
- texCoords[ 0] = 2 * (float)scrX0 / scrW - 1;
- texCoords[ 1] = 2 * (float)scrY0 / scrH - 1;
+ texCoords[ 0] = 2 * (float)scrX0 / viewW - 1;
+ texCoords[ 1] = 2 * (float)scrY0 / viewH - 1;
texCoords[ 2] = texX0;
texCoords[ 3] = texY0;
// Corner 2
- texCoords[ 4] = 2 * (float)scrX1 / scrW - 1;
- texCoords[ 5] = 2 * (float)scrY0 / scrH - 1;
+ texCoords[ 4] = 2 * (float)scrX1 / viewW - 1;
+ texCoords[ 5] = 2 * (float)scrY0 / viewH - 1;
texCoords[ 6] = texX1;
texCoords[ 7] = texY0;
// Corner 3
- texCoords[ 8] = 2 * (float)scrX0 / scrW - 1;
- texCoords[ 9] = 2 * (float)scrY1 / scrH - 1;
+ texCoords[ 8] = 2 * (float)scrX0 / viewW - 1;
+ texCoords[ 9] = 2 * (float)scrY1 / viewH - 1;
texCoords[10] = texX0;
texCoords[11] = texY1;
// Corner 4
- texCoords[12] = 2 * (float)scrX1 / scrW - 1;
- texCoords[13] = 2 * (float)scrY1 / scrH - 1;
+ texCoords[12] = 2 * (float)scrX1 / viewW - 1;
+ texCoords[13] = 2 * (float)scrY1 / viewH - 1;
texCoords[14] = texX1;
texCoords[15] = texY1;
@@ -1162,8 +1563,8 @@ protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH,
}
depthMask(depthMask);
- viewport(viewBuffer.get(0), viewBuffer.get(1),
- viewBuffer.get(2), viewBuffer.get(3));
+ viewportImpl(viewBuffer.get(0), viewBuffer.get(1),
+ viewBuffer.get(2), viewBuffer.get(3));
}
}
@@ -1173,7 +1574,7 @@ protected int getColorValue(int scrX, int scrY) {
colorBuffer = IntBuffer.allocate(1);
}
colorBuffer.rewind();
- readPixels(scrX, pg.height - scrY - 1, 1, 1, RGBA, UNSIGNED_BYTE,
+ readPixels(scrX, graphics.height - scrY - 1, 1, 1, RGBA, UNSIGNED_BYTE,
colorBuffer);
return colorBuffer.get();
}
@@ -1184,7 +1585,7 @@ protected float getDepthValue(int scrX, int scrY) {
depthBuffer = FloatBuffer.allocate(1);
}
depthBuffer.rewind();
- readPixels(scrX, pg.height - scrY - 1, 1, 1, DEPTH_COMPONENT, FLOAT,
+ readPixels(scrX, graphics.height - scrY - 1, 1, 1, DEPTH_COMPONENT, FLOAT,
depthBuffer);
return depthBuffer.get(0);
}
@@ -1194,18 +1595,22 @@ protected byte getStencilValue(int scrX, int scrY) {
if (stencilBuffer == null) {
stencilBuffer = ByteBuffer.allocate(1);
}
- readPixels(scrX, pg.height - scrY - 1, 1, 1, STENCIL_INDEX,
+ stencilBuffer.rewind();
+ readPixels(scrX, graphics.height - scrY - 1, 1, 1, STENCIL_INDEX,
UNSIGNED_BYTE, stencilBuffer);
return stencilBuffer.get(0);
}
+ protected static boolean isPowerOfTwo(int val) {
+ return (val & (val - 1)) == 0;
+ }
+
+
// bit shifting this might be more efficient
protected static int nextPowerOfTwo(int val) {
int ret = 1;
- while (ret < val) {
- ret <<= 1;
- }
+ while (ret < val) ret <<= 1;
return ret;
}
@@ -1216,12 +1621,10 @@ protected static int nextPowerOfTwo(int val) {
*/
protected static int nativeToJavaARGB(int color) {
if (BIG_ENDIAN) { // RGBA to ARGB
- return (color >>> 8) | ((color << 24) & 0xFF000000);
- // equivalent to
- // ((color >> 8) & 0x00FFFFFF) | ((color << 24) & 0xFF000000)
+ return (color >>> 8) | (color << 24);
} else { // ABGR to ARGB
- return ((color & 0xFF) << 16) | ((color & 0xFF0000) >> 16) |
- (color & 0xFF00FF00);
+ int rb = color & 0x00FF00FF;
+ return (color & 0xFF00FF00) | (rb << 16) | (rb >> 16);
}
}
@@ -1240,13 +1643,13 @@ protected static void nativeToJavaARGB(int[] pixels, int width, int height) {
int pixy = pixels[yindex];
int pixi = pixels[index];
if (BIG_ENDIAN) { // RGBA to ARGB
- pixels[index] = (pixy >>> 8) | ((pixy << 24) & 0xFF000000);
- pixels[yindex] = (pixi >>> 8) | ((pixi << 24) & 0xFF000000);
+ pixels[index] = (pixy >>> 8) | (pixy << 24);
+ pixels[yindex] = (pixi >>> 8) | (pixi << 24);
} else { // ABGR to ARGB
- pixels[index] = ((pixy & 0xFF) << 16) | ((pixy & 0xFF0000) >> 16) |
- (pixy & 0xFF00FF00);
- pixels[yindex] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) |
- (pixi & 0xFF00FF00);
+ int rbi = pixi & 0x00FF00FF;
+ int rby = pixy & 0x00FF00FF;
+ pixels[index] = (pixy & 0xFF00FF00) | (rby << 16) | (rby >> 16);
+ pixels[yindex] = (pixi & 0xFF00FF00) | (rbi << 16) | (rbi >> 16);
}
index++;
yindex++;
@@ -1259,10 +1662,10 @@ protected static void nativeToJavaARGB(int[] pixels, int width, int height) {
for (int x = 0; x < width; x++) {
int pixi = pixels[index];
if (BIG_ENDIAN) { // RGBA to ARGB
- pixels[index] = (pixi >>> 8) | ((pixi << 24) & 0xFF000000);
+ pixels[index] = (pixi >>> 8) | (pixi << 24);
} else { // ABGR to ARGB
- pixels[index] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) |
- (pixi & 0xFF00FF00);
+ int rbi = pixi & 0x00FF00FF;
+ pixels[index] = (pixi & 0xFF00FF00) | (rbi << 16) | (rbi >> 16);
}
index++;
}
@@ -1279,8 +1682,9 @@ protected static int nativeToJavaRGB(int color) {
if (BIG_ENDIAN) { // RGBA to ARGB
return (color >>> 8) | 0xFF000000;
} else { // ABGR to ARGB
- return ((color & 0xFF) << 16) | ((color & 0xFF0000) >> 16) |
- (color & 0xFF00FF00) | 0xFF000000;
+ int rb = color & 0x00FF00FF;
+ return 0xFF000000 | (rb << 16) |
+ (color & 0x0000FF00) | (rb >> 16);
}
}
@@ -1303,10 +1707,12 @@ protected static void nativeToJavaRGB(int[] pixels, int width, int height) {
pixels[index] = (pixy >>> 8) | 0xFF000000;
pixels[yindex] = (pixi >>> 8) | 0xFF000000;
} else { // ABGR to ARGB
- pixels[index] = ((pixy & 0xFF) << 16) | ((pixy & 0xFF0000) >> 16) |
- (pixy & 0xFF00FF00) | 0xFF000000;
- pixels[yindex] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) |
- (pixi & 0xFF00FF00) | 0xFF000000;
+ int rbi = pixi & 0x00FF00FF;
+ int rby = pixy & 0x00FF00FF;
+ pixels[index] = 0xFF000000 | (rby << 16) |
+ (pixy & 0x0000FF00) | (rby >> 16);
+ pixels[yindex] = 0xFF000000 | (rbi << 16) |
+ (pixi & 0x0000FF00) | (rbi >> 16);
}
index++;
yindex++;
@@ -1321,8 +1727,9 @@ protected static void nativeToJavaRGB(int[] pixels, int width, int height) {
if (BIG_ENDIAN) { // RGBA to ARGB
pixels[index] = (pixi >>> 8) | 0xFF000000;
} else { // ABGR to ARGB
- pixels[index] = ((pixi & 0xFF) << 16) | ((pixi & 0xFF0000) >> 16) |
- (pixi & 0xFF00FF00) | 0xFF000000;
+ int rbi = pixi & 0x00FF00FF;
+ pixels[index] = 0xFF000000 | (rbi << 16) |
+ (pixi & 0x000FF00) | (rbi >> 16);
}
index++;
}
@@ -1336,10 +1743,10 @@ protected static void nativeToJavaRGB(int[] pixels, int width, int height) {
*/
protected static int javaToNativeARGB(int color) {
if (BIG_ENDIAN) { // ARGB to RGBA
- return ((color >> 24) & 0xFF) | ((color << 8) & 0xFFFFFF00);
+ return (color >>> 24) | (color << 8);
} else { // ARGB to ABGR
- return (color & 0xFF000000) | ((color << 16) & 0xFF0000) |
- (color & 0xFF00) | ((color >> 16) & 0xFF);
+ int rb = color & 0x00FF00FF;
+ return (color & 0xFF00FF00) | (rb << 16) | (rb >> 16);
}
}
@@ -1358,13 +1765,13 @@ protected static void javaToNativeARGB(int[] pixels, int width, int height) {
int pixy = pixels[yindex];
int pixi = pixels[index];
if (BIG_ENDIAN) { // ARGB to RGBA
- pixels[index] = ((pixy >> 24) & 0xFF) | ((pixy << 8) & 0xFFFFFF00);
- pixels[yindex] = ((pixi >> 24) & 0xFF) | ((pixi << 8) & 0xFFFFFF00);
+ pixels[index] = (pixy >>> 24) | (pixy << 8);
+ pixels[yindex] = (pixi >>> 24) | (pixi << 8);
} else { // ARGB to ABGR
- pixels[index] = (pixy & 0xFF000000) | ((pixy << 16) & 0xFF0000) |
- (pixy & 0xFF00) | ((pixy >> 16) & 0xFF);
- pixels[yindex] = (pixi & 0xFF000000) | ((pixi << 16) & 0xFF0000) |
- (pixi & 0xFF00) | ((pixi >> 16) & 0xFF);
+ int rbi = pixi & 0x00FF00FF;
+ int rby = pixy & 0x00FF00FF;
+ pixels[index] = (pixy & 0xFF00FF00) | (rby << 16) | (rby >> 16);
+ pixels[yindex] = (pixi & 0xFF00FF00) | (rbi << 16) | (rbi >> 16);
}
index++;
yindex++;
@@ -1377,10 +1784,10 @@ protected static void javaToNativeARGB(int[] pixels, int width, int height) {
for (int x = 0; x < width; x++) {
int pixi = pixels[index];
if (BIG_ENDIAN) { // ARGB to RGBA
- pixels[index] = ((pixi >> 24) & 0xFF) | ((pixi << 8) & 0xFFFFFF00);
+ pixels[index] = (pixi >>> 24) | (pixi << 8);
} else { // ARGB to ABGR
- pixels[index] = (pixi & 0xFF000000) | ((pixi << 16) & 0xFF0000) |
- (pixi & 0xFF00) | ((pixi >> 16) & 0xFF);
+ int rbi = pixi & 0x00FF00FF;
+ pixels[index] = (pixi & 0xFF00FF00) | (rbi << 16) | (rbi >> 16);
}
index++;
}
@@ -1394,10 +1801,10 @@ protected static void javaToNativeARGB(int[] pixels, int width, int height) {
*/
protected static int javaToNativeRGB(int color) {
if (BIG_ENDIAN) { // ARGB to RGB
- return 0xFF | ((color << 8) & 0xFFFFFF00);
+ return 0xFF | (color << 8);
} else { // ARGB to BGR
- return 0xFF000000 | ((color << 16) & 0xFF0000) |
- (color & 0xFF00) | ((color >> 16) & 0xFF);
+ int rb = color & 0x00FF00FF;
+ return 0xFF000000 | (rb << 16) | (color & 0x0000FF00) | (rb >> 16);
}
}
@@ -1417,13 +1824,15 @@ protected static void javaToNativeRGB(int[] pixels, int width, int height) {
int pixy = pixels[yindex];
int pixi = pixels[index];
if (BIG_ENDIAN) { // ARGB to RGB
- pixels[index] = 0xFF | ((pixy << 8) & 0xFFFFFF00);
- pixels[yindex] = 0xFF | ((pixi << 8) & 0xFFFFFF00);
+ pixels[index] = 0xFF | (pixy << 8);
+ pixels[yindex] = 0xFF | (pixi << 8);
} else { // ARGB to BGR
- pixels[index] = 0xFF000000 | ((pixy << 16) & 0xFF0000) |
- (pixy & 0xFF00) | ((pixy >> 16) & 0xFF);
- pixels[yindex] = 0xFF000000 | ((pixi << 16) & 0xFF0000) |
- (pixi & 0xFF00) | ((pixi >> 16) & 0xFF);
+ int rbi = pixi & 0x00FF00FF;
+ int rby = pixy & 0x00FF00FF;
+ pixels[index] = 0xFF000000 | (rby << 16) |
+ (pixy & 0x0000FF00) | (rby >> 16);
+ pixels[yindex] = 0xFF000000 | (rbi << 16) |
+ (pixi & 0x0000FF00) | (rbi >> 16);
}
index++;
yindex++;
@@ -1436,10 +1845,11 @@ protected static void javaToNativeRGB(int[] pixels, int width, int height) {
for (int x = 0; x < width; x++) {
int pixi = pixels[index];
if (BIG_ENDIAN) { // ARGB to RGB
- pixels[index] = 0xFF | ((pixi << 8) & 0xFFFFFF00);
+ pixels[index] = 0xFF | (pixi << 8);
} else { // ARGB to BGR
- pixels[index] = 0xFF000000 | ((pixi << 16) & 0xFF0000) |
- (pixi & 0xFF00) | ((pixi >> 16) & 0xFF);
+ int rbi = pixi & 0x00FF00FF;
+ pixels[index] = 0xFF000000 | (rbi << 16) |
+ (pixi & 0x0000FF00) | (rbi >> 16);
}
index++;
}
@@ -1458,13 +1868,16 @@ protected static int qualityToSamples(int quality) {
}
+ abstract protected int getGLSLVersion();
+
+
protected String[] loadVertexShader(String filename) {
- return pg.parent.loadStrings(filename);
+ return sketch.loadStrings(filename);
}
protected String[] loadFragmentShader(String filename) {
- return pg.parent.loadStrings(filename);
+ return sketch.loadStrings(filename);
}
@@ -1508,43 +1921,119 @@ protected String[] loadVertexShader(URL url, int version) {
}
- protected static String[] convertFragmentSource(String[] fragSrc0,
- int version0, int version1) {
- if (version0 == 120 && version1 == 150) {
- String[] fragSrc = new String[fragSrc0.length + 2];
- fragSrc[0] = "#version 150";
- fragSrc[1] = "out vec4 fragColor;";
- for (int i = 0; i < fragSrc0.length; i++) {
- String line = fragSrc0[i];
- line = line.replace("varying", "in");
- line = line.replace("attribute", "in");
- line = line.replace("gl_FragColor", "fragColor");
- line = line.replace("texture", "texMap");
- line = line.replace("texMap2D(", "texture(");
- line = line.replace("texMap2DRect(", "texture(");
- fragSrc[i + 2] = line;
- }
- return fragSrc;
+ protected static String[] preprocessFragmentSource(String[] fragSrc0,
+ int version) {
+ if (containsVersionDirective(fragSrc0)) {
+ // The user knows what she or he is doing
+ return fragSrc0;
}
- return fragSrc0;
+
+ String[] fragSrc;
+
+ if (version < 130) {
+ Pattern[] search = { };
+ String[] replace = { };
+ int offset = 1;
+
+ fragSrc = preprocessShaderSource(fragSrc0, search, replace, offset);
+ fragSrc[0] = "#version " + version;
+ } else {
+ // We need to replace 'texture' uniform by 'texMap' uniform and
+ // 'textureXXX()' functions by 'texture()' functions. Order of these
+ // replacements is important to prevent collisions between these two.
+ Pattern[] search = new Pattern[] {
+ Pattern.compile(String.format(GLSL_ID_REGEX, "varying|attribute")),
+ Pattern.compile(String.format(GLSL_ID_REGEX, "texture")),
+ Pattern.compile(String.format(GLSL_FN_REGEX, "textureRect|texture2D|texture3D|textureCube")),
+ Pattern.compile(String.format(GLSL_ID_REGEX, "gl_FragColor"))
+ };
+ String[] replace = new String[] {
+ "in", "texMap", "texture", "_fragColor"
+ };
+ int offset = 2;
+
+ fragSrc = preprocessShaderSource(fragSrc0, search, replace, offset);
+ fragSrc[0] = "#version " + version;
+ fragSrc[1] = "out vec4 _fragColor;";
+ }
+
+ return fragSrc;
}
+ protected static String[] preprocessVertexSource(String[] vertSrc0,
+ int version) {
+ if (containsVersionDirective(vertSrc0)) {
+ // The user knows what she or he is doing
+ return vertSrc0;
+ }
+ String[] vertSrc;
- protected static String[] convertVertexSource(String[] vertSrc0,
- int version0, int version1) {
- if (version0 == 120 && version1 == 150) {
- String[] vertSrc = new String[vertSrc0.length + 1];
- vertSrc[0] = "#version 150";
- for (int i = 0; i < vertSrc0.length; i++) {
- String line = vertSrc0[i];
- line = line.replace("attribute", "in");
- line = line.replace("varying", "out");
- vertSrc[i + 1] = line;
+ if (version < 130) {
+ Pattern[] search = { };
+ String[] replace = { };
+ int offset = 1;
+
+ vertSrc = preprocessShaderSource(vertSrc0, search, replace, offset);
+ vertSrc[0] = "#version " + version;
+ } else {
+ // We need to replace 'texture' uniform by 'texMap' uniform and
+ // 'textureXXX()' functions by 'texture()' functions. Order of these
+ // replacements is important to prevent collisions between these two.
+ Pattern[] search = new Pattern[] {
+ Pattern.compile(String.format(GLSL_ID_REGEX, "varying")),
+ Pattern.compile(String.format(GLSL_ID_REGEX, "attribute")),
+ Pattern.compile(String.format(GLSL_ID_REGEX, "texture")),
+ Pattern.compile(String.format(GLSL_FN_REGEX, "textureRect|texture2D|texture3D|textureCube"))
+ };
+ String[] replace = new String[] {
+ "out", "in", "texMap", "texture",
+ };
+ int offset = 1;
+
+ vertSrc = preprocessShaderSource(vertSrc0, search, replace, offset);
+ vertSrc[0] = "#version " + version;
+ }
+
+ return vertSrc;
+ }
+
+
+ protected static final String GLSL_ID_REGEX = "(?= 0) {
+ line = line.substring(0, versionIndex);
}
- return vertSrc;
+ for (int j = 0; j < search.length; j++) {
+ line = search[j].matcher(line).replaceAll(replace[j]);
+ }
+ src[i+offset] = line;
}
- return vertSrc0;
+ return src;
+ }
+
+ protected static boolean containsVersionDirective(String[] shSrc) {
+ for (int i = 0; i < shSrc.length; i++) {
+ String line = shSrc[i];
+ int versionIndex = line.indexOf("#version");
+ if (versionIndex >= 0) {
+ int commentIndex = line.indexOf("//");
+ if (commentIndex < 0 || versionIndex < commentIndex) {
+ return true;
+ }
+ }
+ }
+ return false;
}
protected int createShader(int shaderType, String source) {
@@ -1620,9 +2109,19 @@ protected boolean validateFramebuffer() {
return false;
}
+ protected boolean isES() {
+ return getString(VERSION).trim().toLowerCase().contains("opengl es");
+ }
protected int[] getGLVersion() {
- String version = getString(VERSION).trim();
+ String version = getString(VERSION).trim().toLowerCase();
+
+ String ES = "opengl es";
+ int esPosition = version.indexOf(ES);
+ if (esPosition >= 0) {
+ version = version.substring(esPosition + ES.length()).trim();
+ }
+
int[] res = {0, 0, 0};
String[] parts = version.split(" ");
for (int i = 0; i < parts.length; i++) {
@@ -1735,6 +2234,42 @@ protected boolean hasAnisoSamplingSupport() {
}
+ protected boolean hasSynchronization() {
+ int[] version = getGLVersion();
+ if (isES()) {
+ return version[0] >= 3;
+ }
+ return (version[0] > 3) || (version[0] == 3 && version[1] >= 2);
+ }
+
+
+ protected boolean hasPBOs() {
+ int[] version = getGLVersion();
+ if (isES()) {
+ return version[0] >= 3;
+ }
+ return (version[0] > 2) || (version[0] == 2 && version[1] >= 1);
+ }
+
+
+ protected boolean hasReadBuffer() {
+ int[] version = getGLVersion();
+ if (isES()) {
+ return version[0] >= 3;
+ }
+ return version[0] >= 2;
+ }
+
+
+ protected boolean hasDrawBuffer() {
+ int[] version = getGLVersion();
+ if (isES()) {
+ return version[0] >= 3;
+ }
+ return version[0] >= 2;
+ }
+
+
protected int maxSamples() {
intBuffer.rewind();
getIntegerv(MAX_SAMPLES, intBuffer);
@@ -1766,7 +2301,10 @@ protected static ByteBuffer allocateByteBuffer(int size) {
protected static ByteBuffer allocateByteBuffer(byte[] arr) {
if (USE_DIRECT_BUFFERS) {
- return allocateDirectByteBuffer(arr.length);
+ ByteBuffer buf = allocateDirectByteBuffer(arr.length);
+ buf.put(arr);
+ buf.position(0);
+ return buf;
} else {
return ByteBuffer.wrap(arr);
}
@@ -1855,7 +2393,10 @@ protected static ShortBuffer allocateShortBuffer(int size) {
protected static ShortBuffer allocateShortBuffer(short[] arr) {
if (USE_DIRECT_BUFFERS) {
- return allocateDirectShortBuffer(arr.length);
+ ShortBuffer buf = allocateDirectShortBuffer(arr.length);
+ buf.put(arr);
+ buf.position(0);
+ return buf;
} else {
return ShortBuffer.wrap(arr);
}
@@ -1944,7 +2485,10 @@ protected static IntBuffer allocateIntBuffer(int size) {
protected static IntBuffer allocateIntBuffer(int[] arr) {
if (USE_DIRECT_BUFFERS) {
- return allocateDirectIntBuffer(arr.length);
+ IntBuffer buf = allocateDirectIntBuffer(arr.length);
+ buf.put(arr);
+ buf.position(0);
+ return buf;
} else {
return IntBuffer.wrap(arr);
}
@@ -2032,7 +2576,10 @@ protected static FloatBuffer allocateFloatBuffer(int size) {
protected static FloatBuffer allocateFloatBuffer(float[] arr) {
if (USE_DIRECT_BUFFERS) {
- return allocateDirectFloatBuffer(arr.length);
+ FloatBuffer buf = allocateDirectFloatBuffer(arr.length);
+ buf.put(arr);
+ buf.position(0);
+ return buf;
} else {
return FloatBuffer.wrap(arr);
}
@@ -2104,25 +2651,18 @@ protected static void fillFloatBuffer(FloatBuffer buf, int i0, int i1,
// TODO: the next three functions shouldn't be here...
+ // Uses 'Object' so that the API can be used w/ Android Typeface objects
- protected int getFontAscent(Object font) {
- return 0;
- }
+ abstract protected int getFontAscent(Object font);
- protected int getFontDescent(Object font) {
- return 0;
- }
+ abstract protected int getFontDescent(Object font);
- protected int getTextWidth(Object font, char buffer[], int start, int stop) {
- return 0;
- }
+ abstract protected int getTextWidth(Object font, char[] buffer, int start, int stop);
- protected Object getDerivedFont(Object font, float size) {
- return null;
- }
+ abstract protected Object getDerivedFont(Object font, float size);
///////////////////////////////////////////////////////////
@@ -2134,12 +2674,17 @@ protected Object getDerivedFont(Object font, float size) {
protected interface Tessellator {
+ public void setCallback(int flag);
+ public void setWindingRule(int rule);
+ public void setProperty(int property, int value);
+
public void beginPolygon();
+ public void beginPolygon(Object data);
public void endPolygon();
- public void setWindingRule(int rule);
public void beginContour();
public void endContour();
public void addVertex(double[] v);
+ public void addVertex(double[] v, int n, Object data);
}
@@ -2251,6 +2796,7 @@ protected interface FontOutline {
public static int TESS_WINDING_NONZERO;
public static int TESS_WINDING_ODD;
+ public static int TESS_EDGE_FLAG;
public static int GENERATE_MIPMAP_HINT;
public static int FASTEST;
@@ -2279,12 +2825,14 @@ protected interface FontOutline {
public static int ARRAY_BUFFER;
public static int ELEMENT_ARRAY_BUFFER;
+ public static int PIXEL_PACK_BUFFER;
public static int MAX_VERTEX_ATTRIBS;
public static int STATIC_DRAW;
public static int DYNAMIC_DRAW;
public static int STREAM_DRAW;
+ public static int STREAM_READ;
public static int BUFFER_SIZE;
public static int BUFFER_USAGE;
@@ -2441,7 +2989,6 @@ protected interface FontOutline {
public static int STENCIL_TEST;
public static int DEPTH_TEST;
public static int DEPTH_WRITEMASK;
- public static int ALPHA_TEST;
public static int COLOR_BUFFER_BIT;
public static int DEPTH_BUFFER_BIT;
@@ -2497,10 +3044,13 @@ protected interface FontOutline {
public static int RENDERBUFFER_INTERNAL_FORMAT;
public static int MULTISAMPLE;
- public static int POINT_SMOOTH;
public static int LINE_SMOOTH;
public static int POLYGON_SMOOTH;
+ public static int SYNC_GPU_COMMANDS_COMPLETE;
+ public static int ALREADY_SIGNALED;
+ public static int CONDITION_SATISFIED;
+
///////////////////////////////////////////////////////////
// Special Functions
@@ -2545,10 +3095,19 @@ protected interface FontOutline {
//////////////////////////////////////////////////////////////////////////////
+ // Synchronization
+
+ public abstract long fenceSync(int condition, int flags);
+ public abstract void deleteSync(long sync);
+ public abstract int clientWaitSync(long sync, int flags, long timeout);
+
+ //////////////////////////////////////////////////////////////////////////////
+
// Viewport and Clipping
public abstract void depthRangef(float n, float f);
public abstract void viewport(int x, int y, int w, int h);
+ protected abstract void viewportImpl(int x, int y, int w, int h);
//////////////////////////////////////////////////////////////////////////////
@@ -2559,14 +3118,37 @@ protected interface FontOutline {
// to glReadPixels() should be done in readPixelsImpl().
public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer){
- boolean pgCall = format != STENCIL_INDEX &&
- format != DEPTH_COMPONENT && format != DEPTH_STENCIL;
- if (pgCall) pg.beginReadPixels();
+ boolean multisampled = isMultisampled() || graphics.offscreenMultisample;
+ boolean depthReadingEnabled = graphics.getHint(PConstants.ENABLE_BUFFER_READING);
+ boolean depthRequested = format == STENCIL_INDEX || format == DEPTH_COMPONENT || format == DEPTH_STENCIL;
+
+ if (multisampled && depthRequested && !depthReadingEnabled) {
+ PGraphics.showWarning(DEPTH_READING_NOT_ENABLED_ERROR);
+ return;
+ }
+
+ graphics.beginReadPixels();
readPixelsImpl(x, y, width, height, format, type, buffer);
- if (pgCall) pg.endReadPixels();
+ graphics.endReadPixels();
+ }
+
+ public void readPixels(int x, int y, int width, int height, int format, int type, long offset){
+ boolean multisampled = isMultisampled() || graphics.offscreenMultisample;
+ boolean depthReadingEnabled = graphics.getHint(PConstants.ENABLE_BUFFER_READING);
+ boolean depthRequested = format == STENCIL_INDEX || format == DEPTH_COMPONENT || format == DEPTH_STENCIL;
+
+ if (multisampled && depthRequested && !depthReadingEnabled) {
+ PGraphics.showWarning(DEPTH_READING_NOT_ENABLED_ERROR);
+ return;
+ }
+
+ graphics.beginReadPixels();
+ readPixelsImpl(x, y, width, height, format, type, offset);
+ graphics.endReadPixels();
}
protected abstract void readPixelsImpl(int x, int y, int width, int height, int format, int type, Buffer buffer);
+ protected abstract void readPixelsImpl(int x, int y, int width, int height, int format, int type, long offset);
//////////////////////////////////////////////////////////////////////////////
@@ -2579,14 +3161,24 @@ public void readPixels(int x, int y, int width, int height, int format, int type
public abstract void vertexAttrib1fv(int index, FloatBuffer values);
public abstract void vertexAttrib2fv(int index, FloatBuffer values);
public abstract void vertexAttrib3fv(int index, FloatBuffer values);
- public abstract void vertexAttri4fv(int index, FloatBuffer values);
+ public abstract void vertexAttrib4fv(int index, FloatBuffer values);
public abstract void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, int offset);
- public abstract void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer data);
public abstract void enableVertexAttribArray(int index);
public abstract void disableVertexAttribArray(int index);
- public abstract void drawArrays(int mode, int first, int count);
- public abstract void drawElements(int mode, int count, int type, int offset);
- public abstract void drawElements(int mode, int count, int type, Buffer indices);
+
+ public void drawArrays(int mode, int first, int count) {
+ geomCount += count;
+ drawArraysImpl(mode, first, count);
+ }
+
+ public abstract void drawArraysImpl(int mode, int first, int count);
+
+ public void drawElements(int mode, int count, int type, int offset) {
+ geomCount += count;
+ drawElementsImpl(mode, count, type, offset);
+ }
+
+ public abstract void drawElementsImpl(int mode, int count, int type, int offset);
//////////////////////////////////////////////////////////////////////////////
@@ -2726,7 +3318,6 @@ public void bindTexture(int target, int texture) {
public abstract void blendFunc(int src, int dst);
public abstract void blendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);
public abstract void blendColor(float red, float green, float blue, float alpha);
- public abstract void alphaFunc(int func, float ref);
///////////////////////////////////////////////////////////
@@ -2736,19 +3327,19 @@ public void bindTexture(int target, int texture) {
public abstract void depthMask(boolean mask);
public abstract void stencilMask(int mask);
public abstract void stencilMaskSeparate(int face, int mask);
- public abstract void clear(int buf);
public abstract void clearColor(float r, float g, float b, float a);
public abstract void clearDepth(float d);
public abstract void clearStencil(int s);
+ public abstract void clear(int buf);
///////////////////////////////////////////////////////////
// Framebuffers Objects
public void bindFramebuffer(int target, int framebuffer) {
- pg.beginBindFramebuffer(target, framebuffer);
+ graphics.beginBindFramebuffer(target, framebuffer);
bindFramebufferImpl(target, framebuffer);
- pg.endBindFramebuffer(target, framebuffer);
+ graphics.endBindFramebuffer(target, framebuffer);
}
protected abstract void bindFramebufferImpl(int target, int framebuffer);
diff --git a/core/src/processing/opengl/PGLES.java b/libs/processing-core/src/main/java/processing/opengl/PGLES.java
similarity index 70%
rename from core/src/processing/opengl/PGLES.java
rename to libs/processing-core/src/main/java/processing/opengl/PGLES.java
index 0a33a60be..e31799170 100644
--- a/core/src/processing/opengl/PGLES.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PGLES.java
@@ -1,3 +1,27 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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, version 2.1.
+
+ 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.opengl;
import java.nio.Buffer;
@@ -6,17 +30,14 @@
import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
-import android.opengl.GLSurfaceView.EGLConfigChooser;
-import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;
-import processing.core.PApplet;
+import android.view.SurfaceView;
+
import processing.opengl.tess.PGLU;
import processing.opengl.tess.PGLUtessellator;
import processing.opengl.tess.PGLUtessellatorCallbackAdapter;
@@ -33,19 +54,14 @@ public class PGLES extends PGL {
public PGLU glu;
/** The current opengl context */
- public static EGLContext context;
+ public EGLContext context;
/** The current surface view */
- public static GLSurfaceView glview;
-
- // ........................................................
+ public GLSurfaceView glview;
- // Internal objects
- /** The renderer object driving the rendering loop, analogous to the
- * GLEventListener in JOGL */
- protected static AndroidRenderer renderer;
- protected static AndroidConfigChooser configChooser;
+ /** Requested major version of the OpenGL ES context */
+ static public int version = 2;
// ........................................................
@@ -53,11 +69,12 @@ public class PGLES extends PGL {
// GLES
static {
+ SINGLE_BUFFERED = true;
+
MIN_DIRECT_BUFFER_SIZE = 1;
INDEX_TYPE = GLES20.GL_UNSIGNED_SHORT;
- SAVE_SURFACE_TO_PIXELS_HACK = false;
- MIPMAPS_ENABLED = false;
+ MIPMAPS_ENABLED = false;
DEFAULT_IN_VERTICES = 16;
DEFAULT_IN_EDGES = 32;
@@ -72,17 +89,17 @@ public class PGLES extends PGL {
}
// Some EGL constants needed to initialize a GLES2 context.
- protected static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
- protected static final int EGL_OPENGL_ES2_BIT = 0x0004;
+ public static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ public static final int EGL_OPENGL_ES2_BIT = 0x0004;
// Coverage multisampling identifiers for nVidia Tegra2
- protected static final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
- protected static final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;
- protected static final int GL_COVERAGE_BUFFER_BIT_NV = 0x8000;
+ public static final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
+ public static final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;
+ public static final int GL_COVERAGE_BUFFER_BIT_NV = 0x8000;
- protected static boolean usingMultisampling = false;
- protected static boolean usingCoverageMultisampling = false;
- protected static int multisampleCount = 1;
+ public static boolean usingMultisampling = false;
+ public static boolean usingCoverageMultisampling = false;
+ public static int multisampleCount = 1;
///////////////////////////////////////////////////////////
@@ -96,26 +113,27 @@ public PGLES(PGraphicsOpenGL pg) {
@Override
- public GLSurfaceView getCanvas() {
+ public GLSurfaceView getNative() {
return glview;
}
@Override
- protected void setFps(float fps) { }
+ public void queueEvent(Runnable runnable) {
+ if (glview != null) {
+ glview.queueEvent(runnable);
+ }
+ }
@Override
protected void initSurface(int antialias) {
- glview = (GLSurfaceView)pg.parent.getSurfaceView();
+ SurfaceView surf = sketch.getSurface().getSurfaceView();
+ if (surf != null) {
+ glview = (GLSurfaceView)surf;
+ }
reqNumSamples = qualityToSamples(antialias);
-
registerListeners();
-
- fboLayerCreated = false;
- fboLayerInUse = false;
- firstFrame = true;
- setFps = false;
}
@@ -127,412 +145,124 @@ protected void reinitSurface() { }
protected void registerListeners() { }
- ///////////////////////////////////////////////////////////
-
- // Frame rendering
-
-
@Override
- protected void getGL(PGL pgl) {
- PGLES pgles = (PGLES)pgl;
- this.gl = pgles.gl;
+ protected int getDepthBits() {
+ intBuffer.rewind();
+ getIntegerv(DEPTH_BITS, intBuffer);
+ return intBuffer.get(0);
}
@Override
- protected boolean canDraw() {
- return true;
+ protected int getStencilBits() {
+ intBuffer.rewind();
+ getIntegerv(STENCIL_BITS, intBuffer);
+ return intBuffer.get(0);
}
@Override
- protected void requestFocus() { }
+ protected int getDefaultDrawBuffer() {
+ return fboLayerEnabled ? COLOR_ATTACHMENT0 : FRONT;
+ }
@Override
- protected void requestDraw() {
- if (pg.initialized && pg.parent.canDraw()) {
- glview.requestRender();
- }
+ protected int getDefaultReadBuffer() {
+ return fboLayerEnabled ? COLOR_ATTACHMENT0 : FRONT;
}
- @Override
- protected void swapBuffers() { }
-
+ public void init(GL10 igl) {
+ gl = igl;
+ context = ((EGL10)EGLContext.getEGL()).eglGetCurrentContext();
+ glContext = context.hashCode();
+ glThread = Thread.currentThread();
- ///////////////////////////////////////////////////////////
+ if (!hasFBOs()) {
+ throw new RuntimeException(PGL.MISSING_FBO_ERROR);
+ }
+ if (!hasShaders()) {
+ throw new RuntimeException(PGL.MISSING_GLSL_ERROR);
+ }
+ }
- // Android specific classes (Renderer, ConfigChooser)
+ ///////////////////////////////////////////////////////////
- public AndroidRenderer getRenderer() {
- renderer = new AndroidRenderer();
- return renderer;
- }
+ // Frame rendering
- public AndroidContextFactory getContextFactory() {
- return new AndroidContextFactory();
+ @Override
+ protected float getPixelScale() {
+ return 1;
}
- public AndroidConfigChooser getConfigChooser(int samples) {
- configChooser = new AndroidConfigChooser(5, 6, 5, 4, 16, 1, samples);
- return configChooser;
+ @Override
+ protected void getGL(PGL pgl) {
+ PGLES pgles = (PGLES)pgl;
+ this.gl = pgles.gl;
+ setThread(pgles.glThread);
}
-
- public AndroidConfigChooser getConfigChooser(int r, int g, int b, int a,
- int d, int s, int samples) {
- configChooser = new AndroidConfigChooser(r, g, b, a, d, s, samples);
- return configChooser;
+ public void getGL(GL10 igl) {
+ gl = igl;
+ glThread = Thread.currentThread();
}
- protected class AndroidRenderer implements Renderer {
- public AndroidRenderer() {
- }
-
- public void onDrawFrame(GL10 igl) {
- gl = igl;
- glThread = Thread.currentThread();
- pg.parent.handleDraw();
- }
+ @Override
+ protected boolean canDraw() { return true; }
- public void onSurfaceChanged(GL10 igl, int iwidth, int iheight) {
- gl = igl;
- // Here is where we should initialize native libs...
- // lib.init(iwidth, iheight);
+ @Override
+ protected void requestFocus() { }
- pg.setSize(iwidth, iheight);
- }
- public void onSurfaceCreated(GL10 igl, EGLConfig config) {
- gl = igl;
- context = ((EGL10)EGLContext.getEGL()).eglGetCurrentContext();
- glContext = context.hashCode();
+ @Override
+ protected void requestDraw() { }
- if (!hasFBOs()) {
- throw new RuntimeException(MISSING_FBO_ERROR);
- }
- if (!hasShaders()) {
- throw new RuntimeException(MISSING_GLSL_ERROR);
- }
- }
- }
+ @Override
+ protected void swapBuffers() { }
- protected class AndroidContextFactory implements
- GLSurfaceView.EGLContextFactory {
- public EGLContext createContext(EGL10 egl, EGLDisplay display,
- EGLConfig eglConfig) {
- int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL10.EGL_NONE };
- EGLContext context = egl.eglCreateContext(display, eglConfig,
- EGL10.EGL_NO_CONTEXT,
- attrib_list);
- return context;
- }
- public void destroyContext(EGL10 egl, EGLDisplay display,
- EGLContext context) {
- egl.eglDestroyContext(display, context);
- }
+ @Override
+ protected int getGLSLVersion() {
+ return 100;
}
- protected class AndroidConfigChooser implements EGLConfigChooser {
- // Desired size (in bits) for the rgba color, depth and stencil buffers.
- public int redTarget;
- public int greenTarget;
- public int blueTarget;
- public int alphaTarget;
- public int depthTarget;
- public int stencilTarget;
-
- // Actual rgba color, depth and stencil sizes (in bits) supported by the
- // device.
- public int redBits;
- public int greenBits;
- public int blueBits;
- public int alphaBits;
- public int depthBits;
- public int stencilBits;
- public int[] tempValue = new int[1];
-
- public int numSamples;
-
- /*
- The GLES2 extensions supported are:
- GL_OES_rgb8_rgba8 GL_OES_depth24 GL_OES_vertex_half_float
- GL_OES_texture_float GL_OES_texture_half_float
- GL_OES_element_index_uint GL_OES_mapbuffer
- GL_OES_fragment_precision_high GL_OES_compressed_ETC1_RGB8_texture
- GL_OES_EGL_image GL_OES_required_internalformat GL_OES_depth_texture
- GL_OES_get_program_binary GL_OES_packed_depth_stencil
- GL_OES_standard_derivatives GL_OES_vertex_array_object GL_OES_egl_sync
- GL_EXT_multi_draw_arrays GL_EXT_texture_format_BGRA8888
- GL_EXT_discard_framebuffer GL_EXT_shader_texture_lod
- GL_IMG_shader_binary GL_IMG_texture_compression_pvrtc
- GL_IMG_texture_stream2 GL_IMG_texture_npot
- GL_IMG_texture_format_BGRA8888 GL_IMG_read_format
- GL_IMG_program_binary GL_IMG_multisampled_render_to_texture
- */
-
- /*
- // The attributes we want in the frame buffer configuration for Processing.
- // For more details on other attributes, see:
- // http://www.khronos.org/opengles/documentation/opengles1_0/html/eglChooseConfig.html
- protected int[] configAttribsGL_MSAA = {
- EGL10.EGL_RED_SIZE, 5,
- EGL10.EGL_GREEN_SIZE, 6,
- EGL10.EGL_BLUE_SIZE, 5,
- EGL10.EGL_ALPHA_SIZE, 4,
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_SAMPLE_BUFFERS, 1,
- EGL10.EGL_SAMPLES, 2,
- EGL10.EGL_NONE };
-
- protected int[] configAttribsGL_CovMSAA = {
- EGL10.EGL_RED_SIZE, 5,
- EGL10.EGL_GREEN_SIZE, 6,
- EGL10.EGL_BLUE_SIZE, 5,
- EGL10.EGL_ALPHA_SIZE, 4,
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_COVERAGE_BUFFERS_NV, 1,
- EGL_COVERAGE_SAMPLES_NV, 2,
- EGL10.EGL_NONE };
-
- protected int[] configAttribsGL_NoMSAA = {
- EGL10.EGL_RED_SIZE, 5,
- EGL10.EGL_GREEN_SIZE, 6,
- EGL10.EGL_BLUE_SIZE, 5,
- EGL10.EGL_ALPHA_SIZE, 4,
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_NONE };
-
- protected int[] configAttribsGL_Good = {
- EGL10.EGL_RED_SIZE, 8,
- EGL10.EGL_GREEN_SIZE, 8,
- EGL10.EGL_BLUE_SIZE, 8,
- EGL10.EGL_ALPHA_SIZE, 8,
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_NONE };
-
- protected int[] configAttribsGL_TestMSAA = {
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_SAMPLE_BUFFERS, 1,
- EGL10.EGL_SAMPLES, 2,
- EGL10.EGL_NONE };
- */
-
- protected int[] attribsNoMSAA = {
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_SAMPLE_BUFFERS, 0,
- EGL10.EGL_NONE };
-
- public AndroidConfigChooser(int rbits, int gbits, int bbits, int abits,
- int dbits, int sbits, int samples) {
- redTarget = rbits;
- greenTarget = gbits;
- blueTarget = bbits;
- alphaTarget = abits;
- depthTarget = dbits;
- stencilTarget = sbits;
- numSamples = samples;
- }
-
- public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
- EGLConfig[] configs = null;
- if (1 < numSamples) {
- int[] attribs = new int[] {
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL10.EGL_SAMPLE_BUFFERS, 1,
- EGL10.EGL_SAMPLES, numSamples,
- EGL10.EGL_NONE };
- configs = chooseConfigWithAttribs(egl, display, attribs);
- if (configs == null) {
- // No normal multisampling config was found. Try to create a
- // coverage multisampling configuration, for the nVidia Tegra2.
- // See the EGL_NV_coverage_sample documentation.
- int[] attribsCov = {
- EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_COVERAGE_BUFFERS_NV, 1,
- EGL_COVERAGE_SAMPLES_NV, numSamples,
- EGL10.EGL_NONE };
- configs = chooseConfigWithAttribs(egl, display, attribsCov);
- if (configs == null) {
- configs = chooseConfigWithAttribs(egl, display, attribsNoMSAA);
- } else {
- usingMultisampling = true;
- usingCoverageMultisampling = true;
- multisampleCount = numSamples;
- }
- } else {
- usingMultisampling = true;
- usingCoverageMultisampling = false;
- multisampleCount = numSamples;
- }
- } else {
- configs = chooseConfigWithAttribs(egl, display, attribsNoMSAA);
- }
-
- if (configs == null) {
- throw new IllegalArgumentException("No EGL configs match configSpec");
- }
-
- if (PApplet.DEBUG) {
- for (EGLConfig config : configs) {
- String configStr = "P3D - selected EGL config : "
- + printConfig(egl, display, config);
- System.out.println(configStr);
- }
- }
-
- // Now return the configuration that best matches the target one.
- return chooseBestConfig(egl, display, configs);
- }
+ @Override
+ protected void initFBOLayer() {
+ if (0 < sketch.frameCount) {
+ IntBuffer buf = allocateDirectIntBuffer(fboWidth * fboHeight);
- public EGLConfig chooseBestConfig(EGL10 egl, EGLDisplay display,
- EGLConfig[] configs) {
- EGLConfig bestConfig = null;
- float bestScore = Float.MAX_VALUE;
-
- for (EGLConfig config : configs) {
- int gl = findConfigAttrib(egl, display, config,
- EGL10.EGL_RENDERABLE_TYPE, 0);
- boolean isGLES2 = (gl & EGL_OPENGL_ES2_BIT) != 0;
- if (isGLES2) {
- int d = findConfigAttrib(egl, display, config,
- EGL10.EGL_DEPTH_SIZE, 0);
- int s = findConfigAttrib(egl, display, config,
- EGL10.EGL_STENCIL_SIZE, 0);
-
- int r = findConfigAttrib(egl, display, config,
- EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config,
- EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config,
- EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config,
- EGL10.EGL_ALPHA_SIZE, 0);
-
- float score = 0.20f * PApplet.abs(r - redTarget) +
- 0.20f * PApplet.abs(g - greenTarget) +
- 0.20f * PApplet.abs(b - blueTarget) +
- 0.15f * PApplet.abs(a - alphaTarget) +
- 0.15f * PApplet.abs(d - depthTarget) +
- 0.10f * PApplet.abs(s - stencilTarget);
-
- if (score < bestScore) {
- // We look for the config closest to the target config.
- // Closeness is measured by the score function defined above:
- // we give more weight to the RGB components, followed by the
- // alpha, depth and finally stencil bits.
- bestConfig = config;
- bestScore = score;
-
- redBits = r;
- greenBits = g;
- blueBits = b;
- alphaBits = a;
- depthBits = d;
- stencilBits = s;
- }
- }
- }
+ if (hasReadBuffer()) readBuffer(BACK);
+ readPixelsImpl(0, 0, fboWidth, fboHeight, RGBA, UNSIGNED_BYTE, buf);
+ bindTexture(TEXTURE_2D, glColorTex.get(frontTex));
+ texSubImage2D(TEXTURE_2D, 0, 0, 0, fboWidth, fboHeight, RGBA, UNSIGNED_BYTE, buf);
- if (PApplet.DEBUG) {
- String configStr = "P3D - selected EGL config : "
- + printConfig(egl, display, bestConfig);
- System.out.println(configStr);
- }
- return bestConfig;
- }
+ bindTexture(TEXTURE_2D, glColorTex.get(backTex));
+ texSubImage2D(TEXTURE_2D, 0, 0, 0, fboWidth, fboHeight, RGBA, UNSIGNED_BYTE, buf);
- protected String printConfig(EGL10 egl, EGLDisplay display,
- EGLConfig config) {
- int r = findConfigAttrib(egl, display, config,
- EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config,
- EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config,
- EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config,
- EGL10.EGL_ALPHA_SIZE, 0);
- int d = findConfigAttrib(egl, display, config,
- EGL10.EGL_DEPTH_SIZE, 0);
- int s = findConfigAttrib(egl, display, config,
- EGL10.EGL_STENCIL_SIZE, 0);
- int type = findConfigAttrib(egl, display, config,
- EGL10.EGL_RENDERABLE_TYPE, 0);
- int nat = findConfigAttrib(egl, display, config,
- EGL10.EGL_NATIVE_RENDERABLE, 0);
- int bufSize = findConfigAttrib(egl, display, config,
- EGL10.EGL_BUFFER_SIZE, 0);
- int bufSurf = findConfigAttrib(egl, display, config,
- EGL10.EGL_RENDER_BUFFER, 0);
-
- return String.format("EGLConfig rgba=%d%d%d%d depth=%d stencil=%d",
- r,g,b,a,d,s)
- + " type=" + type
- + " native=" + nat
- + " buffer size=" + bufSize
- + " buffer surface=" + bufSurf +
- String.format(" caveat=0x%04x",
- findConfigAttrib(egl, display, config,
- EGL10.EGL_CONFIG_CAVEAT, 0));
+ bindTexture(TEXTURE_2D, 0);
+ bindFramebufferImpl(FRAMEBUFFER, 0);
}
+ }
- protected int findConfigAttrib(EGL10 egl, EGLDisplay display,
- EGLConfig config, int attribute, int defaultValue) {
- if (egl.eglGetConfigAttrib(display, config, attribute, tempValue)) {
- return tempValue[0];
- }
- return defaultValue;
- }
-
- protected EGLConfig[] chooseConfigWithAttribs(EGL10 egl,
- EGLDisplay display,
- int[] configAttribs) {
- // Get the number of minimally matching EGL configurations
- int[] configCounts = new int[1];
- egl.eglChooseConfig(display, configAttribs, null, 0, configCounts);
-
- int count = configCounts[0];
-
- if (count <= 0) {
- //throw new IllegalArgumentException("No EGL configs match configSpec");
- return null;
- }
-
- // Allocate then read the array of minimally matching EGL configs
- EGLConfig[] configs = new EGLConfig[count];
- egl.eglChooseConfig(display, configAttribs, configs, count, configCounts);
- return configs;
-
- // Get the number of minimally matching EGL configurations
-// int[] num_config = new int[1];
-// egl.eglChooseConfig(display, configAttribsGL, null, 0, num_config);
-//
-// int numConfigs = num_config[0];
-//
-// if (numConfigs <= 0) {
-// throw new IllegalArgumentException("No EGL configs match configSpec");
-// }
-//
-// // Allocate then read the array of minimally matching EGL configs
-// EGLConfig[] configs = new EGLConfig[numConfigs];
-// egl.eglChooseConfig(display, configAttribsGL, configs, numConfigs,
-// num_config);
- }
+ @Override
+ protected void clearFrontColorBuffer() {
+ // Need to front clear color buffer, otherwise one can lead to the screen not clearning
+ // properly in sketches that do not call background() continously in draw() but only at
+ // specific events.
+ framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0,
+ TEXTURE_2D, glColorTex.get(frontTex), 0);
+ clear(COLOR_BUFFER_BIT);
+ framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0,
+ TEXTURE_2D, glColorTex.get(backTex), 0);
}
@@ -564,28 +294,53 @@ public Tessellator(TessellatorCallback callback) {
PGLU.gluTessCallback(tess, PGLU.GLU_TESS_ERROR, gluCallback);
}
+ @Override
+ public void setCallback(int flag) {
+ PGLU.gluTessCallback(tess, flag, gluCallback);
+ }
+
+ @Override
+ public void setWindingRule(int rule) {
+ setProperty(PGLU.GLU_TESS_WINDING_RULE, rule);
+ }
+
+ public void setProperty(int property, int value) {
+ PGLU.gluTessProperty(tess, property, value);
+ }
+
+ @Override
public void beginPolygon() {
- PGLU.gluTessBeginPolygon(tess, null);
+ beginPolygon(null);
}
- public void endPolygon() {
- PGLU.gluTessEndPolygon(tess);
+ @Override
+ public void beginPolygon(Object data) {
+ PGLU.gluTessBeginPolygon(tess, data);
}
- public void setWindingRule(int rule) {
- PGLU.gluTessProperty(tess, PGLU.GLU_TESS_WINDING_RULE, rule);
+ @Override
+ public void endPolygon() {
+ PGLU.gluTessEndPolygon(tess);
}
+ @Override
public void beginContour() {
PGLU.gluTessBeginContour(tess);
}
+ @Override
public void endContour() {
PGLU.gluTessEndContour(tess);
}
+ @Override
public void addVertex(double[] v) {
- PGLU.gluTessVertex(tess, v, 0, v);
+ addVertex(v, 0, v);
+ }
+
+ @Override
+ public void addVertex(double[] v, int n, Object data) {
+ PGLU.gluTessVertex(tess, v, n, data);
}
protected class GLUCallback extends PGLUtessellatorCallbackAdapter {
@@ -685,6 +440,7 @@ protected FontOutline createFontOutline(char ch, Object font) {
TESS_WINDING_NONZERO = PGLU.GLU_TESS_WINDING_NONZERO;
TESS_WINDING_ODD = PGLU.GLU_TESS_WINDING_ODD;
+ TESS_EDGE_FLAG = PGLU.GLU_TESS_EDGE_FLAG;
GENERATE_MIPMAP_HINT = GLES20.GL_GENERATE_MIPMAP_HINT;
FASTEST = GLES20.GL_FASTEST;
@@ -875,7 +631,6 @@ protected FontOutline createFontOutline(char ch, Object font) {
STENCIL_TEST = GLES20.GL_STENCIL_TEST;
DEPTH_TEST = GLES20.GL_DEPTH_TEST;
DEPTH_WRITEMASK = GLES20.GL_DEPTH_WRITEMASK;
- ALPHA_TEST = 0x0BC0;
COLOR_BUFFER_BIT = GLES20.GL_COLOR_BUFFER_BIT;
DEPTH_BUFFER_BIT = GLES20.GL_DEPTH_BUFFER_BIT;
@@ -899,7 +654,7 @@ protected FontOutline createFontOutline(char ch, Object font) {
DEPTH_COMPONENT24 = 0x81A6;
DEPTH_COMPONENT32 = 0x81A7;
- STENCIL_INDEX = GLES20.GL_STENCIL_INDEX;
+ STENCIL_INDEX = 6401; // GLES20.GL_STENCIL_INDEX is marked as deprecated
STENCIL_INDEX1 = 0x8D46;
STENCIL_INDEX4 = 0x8D47;
STENCIL_INDEX8 = GLES20.GL_STENCIL_INDEX8;
@@ -931,7 +686,6 @@ protected FontOutline createFontOutline(char ch, Object font) {
RENDERBUFFER_INTERNAL_FORMAT = GLES20.GL_RENDERBUFFER_INTERNAL_FORMAT;
MULTISAMPLE = -1;
- POINT_SMOOTH = -1;
LINE_SMOOTH = -1;
POLYGON_SMOOTH = -1;
}
@@ -1089,9 +843,16 @@ public void depthRangef(float n, float f) {
@Override
public void viewport(int x, int y, int w, int h) {
+ float scale = getPixelScale();
+ viewportImpl((int)scale * x, (int)(scale * y), (int)(scale * w), (int)(scale * h));
+ }
+
+ @Override
+ protected void viewportImpl(int x, int y, int w, int h) {
GLES20.glViewport(x, y, w, h);
}
+
//////////////////////////////////////////////////////////////////////////////
// Reading Pixels
@@ -1101,6 +862,14 @@ public void readPixelsImpl(int x, int y, int width, int height, int format, int
GLES20.glReadPixels(x, y, width, height, format, type, buffer);
}
+
+ @Override
+ protected void readPixelsImpl(int x, int y, int width, int height, int format,
+ int type, long offset) {
+ // TODO Auto-generated method stub
+ }
+
+
//////////////////////////////////////////////////////////////////////////////
// Vertices
@@ -1141,7 +910,7 @@ public void vertexAttrib3fv(int index, FloatBuffer values) {
}
@Override
- public void vertexAttri4fv(int index, FloatBuffer values) {
+ public void vertexAttrib4fv(int index, FloatBuffer values) {
GLES20.glVertexAttrib4fv(index, values);
}
@@ -1150,11 +919,6 @@ public void vertexAttribPointer(int index, int size, int type, boolean normalize
GLES20.glVertexAttribPointer(index, size, type, normalized, stride, offset);
}
- @Override
- public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer data) {
- GLES20.glVertexAttribPointer(index, size, type, normalized, stride, data);
- }
-
@Override
public void enableVertexAttribArray(int index) {
GLES20.glEnableVertexAttribArray(index);
@@ -1166,20 +930,15 @@ public void disableVertexAttribArray(int index) {
}
@Override
- public void drawArrays(int mode, int first, int count) {
+ public void drawArraysImpl(int mode, int first, int count) {
GLES20.glDrawArrays(mode, first, count);
}
@Override
- public void drawElements(int mode, int count, int type, int offset) {
+ public void drawElementsImpl(int mode, int count, int type, int offset) {
GLES20.glDrawElements(mode, count, type, offset);
}
- @Override
- public void drawElements(int mode, int count, int type, Buffer indices) {
- GLES20.glDrawElements(mode, count, type, indices);
- }
-
//////////////////////////////////////////////////////////////////////////////
// Rasterization
@@ -1254,7 +1013,7 @@ public void texParameteri(int target, int pname, int param) {
@Override
public void texParameterf(int target, int pname, float param) {
- gl.glTexParameterf(target, pname, param);
+ GLES20.glTexParameterf(target, pname, param);
}
@Override
@@ -1645,11 +1404,6 @@ public void blendColor(float red, float green, float blue, float alpha) {
GLES20.glBlendColor(red, green, blue, alpha);
}
- @Override
- public void alphaFunc(int func, float ref) {
- throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glAlphaFunc()"));
- }
-
///////////////////////////////////////////////////////////
// Whole Framebuffer Operations
@@ -1773,21 +1527,83 @@ public void getRenderbufferParameteriv(int target, int pname, IntBuffer params)
@Override
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
- throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glBlitFramebuffer()"));
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glBlitFramebuffer()"));
}
@Override
public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height) {
- throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glRenderbufferStorageMultisample()"));
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glRenderbufferStorageMultisample()"));
}
@Override
public void readBuffer(int buf) {
- throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glReadBuffer()"));
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glReadBuffer()"));
}
@Override
public void drawBuffer(int buf) {
- throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glDrawBuffer()"));
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glDrawBuffer()"));
+ }
+
+
+ @Override
+ protected int getFontAscent(Object font) {
+ // TODO Auto-generated method stub
+ return 0;
}
+
+
+ @Override
+ protected int getFontDescent(Object font) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+ @Override
+ protected int getTextWidth(Object font, char[] buffer, int start, int stop) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+ @Override
+ protected Object getDerivedFont(Object font, float size) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Synchronization
+
+ @Override
+ public long fenceSync(int condition, int flags) {
+ return 0;
+// if (gl3es3 != null) {
+// return gl3es3.glFenceSync(condition, flags);
+// } else {
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "fenceSync()"));
+// }
+ }
+
+ @Override
+ public void deleteSync(long sync) {
+// if (gl3es3 != null) {
+// gl3es3.glDeleteSync(sync);
+// } else {
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "deleteSync()"));
+// }
+ }
+
+ @Override
+ public int clientWaitSync(long sync, int flags, long timeout) {
+ return 0;
+// if (gl3es3 != null) {
+// return gl3es3.glClientWaitSync(sync, flags, timeout);
+// } else {
+// throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "clientWaitSync()"));
+// }
+ }
+
}
diff --git a/core/src/processing/opengl/PGraphics2D.java b/libs/processing-core/src/main/java/processing/opengl/PGraphics2D.java
similarity index 87%
rename from core/src/processing/opengl/PGraphics2D.java
rename to libs/processing-core/src/main/java/processing/opengl/PGraphics2D.java
index b3fc4e890..2fdd47543 100644
--- a/core/src/processing/opengl/PGraphics2D.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PGraphics2D.java
@@ -3,11 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,20 +20,16 @@
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.opengl;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
-
-import processing.core.PApplet;
-import processing.core.PConstants;
import processing.core.PGraphics;
+import processing.core.PMatrix2D;
import processing.core.PMatrix3D;
import processing.core.PShape;
import processing.core.PShapeSVG;
-import processing.data.XML;
+
public class PGraphics2D extends PGraphicsOpenGL {
@@ -156,7 +154,7 @@ public void camera(float eyeX, float eyeY, float eyeZ,
@Override
protected void defaultCamera() {
- cameraEyeX = cameraEyeY = cameraEyeZ = 0;
+ eyeDist = 1;
resetMatrix();
}
@@ -233,6 +231,7 @@ public void shape(PShape shape, float x, float y, float z,
}
+
//////////////////////////////////////////////////////////////
// SHAPE I/O
@@ -243,30 +242,39 @@ static protected boolean isSupportedExtension(String extension) {
}
- static protected PShape loadShapeImpl(PGraphics pg, String filename,
- String extension) {
- PShapeSVG svg = null;
+ static protected PShape loadShapeImpl(PGraphics pg,
+ String filename, String extension) {
+ if (extension.equals("svg") || extension.equals("svgz")) {
+ PShapeSVG svg = new PShapeSVG(pg.parent.loadXML(filename));
+ return PShapeOpenGL.createShape((PGraphicsOpenGL) pg, svg);
+ }
+ return null;
+ }
- if (extension.equals("svg")) {
- svg = new PShapeSVG(pg.parent.loadXML(filename));
- } else if (extension.equals("svgz")) {
- try {
- InputStream input =
- new GZIPInputStream(pg.parent.createInput(filename));
- XML xml = new XML(PApplet.createReader(input));
- svg = new PShapeSVG(xml);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+ //////////////////////////////////////////////////////////////
- if (svg != null) {
- PShapeOpenGL p2d = PShapeOpenGL.createShape2D((PGraphicsOpenGL)pg, svg);
- return p2d;
- } else {
- return null;
- }
+ // SCREEN TRANSFORMS
+
+
+ @Override
+ public float modelX(float x, float y, float z) {
+ showDepthWarning("modelX");
+ return 0;
+ }
+
+
+ @Override
+ public float modelY(float x, float y, float z) {
+ showDepthWarning("modelY");
+ return 0;
+ }
+
+
+ @Override
+ public float modelZ(float x, float y, float z) {
+ showDepthWarning("modelZ");
+ return 0;
}
@@ -275,6 +283,19 @@ static protected PShape loadShapeImpl(PGraphics pg, String filename,
// SHAPE CREATION
+// @Override
+// protected PShape createShapeFamily(int type) {
+// return new PShapeOpenGL(this, type);
+// }
+//
+//
+// @Override
+// protected PShape createShapePrimitive(int kind, float... p) {
+// return new PShapeOpenGL(this, kind, p);
+// }
+
+
+ /*
@Override
public PShape createShape(PShape source) {
return PShapeOpenGL.createShape2D(this, source);
@@ -308,7 +329,7 @@ static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int type) {
} else if (type == PShape.GEOMETRY) {
shape = new PShapeOpenGL(pg, PShape.GEOMETRY);
}
- shape.is3D(false);
+ shape.set3D(false);
return shape;
}
@@ -379,9 +400,10 @@ static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg,
shape.setParams(p);
}
- shape.is3D(false);
+ shape.set3D(false);
return shape;
}
+ */
//////////////////////////////////////////////////////////////
@@ -526,6 +548,19 @@ public float screenZ(float x, float y, float z) {
return 0;
}
+ @Override
+ public PMatrix2D getMatrix(PMatrix2D target) {
+ if (target == null) {
+ target = new PMatrix2D();
+ }
+ // This set operation is well defined, since modelview is a 2D-only
+ // transformation matrix in the P2D renderer.
+ target.set(modelview.m00, modelview.m01, modelview.m03,
+ modelview.m10, modelview.m11, modelview.m13);
+ return target;
+
+ }
+
@Override
public PMatrix3D getMatrix(PMatrix3D target) {
showVariationWarning("getMatrix");
diff --git a/libs/processing-core/src/main/java/processing/opengl/PGraphics2DX.java b/libs/processing-core/src/main/java/processing/opengl/PGraphics2DX.java
new file mode 100755
index 000000000..60fee8e16
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/opengl/PGraphics2DX.java
@@ -0,0 +1,2055 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2019-21 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.opengl;
+
+import static processing.core.PApplet.println;
+
+import java.net.URL;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import processing.core.PApplet;
+import processing.core.PGraphics;
+import processing.core.PImage;
+import processing.core.PMatrix3D;
+import processing.core.PShape;
+import processing.core.PShapeSVG;
+
+/**
+ * Super fast OpenGL 2D renderer originally contributed by Miles Fogle:
+ * https://github.com/hazmatsuitor
+ *
+ * It speeds-up rendering of 2D geometry by essentially two key optimizations: packing all the
+ * vertex data in a single VBO, and using a custom stroke tessellator (see StrokeRenderer class
+ * at the end). There are a number of other, less critical optimizations, for example using a single
+ * shader for textured and non-textured geometry and a depth algorithm that allows stacking a large
+ * number of 2D shapes without z-fighting (so occlusion is based on drawing order).
+ *
+ * Some notes from Miles:
+ *
+ * for testing purposes, I found it easier to create a separate class and avoid
+ * touching existing code for now, rather than directly editing PGraphics2D/PGraphicsOpenGL
+ * if this code becomes the new P2D implementation, then it will be properly migrated/integrated
+
+ * NOTE: this implementation doesn't use some of Processing's OpenGL wrappers
+ * (e.g. PShader, Texture) because I found it more convenient to handle them manually
+ * it could probably be made to use those classes with a bit of elbow grease and a spot of guidance
+ * but it may not be worth it - I doubt it would reduce complexity much, if at all
+ * (if there are reasons we need to use those classes, let me know)
+ *
+ */
+
+//TODO: track debug performance stats
+public final class PGraphics2DX extends PGraphicsOpenGL {
+ static final String NON_2D_SHAPE_ERROR = "The shape object is not 2D, cannot be displayed with this renderer";
+ static final String STROKE_PERSPECTIVE_ERROR = "Strokes cannot be perspective-corrected in 2D";
+
+ static final String NON_2D_SHADER_ERROR = "This shader cannot be used for 2D rendering";
+ static final String WRONG_SHADER_PARAMS = "The P2D renderer does not accept shaders of different tyes";
+
+ static protected final int SHADER2D = 7;
+
+ // Uses the implementations in the parent PGraphicsOpenGL class, which is needed to to draw obj files
+ // and apply shader filters.
+ protected boolean useParentImpl = false;
+
+ protected boolean initialized;
+
+ protected PGL.Tessellator tess;
+
+ protected PShader twoShader;
+ protected PShader defTwoShader;
+
+ protected int positionLoc;
+ protected int colorLoc;
+ protected int texCoordLoc;
+ protected int texFactorLoc;
+
+ protected int transformLoc;
+ protected int texScaleLoc;
+
+ static protected URL defP2DShaderVertURL =
+ PGraphicsOpenGL.class.getResource("/assets/shaders/P2DVert.glsl");
+ static protected URL defP2DShaderFragURL =
+ PGraphicsOpenGL.class.getResource("/assets/shaders/P2DFrag.glsl");
+
+
+ public PGraphics2DX() {
+ super();
+ initTess();
+ initVerts();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // RENDERER SUPPORT QUERIES
+
+
+ @Override
+ public boolean is2D() {
+ return true;
+ }
+
+
+ @Override
+ public boolean is3D() {
+ return false;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // RENDERING
+
+
+ @Override
+ public void beginDraw() {
+ super.beginDraw();
+ if (!useParentImpl) {
+ pgl.depthFunc(PGL.LESS);
+ depth = 1.0f;
+ }
+ }
+
+
+ @Override
+ public void flush() {
+ // If no vertices where created with the base implementation, then flush() will do nothing.
+ super.flush();
+ flushBuffer();
+ }
+
+
+ // These two methods are meant for debugging (comparing the new and old P2D renderers)
+ // and will go away.
+
+
+ public void useOldP2D() {
+ useParentImpl = true;
+ pgl.depthFunc(PGL.LEQUAL);
+ }
+
+
+ public void useNewP2D() {
+ useParentImpl = false;
+ pgl.depthFunc(PGL.LESS);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // HINTS
+
+
+ @Override
+ public void hint(int which) {
+ if (which == ENABLE_STROKE_PERSPECTIVE) {
+ showWarning(STROKE_PERSPECTIVE_ERROR);
+ return;
+ }
+ super.hint(which);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // PROJECTION
+
+
+ @Override
+ public void ortho() {
+ showMethodWarning("ortho");
+ }
+
+
+ @Override
+ public void ortho(float left, float right,
+ float bottom, float top) {
+ showMethodWarning("ortho");
+ }
+
+
+ @Override
+ public void ortho(float left, float right,
+ float bottom, float top,
+ float near, float far) {
+ showMethodWarning("ortho");
+ }
+
+
+ @Override
+ public void perspective() {
+ showMethodWarning("perspective");
+ }
+
+
+ @Override
+ public void perspective(float fov, float aspect, float zNear, float zFar) {
+ showMethodWarning("perspective");
+ }
+
+
+ @Override
+ public void frustum(float left, float right, float bottom, float top,
+ float znear, float zfar) {
+ showMethodWarning("frustum");
+ }
+
+
+ @Override
+ protected void defaultPerspective() {
+ super.ortho(0, width, -height, 0, -1, +1);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CAMERA
+
+
+ @Override
+ public void beginCamera() {
+ showMethodWarning("beginCamera");
+ }
+
+
+ @Override
+ public void endCamera() {
+ showMethodWarning("endCamera");
+ }
+
+
+ @Override
+ public void camera() {
+ showMethodWarning("camera");
+ }
+
+
+ @Override
+ public void camera(float eyeX, float eyeY, float eyeZ,
+ float centerX, float centerY, float centerZ,
+ float upX, float upY, float upZ) {
+ showMethodWarning("camera");
+ }
+
+
+ @Override
+ protected void defaultCamera() {
+ eyeDist = 1;
+ resetMatrix();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHAPE
+
+
+ @Override
+ public void shape(PShape shape) {
+ if (shape.is2D()) {
+ if (!useParentImpl) {
+ useOldP2D();
+ super.shape(shape);
+ useNewP2D();
+ } else {
+ super.shape(shape);
+ }
+ } else {
+ showWarning(NON_2D_SHAPE_ERROR);
+ }
+ }
+
+
+ @Override
+ public void shape(PShape shape, float x, float y) {
+ if (shape.is2D()) {
+ if (!useParentImpl) {
+ useOldP2D();
+ super.shape(shape, x, y);
+ useNewP2D();
+ } else {
+ super.shape(shape, x, y);
+ }
+ } else {
+ showWarning(NON_2D_SHAPE_ERROR);
+ }
+ }
+
+
+ @Override
+ public void shape(PShape shape, float a, float b, float c, float d) {
+ if (shape.is2D()) {
+ if (!useParentImpl) {
+ useOldP2D();
+ super.shape(shape, a, b, c, d);
+ useNewP2D();
+ } else {
+ super.shape(shape, a, b, c, d);
+ }
+ } else {
+ showWarning(NON_2D_SHAPE_ERROR);
+ }
+ }
+
+
+ @Override
+ public void shape(PShape shape, float x, float y, float z) {
+ showDepthWarningXYZ("shape");
+ }
+
+
+ @Override
+ public void shape(PShape shape, float x, float y, float z,
+ float c, float d, float e) {
+ showDepthWarningXYZ("shape");
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHAPE I/O
+
+
+ static protected boolean isSupportedExtension(String extension) {
+ return extension.equals("svg") || extension.equals("svgz");
+ }
+
+
+ static protected PShape loadShapeImpl(PGraphics pg,
+ String filename, String extension) {
+ if (extension.equals("svg") || extension.equals("svgz")) {
+ PShapeSVG svg = new PShapeSVG(pg.parent.loadXML(filename));
+ return PShapeOpenGL.createShape((PGraphicsOpenGL) pg, svg);
+ }
+ return null;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SCREEN TRANSFORMS
+
+
+ @Override
+ public float modelX(float x, float y, float z) {
+ showDepthWarning("modelX");
+ return 0;
+ }
+
+
+ @Override
+ public float modelY(float x, float y, float z) {
+ showDepthWarning("modelY");
+ return 0;
+ }
+
+
+ @Override
+ public float modelZ(float x, float y, float z) {
+ showDepthWarning("modelZ");
+ return 0;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // VERTEX SHAPES
+
+
+ @Override
+ public void texture(PImage image) {
+ super.texture(image);
+
+ if (image == null) {
+ return;
+ }
+
+ Texture t = currentPG.getTexture(image);
+ texWidth = t.width;
+ texHeight = t.height;
+ imageTex = t.glName;
+ textureImpl(imageTex);
+ }
+
+
+ @Override
+ public void beginShape(int kind) {
+ if (useParentImpl) {
+ super.beginShape(kind);
+ return;
+ }
+
+ shapeType = kind;
+ vertCount = 0;
+ contourCount = 0;
+ }
+
+
+ @Override
+ public void endShape(int mode) {
+ if (useParentImpl) {
+ super.endShape(mode);
+ return;
+ }
+
+ //end the current contour
+ appendContour(vertCount);
+
+ if (fill) {
+ incrementDepth();
+
+ if (shapeType == POLYGON) {
+ if (knownConvexPolygon) {
+ for (int i = 2; i < vertCount; ++i) {
+ check(3);
+ vertexImpl(shapeVerts[0]);
+ vertexImpl(shapeVerts[i - 1]);
+ vertexImpl(shapeVerts[i]);
+ }
+
+ knownConvexPolygon = false;
+ } else {
+ tess.beginPolygon(this);
+ tess.beginContour();
+
+ int c = 0;
+ for (int i = 0; i < vertCount; ++i) {
+ if (contours[c] == i) {
+ tess.endContour();
+ tess.beginContour();
+ c++; //lol no, this is java
+ }
+
+ tempDoubles[0] = shapeVerts[i].x;
+ tempDoubles[1] = shapeVerts[i].y;
+ tess.addVertex(tempDoubles, 0, shapeVerts[i]);
+ }
+ tess.endContour();
+ tess.endPolygon();
+ }
+ } else if (shapeType == QUAD_STRIP) {
+ for (int i = 0; i <= vertCount - 4; i += 2) {
+ check(6);
+ vertexImpl(shapeVerts[i + 0]);
+ vertexImpl(shapeVerts[i + 1]);
+ vertexImpl(shapeVerts[i + 2]);
+ vertexImpl(shapeVerts[i + 1]);
+ vertexImpl(shapeVerts[i + 2]);
+ vertexImpl(shapeVerts[i + 3]);
+ }
+ } else if (shapeType == QUADS) {
+ for (int i = 0; i <= vertCount - 4; i += 4) {
+ check(6);
+ vertexImpl(shapeVerts[i + 0]);
+ vertexImpl(shapeVerts[i + 1]);
+ vertexImpl(shapeVerts[i + 2]);
+ vertexImpl(shapeVerts[i + 0]);
+ vertexImpl(shapeVerts[i + 2]);
+ vertexImpl(shapeVerts[i + 3]);
+ }
+ } else if (shapeType == TRIANGLE_STRIP) {
+ for (int i = 0; i <= vertCount - 3; i += 1) {
+ check(3);
+ vertexImpl(shapeVerts[i + 0]);
+ vertexImpl(shapeVerts[i + 1]);
+ vertexImpl(shapeVerts[i + 2]);
+ }
+ } else if (shapeType == TRIANGLE_FAN) {
+ for (int i = 0; i <= vertCount - 3; i += 1) {
+ check(3);
+ vertexImpl(shapeVerts[0 + 0]);
+ vertexImpl(shapeVerts[i + 1]);
+ vertexImpl(shapeVerts[i + 2]);
+ }
+
+ //close the fan
+ if (vertCount >= 3) {
+ check(3);
+ vertexImpl(shapeVerts[0]);
+ vertexImpl(shapeVerts[vertCount - 1]);
+ vertexImpl(shapeVerts[1]);
+ }
+ } else if (shapeType == TRIANGLES) {
+ for (int i = 0; i <= vertCount - 3; i += 3) {
+ check(3);
+ vertexImpl(shapeVerts[i + 0]);
+ vertexImpl(shapeVerts[i + 1]);
+ vertexImpl(shapeVerts[i + 2]);
+ }
+ }
+ }
+
+ if (stroke) {
+ incrementDepth();
+
+ if (shapeType == POLYGON) {
+ if (vertCount < 3) {
+ return;
+ }
+
+ int c = 0;
+ sr.beginLine();
+ for (int i = 0; i < vertCount; ++i) {
+ if (contours[c] == i) {
+ sr.endLine(mode == CLOSE);
+ sr.beginLine();
+ c++;
+ }
+
+ sr.lineVertex(shapeVerts[i].x, shapeVerts[i].y);
+ }
+ sr.endLine(mode == CLOSE);
+ } else if (shapeType == QUAD_STRIP) {
+ for (int i = 0; i <= vertCount - 4; i += 2) {
+ sr.beginLine();
+ sr.lineVertex(shapeVerts[i + 0].x, shapeVerts[i + 0].y);
+ sr.lineVertex(shapeVerts[i + 1].x, shapeVerts[i + 1].y);
+ sr.lineVertex(shapeVerts[i + 3].x, shapeVerts[i + 3].y);
+ sr.lineVertex(shapeVerts[i + 2].x, shapeVerts[i + 2].y);
+ sr.endLine(true);
+ }
+ } else if (shapeType == QUADS) {
+ for (int i = 0; i <= vertCount - 4; i += 4) {
+ sr.beginLine();
+ sr.lineVertex(shapeVerts[i + 0].x, shapeVerts[i + 0].y);
+ sr.lineVertex(shapeVerts[i + 1].x, shapeVerts[i + 1].y);
+ sr.lineVertex(shapeVerts[i + 2].x, shapeVerts[i + 2].y);
+ sr.lineVertex(shapeVerts[i + 3].x, shapeVerts[i + 3].y);
+ sr.endLine(true);
+ }
+ } else if (shapeType == TRIANGLE_STRIP) {
+ for (int i = 0; i <= vertCount - 3; i += 1) {
+ sr.beginLine();
+ sr.lineVertex(shapeVerts[i + 0].x, shapeVerts[i + 0].y);
+ sr.lineVertex(shapeVerts[i + 1].x, shapeVerts[i + 1].y);
+ sr.lineVertex(shapeVerts[i + 2].x, shapeVerts[i + 2].y);
+ sr.endLine(true);
+ }
+ } else if (shapeType == TRIANGLE_FAN) {
+ for (int i = 0; i <= vertCount - 3; i += 1) {
+ sr.beginLine();
+ sr.lineVertex(shapeVerts[0 + 0].x, shapeVerts[0 + 0].y);
+ sr.lineVertex(shapeVerts[i + 1].x, shapeVerts[i + 1].y);
+ sr.lineVertex(shapeVerts[i + 2].x, shapeVerts[i + 2].y);
+ sr.endLine(true);
+ }
+
+ //close the fan
+ if (vertCount >= 3) {
+ sr.beginLine();
+ sr.lineVertex(shapeVerts[0].x, shapeVerts[0].y);
+ sr.lineVertex(shapeVerts[vertCount - 1].x, shapeVerts[vertCount - 1].y);
+ sr.lineVertex(shapeVerts[1].x, shapeVerts[1].y);
+ sr.endLine(true);
+ }
+ } else if (shapeType == TRIANGLES) {
+ for (int i = 0; i <= vertCount - 3; i += 3) {
+ sr.beginLine();
+ sr.lineVertex(shapeVerts[i + 0].x, shapeVerts[i + 0].y);
+ sr.lineVertex(shapeVerts[i + 1].x, shapeVerts[i + 1].y);
+ sr.lineVertex(shapeVerts[i + 2].x, shapeVerts[i + 2].y);
+ sr.endLine(true);
+ }
+ } else if (shapeType == LINES) {
+ for (int i = 0; i <= vertCount - 2; i += 2) {
+ TessVertex s1 = shapeVerts[i + 0];
+ TessVertex s2 = shapeVerts[i + 1];
+ singleLine(s1.x, s1.y, s2.x, s2.y, strokeColor);
+ }
+ } else if (shapeType == POINTS) {
+ for (int i = 0; i <= vertCount - 1; i += 1) {
+ singlePoint(shapeVerts[i].x, shapeVerts[i].y, strokeColor);
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public void beginContour() {
+ super.beginContour();
+ if (useParentImpl) {
+ return;
+ }
+
+ //XXX: not sure what the exact behavior should be for invalid calls to begin/endContour()
+ //but this should work for valid cases for now
+ appendContour(vertCount);
+ }
+
+
+ @Override
+ public void vertex(float x, float y) {
+ if (useParentImpl) {
+ super.vertex(x, y);
+ return;
+ }
+
+ curveVerts = 0;
+ shapeVertex(x, y, 0, 0, fillColor, 0);
+ }
+
+
+ @Override
+ public void vertex(float x, float y, float u, float v) {
+ if (useParentImpl) {
+ super.vertex(x, y, u, v);
+ return;
+ }
+
+ curveVerts = 0;
+ textureImpl(imageTex);
+ shapeVertex(x, y, u, v, tint? tintColor : 0xFFFFFFFF, 1);
+ }
+
+
+ @Override
+ public void vertex(float x, float y, float z) {
+ showDepthWarningXYZ("vertex");
+ }
+
+
+ @Override
+ public void vertex(float x, float y, float z, float u, float v) {
+ showDepthWarningXYZ("vertex");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // BEZIER VERTICES
+
+
+ //this method is almost wholesale copied from PGraphics.bezierVertex()
+ //TODO: de-duplicate this code if there is a convenient way to do so
+ @Override
+ public void bezierVertex(float x2, float y2, float x3, float y3, float x4, float y4) {
+ if (useParentImpl) {
+ super.bezierVertex(x2, y2, x3, y3, x4, y4);
+ return;
+ }
+
+ bezierInitCheck();
+// bezierVertexCheck(); //TODO: re-implement this (and other run-time sanity checks)
+ PMatrix3D draw = bezierDrawMatrix;
+
+ //(these are the only lines that are different)
+ float x1 = shapeVerts[vertCount - 1].x;
+ float y1 = shapeVerts[vertCount - 1].y;
+
+ float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4;
+ float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4;
+ float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4;
+
+ float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4;
+ float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4;
+ float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4;
+
+ for (int j = 0; j < bezierDetail; j++) {
+ x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
+ y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
+ shapeVertex(x1, y1, 0, 0, fillColor, 0);
+ }
+ }
+
+
+ @Override
+ public void bezierVertex(float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4) {
+ showDepthWarningXYZ("bezierVertex");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // QUADRATIC BEZIER VERTICES
+
+
+ //this method is almost wholesale copied from PGraphics.quadraticVertex()
+ //TODO: de-duplicate this code if there is a convenient way to do so
+ @Override
+ public void quadraticVertex(float cx, float cy,
+ float x3, float y3) {
+ if (useParentImpl) {
+ super.quadraticVertex(cx, cy, x3, y3);
+ return;
+ }
+
+ //(these are the only lines that are different)
+ float x1 = shapeVerts[vertCount - 1].x;
+ float y1 = shapeVerts[vertCount - 1].y;
+
+ //TODO: optimize this?
+ bezierVertex(x1 + ((cx-x1)*2/3.0f), y1 + ((cy-y1)*2/3.0f),
+ x3 + ((cx-x3)*2/3.0f), y3 + ((cy-y3)*2/3.0f),
+ x3, y3);
+ }
+
+
+ @Override
+ public void quadraticVertex(float x2, float y2, float z2,
+ float x4, float y4, float z4) {
+ showDepthWarningXYZ("quadVertex");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CURVE VERTICES
+
+
+ //curve vertices
+ private float cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
+ private int curveVerts;
+
+
+ @Override
+ public void curveVertex(float x, float y) {
+ if (useParentImpl) {
+ super.curveVertex(x, y);
+ return;
+ }
+
+// curveVertexCheck(); //TODO: re-implement this (and other runtime checks)
+
+ curveInitCheck();
+
+ cx1 = cx2;
+ cx2 = cx3;
+ cx3 = cx4;
+
+ cy1 = cy2;
+ cy2 = cy3;
+ cy3 = cy4;
+
+ cx4 = x;
+ cy4 = y;
+
+ curveVerts += 1;
+
+ if (curveVerts > 3) {
+ println("drawing curve...");
+
+ PMatrix3D draw = curveDrawMatrix;
+
+ float xplot1 = draw.m10*cx1 + draw.m11*cx2 + draw.m12*cx3 + draw.m13*cx4;
+ float xplot2 = draw.m20*cx1 + draw.m21*cx2 + draw.m22*cx3 + draw.m23*cx4;
+ float xplot3 = draw.m30*cx1 + draw.m31*cx2 + draw.m32*cx3 + draw.m33*cx4;
+
+ float yplot1 = draw.m10*cy1 + draw.m11*cy2 + draw.m12*cy3 + draw.m13*cy4;
+ float yplot2 = draw.m20*cy1 + draw.m21*cy2 + draw.m22*cy3 + draw.m23*cy4;
+ float yplot3 = draw.m30*cy1 + draw.m31*cy2 + draw.m32*cy3 + draw.m33*cy4;
+
+ float x0 = cx2;
+ float y0 = cy2;
+
+ if (curveVerts == 4) {
+ shapeVertex(x0, y0, 0, 0, fillColor, 0);
+ }
+
+ for (int j = 0; j < curveDetail; j++) {
+ x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
+ y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
+ shapeVertex(x0, y0, 0, 0, fillColor, 0);
+ }
+ }
+ }
+
+
+ @Override
+ public void curveVertex(float x, float y, float z) {
+ showDepthWarningXYZ("curveVertex");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // PRIMITIVES
+
+
+ /*
+ * Re-implementations of the various shape drawing methods.
+ *
+ * Ideally we could just call the versions in PGraphics,
+ * since most of those will work correctly without modification,
+ * but there's no good way to do that in Java,
+ * so as long as we're inheriting from PGraphicsOpenGL,
+ * we need to re-implement them.
+ */
+
+
+ @Override
+ public void quad(float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4) {
+ if (useParentImpl) {
+ super.quad(x1, y1, x2, y2, x3, y3, x4, y4);
+ return;
+ }
+
+ beginShape(QUADS);
+ vertex(x1, y1);
+ vertex(x2, y2);
+ vertex(x3, y3);
+ vertex(x4, y4);
+ endShape();
+ }
+
+
+ @Override
+ public void triangle(float x1, float y1, float x2, float y2,
+ float x3, float y3) {
+ if (useParentImpl) {
+ super.triangle(x1, y1, x2, y2, x3, y3);
+ return;
+ }
+
+ beginShape(TRIANGLES);
+ vertex(x1, y1);
+ vertex(x2, y2);
+ vertex(x3, y3);
+ endShape();
+ }
+
+
+ @Override
+ public void ellipseImpl(float a, float b, float c, float d) {
+ if (useParentImpl) {
+ super.ellipseImpl(a, b, c, d);
+ return;
+ }
+
+ beginShape(POLYGON);
+
+ //convert corner/diameter to center/radius
+ float rx = c * 0.5f;
+ float ry = d * 0.5f;
+ float x = a + rx;
+ float y = b + ry;
+
+ //since very wide stroke and/or very small radius might cause the
+ //stroke to account for a significant portion of the overall radius,
+ //we take it into account when calculating detail, just to be safe
+ int segments = circleDetail(PApplet.max(rx, ry) + (stroke? strokeWeight : 0), TWO_PI);
+ float step = TWO_PI / segments;
+
+ float cos = PApplet.cos(step);
+ float sin = PApplet.sin(step);
+ float dx = 0, dy = 1;
+ for (int i = 0; i < segments; ++i) {
+ shapeVertex(x + dx * rx, y + dy * ry, 0, 0, fillColor, 0);
+ //this is the equivalent of multiplying the vector by the 2x2 rotation matrix [[cos -sin] [sin cos]]
+ float tempx = dx * cos - dy * sin;
+ dy = dx * sin + dy * cos;
+ dx = tempx;
+ }
+
+ knownConvexPolygon = true;
+ endShape(CLOSE);
+ }
+
+
+ @Override
+ public void line(float x1, float y1, float x2, float y2) {
+ if (useParentImpl) {
+ super.line(x1, y1, x2, y2);
+ return;
+ }
+
+ incrementDepth();
+ singleLine(x1, y1, x2, y2, strokeColor);
+ }
+
+
+ @Override
+ public void point(float x, float y) {
+ if (useParentImpl) {
+ super.point(x, y);
+ return;
+ }
+
+ incrementDepth();
+ singlePoint(x, y, strokeColor);
+ }
+
+
+ @Override
+ protected void arcImpl(float x, float y, float w, float h, float start, float stop, int mode) {
+ if (useParentImpl) {
+ super.arcImpl(x, y, w, h, start, stop, mode);
+ return;
+ }
+
+ //INVARIANT: stop > start
+ //INVARIANT: stop - start <= TWO_PI
+
+ //convert corner/diameter to center/radius
+ w *= 0.5f;
+ h *= 0.5f;
+ x += w;
+ y += h;
+
+ float diff = stop - start;
+ int segments = circleDetail(PApplet.max(w, h), diff);
+ float step = diff / segments;
+
+ beginShape(POLYGON);
+
+ //no constant is defined for the default arc mode, so we just use a literal 0
+ //(this is consistent with code elsewhere)
+ if (mode == 0 || mode == PIE) {
+ vertex(x, y);
+ }
+
+ if (mode == 0) {
+ //kinda hacky way to disable drawing a stroke along the first edge
+ appendContour(vertCount);
+ }
+
+ float dx = PApplet.cos(start);
+ float dy = PApplet.sin(start);
+ float c = PApplet.cos(step);
+ float s = PApplet.sin(step);
+ for (int i = 0; i <= segments; ++i) {
+ shapeVertex(x + dx * w, y + dy * h, 0, 0, fillColor, 0);
+ //this is the equivalent of multiplying the vector by the 2x2 rotation matrix [[c -s] [s c]]
+ float tempx = dx * c - dy * s;
+ dy = dx * s + dy * c;
+ dx = tempx;
+ }
+
+ //for the case `(mode == PIE || mode == 0) && diff > HALF_PI`, the polygon
+ //will not actually be convex, but due to known vertex order, we can still safely tessellate as if it is
+ knownConvexPolygon = true;
+ if (mode == CHORD || mode == PIE) {
+ endShape(CLOSE);
+ } else {
+ endShape();
+ }
+ }
+
+
+ @Override
+ protected void rectImpl(float x1, float y1, float x2, float y2,
+ float tl, float tr, float br, float bl) {
+ if (useParentImpl) {
+ super.rectImpl(x1, y1, x2, y2, tl, tr, br, bl);
+ return;
+ }
+
+ beginShape();
+ if (tr != 0) {
+ vertex(x2-tr, y1);
+ quadraticVertex(x2, y1, x2, y1+tr);
+ } else {
+ vertex(x2, y1);
+ }
+ if (br != 0) {
+ vertex(x2, y2-br);
+ quadraticVertex(x2, y2, x2-br, y2);
+ } else {
+ vertex(x2, y2);
+ }
+ if (bl != 0) {
+ vertex(x1+bl, y2);
+ quadraticVertex(x1, y2, x1, y2-bl);
+ } else {
+ vertex(x1, y2);
+ }
+ if (tl != 0) {
+ vertex(x1, y1+tl);
+ quadraticVertex(x1, y1, x1+tl, y1);
+ } else {
+ vertex(x1, y1);
+ }
+ knownConvexPolygon = true;
+ endShape(CLOSE);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // BOX
+
+
+ @Override
+ public void box(float w, float h, float d) {
+ showMethodWarning("box");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SPHERE
+
+
+ @Override
+ public void sphere(float r) {
+ showMethodWarning("sphere");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // PIXELS
+
+
+ @Override
+ public void loadPixels() {
+ super.loadPixels();
+
+ allocatePixels();
+ readPixels();
+ }
+
+
+ @Override
+ public void updatePixels() {
+ super.updatePixels();
+ image(this, 0, 0, width * 2, height * 2, 0, 0, pixelWidth, pixelHeight);
+ flushBuffer();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CLIPPING
+
+ /*
+ @Override
+ public void clipImpl(float x1, float y1, float x2, float y2) {
+ //XXX: exactly the same as the implementation in PGraphicsOpenGL,
+ //but calls flushBuffer() instead of flush()
+ flushBuffer();
+ pgl.enable(PGL.SCISSOR_TEST);
+
+ float h = y2 - y1;
+ clipRect[0] = (int)x1;
+ clipRect[1] = (int)(height - y1 - h);
+ clipRect[2] = (int)(x2 - x1);
+ clipRect[3] = (int)h;
+ pgl.scissor(clipRect[0], clipRect[1], clipRect[2], clipRect[3]);
+
+ clip = true;
+ }
+
+ @Override
+ public void noClip() {
+ //XXX: exactly the same as the implementation in PGraphicsOpenGL,
+ //but calls flushBuffer() instead of flush()
+ if (clip) {
+ flushBuffer();
+ pgl.disable(PGL.SCISSOR_TEST);
+ clip = false;
+ }
+ }
+*/
+
+
+ //////////////////////////////////////////////////////////////
+
+ // TEXT
+
+
+ //NOTE: a possible improvement to text rendering performance is to batch all glyphs
+ //from the same texture page together instead of rendering each char strictly in sequence.
+ //it remains to be seen whether this would improve performance in practice
+ //(I don't know how common it is for a font to occupy multiple texture pages)
+
+ @Override
+ protected void textCharModelImpl(FontTexture.TextureInfo info,
+ float x0, float y0, float x1, float y1) {
+ incrementDepth();
+ check(6);
+ textureImpl(textTex.textures[info.texIndex].glName);
+ vertexImpl(x0, y0, info.u0, info.v0, fillColor, 1);
+ vertexImpl(x1, y0, info.u1, info.v0, fillColor, 1);
+ vertexImpl(x0, y1, info.u0, info.v1, fillColor, 1);
+ vertexImpl(x1, y0, info.u1, info.v0, fillColor, 1);
+ vertexImpl(x0, y1, info.u0, info.v1, fillColor, 1);
+ vertexImpl(x1, y1, info.u1, info.v1, fillColor, 1);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // MATRIX MORE!
+
+
+ @Override
+ protected void begin2D() {
+ pushProjection();
+ defaultPerspective();
+ pushMatrix();
+ defaultCamera();
+ }
+
+
+ @Override
+ protected void end2D() {
+ popMatrix();
+ popProjection();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHADER FILTER
+
+
+
+ @Override
+ public void filter(PShader shader) {
+ // TODO: not working... the loadShader() method uses the P2 vertex stage
+ // The filter method needs to use the geometry-generation in the base class.
+ // We could re-implement it here, but this is easier.
+ if (!useParentImpl) {
+ useOldP2D();
+ super.filter(shader);
+ useNewP2D();
+ } else {
+ super.filter(shader);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHADER API
+
+
+ @Override
+ public PShader loadShader(String fragFilename) {
+ if (fragFilename == null || fragFilename.equals("")) {
+ PGraphics.showWarning(MISSING_FRAGMENT_SHADER);
+ return null;
+ }
+
+ PShader shader = new PShader(parent);
+
+ shader.setFragmentShader(fragFilename);
+ String[] vertSource = pgl.loadVertexShader(defP2DShaderVertURL);
+ shader.setVertexShader(vertSource);
+
+ return shader;
+ }
+
+
+ @Override
+ public void shader(PShader shader) {
+ if (useParentImpl) {
+ super.shader(shader);
+ return;
+ }
+ flushBuffer(); // Flushing geometry drawn with a different shader.
+
+ if (shader != null) shader.init();
+ boolean res = checkShaderLocs(shader);
+ if (res) {
+ twoShader = shader;
+ shader.type = SHADER2D;
+ } else {
+ PGraphics.showWarning(NON_2D_SHADER_ERROR);
+ }
+ }
+
+
+ @Override
+ public void shader(PShader shader, int kind) {
+ if (useParentImpl) {
+ super.shader(shader, kind);
+ return;
+ }
+ PGraphics.showWarning(WRONG_SHADER_PARAMS);
+ }
+
+
+ @Override
+ public void resetShader() {
+ if (useParentImpl) {
+ super.resetShader();
+ return;
+ }
+ flushBuffer();
+ twoShader = null;
+ }
+
+
+ @Override
+ public void resetShader(int kind) {
+ if (useParentImpl) {
+ super.resetShader(kind);
+ return;
+ }
+ PGraphics.showWarning(WRONG_SHADER_PARAMS);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // MATRIX TRANSFORMATIONS
+
+
+ @Override
+ public void translate(float tx, float ty, float tz) {
+ showDepthWarningXYZ("translate");
+ }
+
+ @Override
+ public void rotateX(float angle) {
+ showDepthWarning("rotateX");
+ }
+
+ @Override
+ public void rotateY(float angle) {
+ showDepthWarning("rotateY");
+ }
+
+ @Override
+ public void rotateZ(float angle) {
+ showDepthWarning("rotateZ");
+ }
+
+ @Override
+ public void rotate(float angle, float vx, float vy, float vz) {
+ showVariationWarning("rotate");
+ }
+
+ @Override
+ public void applyMatrix(PMatrix3D source) {
+ showVariationWarning("applyMatrix");
+ }
+
+ @Override
+ public void applyMatrix(float n00, float n01, float n02, float n03,
+ float n10, float n11, float n12, float n13,
+ float n20, float n21, float n22, float n23,
+ float n30, float n31, float n32, float n33) {
+ showVariationWarning("applyMatrix");
+ }
+
+ @Override
+ public void scale(float sx, float sy, float sz) {
+ showDepthWarningXYZ("scale");
+ }
+
+ //////////////////////////////////////////////////////////////
+
+ // SCREEN AND MODEL COORDS
+
+
+ @Override
+ public float screenX(float x, float y, float z) {
+ showDepthWarningXYZ("screenX");
+ return 0;
+ }
+
+ @Override
+ public float screenY(float x, float y, float z) {
+ showDepthWarningXYZ("screenY");
+ return 0;
+ }
+
+ @Override
+ public float screenZ(float x, float y, float z) {
+ showDepthWarningXYZ("screenZ");
+ return 0;
+ }
+
+ @Override
+ public PMatrix3D getMatrix(PMatrix3D target) {
+ showVariationWarning("getMatrix");
+ return target;
+ }
+
+ @Override
+ public void setMatrix(PMatrix3D source) {
+ showVariationWarning("setMatrix");
+ }
+
+ //////////////////////////////////////////////////////////////
+
+ // LIGHTS
+
+
+ @Override
+ public void lights() {
+ showMethodWarning("lights");
+ }
+
+ @Override
+ public void noLights() {
+ showMethodWarning("noLights");
+ }
+
+ @Override
+ public void ambientLight(float red, float green, float blue) {
+ showMethodWarning("ambientLight");
+ }
+
+ @Override
+ public void ambientLight(float red, float green, float blue,
+ float x, float y, float z) {
+ showMethodWarning("ambientLight");
+ }
+
+ @Override
+ public void directionalLight(float red, float green, float blue,
+ float nx, float ny, float nz) {
+ showMethodWarning("directionalLight");
+ }
+
+ @Override
+ public void pointLight(float red, float green, float blue,
+ float x, float y, float z) {
+ showMethodWarning("pointLight");
+ }
+
+ @Override
+ public void spotLight(float red, float green, float blue,
+ float x, float y, float z,
+ float nx, float ny, float nz,
+ float angle, float concentration) {
+ showMethodWarning("spotLight");
+ }
+
+ @Override
+ public void lightFalloff(float constant, float linear, float quadratic) {
+ showMethodWarning("lightFalloff");
+ }
+
+ @Override
+ public void lightSpecular(float v1, float v2, float v3) {
+ showMethodWarning("lightSpecular");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // PRIVATE IMPLEMENTATION
+
+
+ //maxVerts can be tweaked for memory/performance trade-off
+ //in my testing, performance seems to plateau after around 6000 (= 2000*3)
+ //memory usage should be around ~165kb for 6000 verts
+ final private int maxVerts = 2000*3;
+ final private int vertSize = 7*Float.BYTES; //xyzuvcf
+ private float[] vertexData = new float[maxVerts*7];
+ private int usedVerts = 0;
+
+ private float depth = 1.0f;
+
+ private int imageTex;
+ private int tex;
+ private int vbo;
+ private int texWidth, texHeight;
+
+ // Determination of the smallest increments and largest-greater-than-minus-one
+ // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
+
+ // Using the smallest positive normal number in half (16-bit) precision, which is how the depth
+ // buffer is initialized in mobile
+ private float smallestDepthIncrement = (float)Math.pow(2, -14);
+
+ // As the limit for the depth increase, we take the minus the largest number less than one in
+ // half (16-bit) precision
+ private float largestNumberLessThanOne = 1 - (float)Math.pow(2, -11);
+
+ private void incrementDepth() {
+ // By resetting the depth buffer when needed, we are able to have arbitrarily many
+ // layers, unlimited by depth buffer precision. In practice, the precision of this
+ // algorithm seems to be acceptable (exactly (1 + 1 - pow(2, -11))/pow(2, -14) = 32,760 layers)
+ // for mobile.
+ if (depth < -largestNumberLessThanOne) {
+ flushBuffer();
+ pgl.clear(PGL.DEPTH_BUFFER_BIT);
+ // Depth test will fail at depth = 1.0 after clearing the depth buffer,
+ // But since we always increment before drawing anything, this should be okay
+ depth = 1.0f;
+ }
+
+ depth -= smallestDepthIncrement;
+ }
+
+
+ private void initTess() {
+ PGL.TessellatorCallback callback = new PGL.TessellatorCallback() {
+
+ public void begin(int type) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void end() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void vertex(Object data) {
+ if (usedVerts % 3 == 0) {
+ check(3);
+ }
+
+ TessVertex vert = (TessVertex)data;
+ vertexImpl(vert.x, vert.y, vert.u, vert.v, vert.c, vert.f);
+ }
+
+ public void combine(double[] coords, Object[] data, float[] weights, Object[] outData) {
+ //here we do some horrible things to blend the colors
+ float r = 0, g = 0, b = 0, a = 0;
+ for (int i = 0; i < data.length; ++i) {
+ int c = ((TessVertex)data[i]).c;
+ a += weights[i] * ((c >> 24) & 0xFF);
+ r += weights[i] * ((c >> 16) & 0xFF);
+ g += weights[i] * ((c >> 8) & 0xFF);
+ b += weights[i] * (c & 0xFF);
+ }
+ int c = ((int)a << 24) + ((int)r << 16) + ((int)g << 8) + (int)b;
+
+ float u = 0, v = 0, f = 0;
+ for (int i = 0; i < data.length; ++i) {
+ u += weights[i] * ((TessVertex)data[i]).u;
+ v += weights[i] * ((TessVertex)data[i]).v;
+ f += weights[i] * ((TessVertex)data[i]).f;
+ }
+
+ outData[0] = new TessVertex((float)coords[0], (float)coords[1], u, v, c, f);
+ }
+
+ public void error(int err) {
+ println("glu error: " + err);
+ }
+ };
+ tess = pgl.createTessellator(callback);
+
+ // We specify the edge flag callback as a no-op to force the tesselator to only pass us
+ // triangle primitives (no triangle fans or triangle strips), for simplicity
+ tess.setCallback(PGL.TESS_EDGE_FLAG);
+ tess.setWindingRule(PGL.TESS_WINDING_NONZERO);
+ }
+
+
+ private void initVerts() {
+ for (int i = 0; i < shapeVerts.length; ++i) {
+ shapeVerts[i] = new TessVertex();
+ }
+ }
+
+
+ private void flushBuffer() {
+ if (usedVerts == 0) {
+ return;
+ }
+
+ if (vbo == 0) {
+ // Generate vbo
+ IntBuffer vboBuff = IntBuffer.allocate(1);
+ pgl.genBuffers(1, vboBuff);
+ vbo = vboBuff.get(0);
+ }
+
+ // Upload vertex data
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, vbo);
+ pgl.bufferData(PGL.ARRAY_BUFFER, usedVerts * vertSize,
+ FloatBuffer.wrap(vertexData), PGL.DYNAMIC_DRAW);
+
+ PShader shader = getShader();
+ shader.bind();
+ setAttribs();
+ loadUniforms();
+
+ pgl.drawArrays(PGL.TRIANGLES, 0, usedVerts);
+
+ usedVerts = 0;
+ shader.unbind();
+
+ //XXX: DEBUG
+// println("flushed: " + tex + ", " + imageTex);
+ }
+
+
+ private boolean checkShaderLocs(PShader shader) {
+ int positionLoc = shader.getAttributeLoc("position");
+ if (positionLoc == -1) {
+ positionLoc = shader.getAttributeLoc("vertex");
+ }
+// int colorLoc = shader.getAttributeLoc("color");
+ int transformLoc = shader.getUniformLoc("transform");
+ if (transformLoc == -1) {
+ transformLoc = shader.getUniformLoc("transformMatrix");
+ }
+
+ /*
+ // Became less demanding and 2D shaders do not need to have texture uniforms/attribs
+ int texScaleLoc = shader.getUniformLoc("texScale");
+ if (texScaleLoc == -1) {
+ texScaleLoc = shader.getUniformLoc("texOffset");
+ }
+ int texCoordLoc = shader.getAttributeLoc("texCoord");
+ int texFactorLoc = shader.getAttributeLoc("texFactor");
+ */
+
+ return positionLoc != -1 && transformLoc != -1;
+// colorLoc != -1 && texCoordLoc != -1 && texFactorLoc != -1 && texScaleLoc != -1;
+ }
+
+
+ private void loadShaderLocs(PShader shader) {
+ positionLoc = shader.getAttributeLoc("position");
+ if (positionLoc == -1) {
+ positionLoc = shader.getAttributeLoc("vertex");
+ }
+ colorLoc = shader.getAttributeLoc("color");
+ texCoordLoc = shader.getAttributeLoc("texCoord");
+ texFactorLoc = shader.getAttributeLoc("texFactor");
+ transformLoc = shader.getUniformLoc("transform");
+ if (transformLoc == -1) {
+ transformLoc = shader.getUniformLoc("transformMatrix");
+ }
+ texScaleLoc = shader.getUniformLoc("texScale");
+ if (texScaleLoc == -1) {
+ texScaleLoc = shader.getUniformLoc("texOffset");
+ }
+ }
+
+
+ private PShader getShader() {
+ // TODO: Perhaps a better way to handle the new 2D rendering would be to define a PShader2D
+ // subclass of PShader...
+ PShader shader;
+ if (twoShader == null) {
+ if (defTwoShader == null) {
+ String[] vertSource = pgl.loadVertexShader(defP2DShaderVertURL);
+ String[] fragSource = pgl.loadFragmentShader(defP2DShaderFragURL);
+ defTwoShader = new PShader(parent, vertSource, fragSource);
+ }
+ shader = defTwoShader;
+ } else {
+ shader = twoShader;
+ }
+// if (shader != defTwoShader) {
+ loadShaderLocs(shader);
+// }
+ return shader;
+ }
+
+ @Override
+ protected PShader getPolyShader(boolean lit, boolean tex) {
+ return super.getPolyShader(lit, tex);
+ }
+
+ private void setAttribs() {
+ pgl.vertexAttribPointer(positionLoc, 3, PGL.FLOAT, false, vertSize, 0);
+ pgl.enableVertexAttribArray(positionLoc);
+ if (-1 < texCoordLoc) {
+ pgl.vertexAttribPointer(texCoordLoc, 2, PGL.FLOAT, false, vertSize, 3*Float.BYTES);
+ pgl.enableVertexAttribArray(texCoordLoc);
+ }
+ pgl.vertexAttribPointer(colorLoc, 4, PGL.UNSIGNED_BYTE, true, vertSize, 5*Float.BYTES);
+ pgl.enableVertexAttribArray(colorLoc);
+ if (-1 < texFactorLoc) {
+ pgl.vertexAttribPointer(texFactorLoc, 1, PGL.FLOAT, false, vertSize, 6*Float.BYTES);
+ pgl.enableVertexAttribArray(texFactorLoc);
+ }
+ }
+
+
+ private void loadUniforms() {
+ //set matrix uniform
+ pgl.uniformMatrix4fv(transformLoc, 1, true, FloatBuffer.wrap(new PMatrix3D().get(null)));
+
+ //set texture info
+ pgl.activeTexture(PGL.TEXTURE0);
+ pgl.bindTexture(PGL.TEXTURE_2D, tex);
+ if (-1 < texScaleLoc) {
+ //enable uv scaling only for use-defined images, not for fonts
+ if (tex == imageTex) {
+ pgl.uniform2f(texScaleLoc, 1f/texWidth, 1f/texHeight);
+ } else {
+ pgl.uniform2f(texScaleLoc, 1, 1);
+ }
+ }
+ }
+
+
+ private void textureImpl(int glId) {
+ if (glId == tex) {
+ return; //texture is already bound; no work to be done
+ }
+
+ flushBuffer();
+ tex = glId;
+ }
+
+
+ private void check(int newVerts) {
+ if (usedVerts + newVerts > maxVerts) {
+ flushBuffer();
+ }
+ }
+
+
+ private void vertexImpl(float x, float y, float u, float v, int c, float f) {
+ int idx = usedVerts * 7;
+ //inline multiply only x and y to avoid an allocation and a few flops
+ vertexData[idx + 0] = projmodelview.m00*x + projmodelview.m01*y + projmodelview.m03;
+ vertexData[idx + 1] = projmodelview.m10*x + projmodelview.m11*y + projmodelview.m13;
+ vertexData[idx + 2] = depth;
+ vertexData[idx + 3] = u;
+ vertexData[idx + 4] = v;
+ vertexData[idx + 5] = Float.intBitsToFloat(c);
+ vertexData[idx + 6] = f;
+ usedVerts++;
+ }
+
+
+ private void vertexImpl(TessVertex vert) {
+ vertexImpl(vert.x, vert.y, vert.u, vert.v, vert.c, vert.f);
+ }
+
+
+ //one of POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, QUAD_STRIP, POLYGON
+ private int shapeType;
+ private int vertCount;
+ private TessVertex[] shapeVerts = new TessVertex[16]; //initial size is arbitrary
+
+ //list of indices (into shapeVerts array) at which a new contour begins
+ private int[] contours = new int[2]; //initial size is arbitrary
+ private int contourCount;
+
+
+ private void appendContour(int vertIndex) {
+ //dynamically expand contour array as needed
+ if (contourCount >= contours.length) {
+ contours = PApplet.expand(contours, contours.length * 2);
+ }
+
+ contours[contourCount] = vertIndex;
+ contourCount += 1;
+ }
+
+
+ //used by endShape() as a temporary to avoid unnecessary allocations
+ private double[] tempDoubles = new double[3];
+
+ //If this flag is set, then the next call to endShape() with shape type of POLYGON
+ //will triangulate blindly instead of going through the GLU tessellator (for performance).
+ //This is useful for shapes (like ellipse(), rect(), etc.) that we know will always be convex.
+ //TODO: Make this an optional argument to endShape()
+ //once we start integrating PGraphics4D into the rest of the codebase.
+ private boolean knownConvexPolygon = false;
+
+
+ private void shapeVertex(float x, float y, float u, float v, int c, float f) {
+ //avoid adding a duplicate because it will cause the GLU tess to fail spectacularly
+ //by spitting out-of-memory errors and passing null parameters to the combine() callback
+ //TODO: figure out why that happens and how to stop it
+ //(P2D renderer doesn't appear to have such a problem, so presumably there must be a way)
+ for (int i = 0; i < vertCount; ++i) {
+ if (shapeVerts[i].x == x && shapeVerts[i].y == y) {
+ return;
+ }
+ }
+
+ //dynamically expand input vertex array as needed
+ if (vertCount >= shapeVerts.length) {
+ shapeVerts = (TessVertex[]) PApplet.expand(shapeVerts, shapeVerts.length * 2);
+
+ //allocate objects for the new half of the array so we don't NPE ourselves
+ for (int i = shapeVerts.length/2; i < shapeVerts.length; ++i) {
+ shapeVerts[i] = new TessVertex();
+ }
+ }
+
+ shapeVerts[vertCount].set(x, y, u, v, c, f);
+ vertCount += 1;
+ }
+
+
+ private void triangle(float x1, float y1, float x2, float y2, float x3, float y3, int color) {
+ check(3);
+ vertexImpl(x1, y1, 0, 0, color, 0);
+ vertexImpl(x2, y2, 0, 0, color, 0);
+ vertexImpl(x3, y3, 0, 0, color, 0);
+ }
+
+
+ //below r == LINE_DETAIL_LIMIT, all lines will be drawn as plain rectangles
+ //instead of using fancy stroke rendering algorithms, since the result is visually indistinguishable
+ static final private float LINE_DETAIL_LIMIT = 1.0f;
+
+
+ private void singleLine(float x1, float y1, float x2, float y2, int color) {
+ float r = strokeWeight * 0.5f;
+
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+ float d = PApplet.sqrt(dx*dx + dy*dy);
+ float tx = dy / d * r;
+ float ty = dx / d * r;
+
+ if (strokeCap == PROJECT) {
+ x1 -= ty;
+ x2 += ty;
+ y1 -= tx;
+ y2 += tx;
+ }
+
+ triangle(x1 - tx, y1 + ty, x1 + tx, y1 - ty, x2 - tx, y2 + ty, color);
+ triangle(x2 + tx, y2 - ty, x2 - tx, y2 + ty, x1 + tx, y1 - ty, color);
+
+ if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) {
+ int segments = circleDetail(r, HALF_PI);
+ float step = HALF_PI / segments;
+ float c = PApplet.cos(step);
+ float s = PApplet.sin(step);
+ for (int i = 0; i < segments; ++i) {
+ //this is the equivalent of multiplying the vector by the 2x2 rotation matrix [[c -s] [s c]]
+ float nx = c * tx - s * ty;
+ float ny = s * tx + c * ty;
+
+ triangle(x2, y2, x2 + ty, y2 + tx, x2 + ny, y2 + nx, color);
+ triangle(x2, y2, x2 - tx, y2 + ty, x2 - nx, y2 + ny, color);
+ triangle(x1, y1, x1 - ty, y1 - tx, x1 - ny, y1 - nx, color);
+ triangle(x1, y1, x1 + tx, y1 - ty, x1 + nx, y1 - ny, color);
+
+ tx = nx;
+ ty = ny;
+ }
+ }
+ }
+
+
+ private void singlePoint(float x, float y, int color) {
+ float r = strokeWeight * 0.5f;
+ if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) {
+ int segments = circleDetail(r);
+ float step = QUARTER_PI / segments;
+
+ float x1 = 0, y1 = r;
+ float c = PApplet.cos(step);
+ float s = PApplet.sin(step);
+ for (int i = 0; i < segments; ++i) {
+ //this is the equivalent of multiplying the vector by the 2x2 rotation matrix [[c -s] [s c]]
+ float x2 = c * x1 - s * y1;
+ float y2 = s * x1 + c * y1;
+
+ triangle(x, y, x + x1, y + y1, x + x2, y + y2, strokeColor);
+ triangle(x, y, x + x1, y - y1, x + x2, y - y2, strokeColor);
+ triangle(x, y, x - x1, y + y1, x - x2, y + y2, strokeColor);
+ triangle(x, y, x - x1, y - y1, x - x2, y - y2, strokeColor);
+
+ triangle(x, y, x + y1, y + x1, x + y2, y + x2, strokeColor);
+ triangle(x, y, x + y1, y - x1, x + y2, y - x2, strokeColor);
+ triangle(x, y, x - y1, y + x1, x - y2, y + x2, strokeColor);
+ triangle(x, y, x - y1, y - x1, x - y2, y - x2, strokeColor);
+
+ x1 = x2;
+ y1 = y2;
+ }
+ } else {
+ triangle(x - r, y - r, x + r, y - r, x - r, y + r, color);
+ triangle(x + r, y - r, x - r, y + r, x + r, y + r, color);
+ }
+ }
+
+
+ private StrokeRenderer sr = new StrokeRenderer();
+
+
+ private class StrokeRenderer {
+ int lineVertexCount;
+ float fx, fy;
+ float sx, sy, sdx, sdy;
+ float px, py, pdx, pdy;
+ float lx, ly;
+ float r;
+
+
+ void arcJoin(float x, float y, float dx1, float dy1, float dx2, float dy2) {
+ //we don't need to normalize before doing these products
+ //since the vectors are the same length and only used as arguments to atan2()
+ float cross = dx1 * dy2 - dy1 * dx2;
+ float dot = dx1 * dx2 + dy1 * dy2;
+ float theta = PApplet.atan2(cross, dot);
+ int segments = circleDetail(r, theta);
+ float px = x + dx1, py = y + dy1;
+ if (segments > 1) {
+ float c = PApplet.cos(theta / segments);
+ float s = PApplet.sin(theta / segments);
+ for (int i = 1; i < segments; ++i) {
+ //this is the equivalent of multiplying the vector by the 2x2 rotation matrix [[c -s] [s c]]
+ float tempx = c * dx1 - s * dy1;
+ dy1 = s * dx1 + c * dy1;
+ dx1 = tempx;
+
+ float nx = x + dx1;
+ float ny = y + dy1;
+ triangle(x, y, px, py, nx, ny, strokeColor);
+ px = nx;
+ py = ny;
+ }
+ }
+ triangle(x, y, px, py, x + dx2, y + dy2, strokeColor);
+ }
+
+ void beginLine() {
+ lineVertexCount = 0;
+ r = strokeWeight * 0.5f;
+ }
+
+ void lineVertex(float x, float y) {
+ //disallow adding consecutive duplicate vertices,
+ //as it is pointless and just creates an extra edge case
+ if (lineVertexCount > 0 && x == lx && y == ly) {
+ return;
+ }
+
+ if (lineVertexCount == 0) {
+ fx = x;
+ fy = y;
+ } else if (r < LINE_DETAIL_LIMIT) {
+ singleLine(lx, ly, x, y, strokeColor);
+ } else if (lineVertexCount == 1) {
+ sx = x;
+ sy = y;
+ } else {
+ //calculate normalized direction vectors for each leg
+ float leg1x = lx - px;
+ float leg1y = ly - py;
+ float leg2x = x - lx;
+ float leg2y = y - ly;
+ float len1 = PApplet.sqrt(leg1x * leg1x + leg1y * leg1y);
+ float len2 = PApplet.sqrt(leg2x * leg2x + leg2y * leg2y);
+ leg1x /= len1;
+ leg1y /= len1;
+ leg2x /= len2;
+ leg2y /= len2;
+
+ float legDot = -leg1x * leg2x - leg1y * leg2y;
+ float cosPiOver15 = 0.97815f;
+ if (strokeJoin == BEVEL || strokeJoin == ROUND || legDot > cosPiOver15 || legDot < -0.999) {
+ float tx = leg1y * r;
+ float ty = -leg1x * r;
+
+ if (lineVertexCount == 2) {
+ sdx = tx;
+ sdy = ty;
+ } else {
+ triangle(px - pdx, py - pdy, px + pdx, py + pdy, lx - tx, ly - ty, strokeColor);
+ triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor);
+ }
+
+ float nx = leg2y * r;
+ float ny = -leg2x * r;
+
+ float legCross = leg1x * leg2y - leg1y * leg2x;
+ if (strokeJoin == ROUND) {
+ if (legCross > 0) {
+ arcJoin(lx, ly, tx, ty, nx, ny);
+ } else {
+ arcJoin(lx, ly, -tx, -ty, -nx, -ny);
+ }
+ } else if (legCross > 0) {
+ triangle(lx, ly, lx + tx, ly + ty, lx + nx, ly + ny, strokeColor);
+ } else {
+ triangle(lx, ly, lx - tx, ly - ty, lx - nx, ly - ny, strokeColor);
+ }
+
+ pdx = nx;
+ pdy = ny;
+ } else { //miter joint
+ //find the bisecting vector
+ float x1 = leg2x - leg1x;
+ float y1 = leg2y - leg1y;
+ //find a (normalized) vector perpendicular to one of the legs
+ float x2 = leg1y;
+ float y2 = -leg1x;
+ //scale the bisecting vector to the correct length using magic (not sure how to explain this one)
+ float dot = x1 * x2 + y1 * y2;
+ float bx = x1 * (r / dot);
+ float by = y1 * (r / dot);
+
+ if (lineVertexCount == 2) {
+ sdx = bx;
+ sdy = by;
+ } else {
+ triangle(px - pdx, py - pdy, px + pdx, py + pdy, lx - bx, ly - by, strokeColor);
+ triangle(px + pdx, py + pdy, lx - bx, ly - by, lx + bx, ly + by, strokeColor);
+ }
+
+ pdx = bx;
+ pdy = by;
+ }
+ }
+
+ px = lx;
+ py = ly;
+ lx = x;
+ ly = y;
+
+ lineVertexCount += 1;
+ }
+
+ void lineCap(float x, float y, float dx, float dy) {
+ int segments = circleDetail(r, HALF_PI);
+ float px = dy, py = -dx;
+ if (segments > 1) {
+ float c = PApplet.cos(HALF_PI / segments);
+ float s = PApplet.sin(HALF_PI / segments);
+ for (int i = 1; i < segments; ++i) {
+ //this is the equivalent of multiplying the vector by the 2x2 rotation matrix [[c -s] [s c]]
+ float nx = c * px - s * py;
+ float ny = s * px + c * py;
+ triangle(x, y, x + px, y + py, x + nx, y + ny, strokeColor);
+ triangle(x, y, x - py, y + px, x - ny, y + nx, strokeColor);
+ px = nx;
+ py = ny;
+ }
+ }
+ triangle(x, y, x + px, y + py, x + dx, y + dy, strokeColor);
+ triangle(x, y, x - py, y + px, x - dy, y + dx, strokeColor);
+ }
+
+ void endLine(boolean closed) {
+ if (lineVertexCount < 2) {
+ return;
+ }
+
+ if (lineVertexCount == 2) {
+ singleLine(px, py, lx, ly, strokeColor);
+ return;
+ }
+
+ if (r < LINE_DETAIL_LIMIT) {
+ if (closed) {
+ singleLine(lx, ly, fx, fy, strokeColor);
+ }
+ return;
+ }
+
+ if (closed) {
+ //draw the last two legs
+ lineVertex(fx, fy);
+ lineVertex(sx, sy);
+
+ //connect first and second vertices
+ triangle(px - pdx, py - pdy, px + pdx, py + pdy, sx - sdx, sy - sdy, strokeColor);
+ triangle(px + pdx, py + pdy, sx - sdx, sy - sdy, sx + sdx, sy + sdy, strokeColor);
+ } else {
+ //draw last line (with cap)
+ float dx = lx - px;
+ float dy = ly - py;
+ float d = PApplet.sqrt(dx*dx + dy*dy);
+ float tx = dy / d * r;
+ float ty = -dx / d * r;
+
+ if (strokeCap == PROJECT) {
+ lx -= ty;
+ ly += tx;
+ }
+
+ triangle(px - pdx, py - pdy, px + pdx, py + pdy, lx - tx, ly - ty, strokeColor);
+ triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor);
+
+ if (strokeCap == ROUND) {
+ lineCap(lx, ly, -ty, tx);
+ }
+
+ //draw first line (with cap)
+ dx = fx - sx;
+ dy = fy - sy;
+ d = PApplet.sqrt(dx*dx + dy*dy);
+ tx = dy / d * r;
+ ty = -dx / d * r;
+
+ if (strokeCap == PROJECT) {
+ fx -= ty;
+ fy += tx;
+ }
+
+ triangle(sx - sdx, sy - sdy, sx + sdx, sy + sdy, fx + tx, fy + ty, strokeColor);
+ triangle(sx + sdx, sy + sdy, fx + tx, fy + ty, fx - tx, fy - ty, strokeColor);
+
+ if (strokeCap == ROUND) {
+ lineCap(fx, fy, -ty, tx);
+ }
+ }
+ }
+ }
+
+
+ //returns the total number of points needed to approximate an arc of a given radius and extent
+ int circleDetail(float radius, float delta) {
+ //this serves as a rough approximation of how much the longest axis
+ //of an ellipse will be scaled by a given matrix
+ //(in other words, the amount by which its on-screen size changes)
+ float sxi = projmodelview.m00 * width / 2;
+ float syi = projmodelview.m10 * height / 2;
+ float sxj = projmodelview.m01 * width / 2;
+ float syj = projmodelview.m11 * height / 2;
+ float Imag2 = sxi * sxi + syi * syi;
+ float Jmag2 = sxj * sxj + syj * syj;
+ float ellipseDetailMultiplier = PApplet.sqrt(PApplet.max(Imag2, Jmag2));
+ radius *= ellipseDetailMultiplier;
+ return (int)(PApplet.min(127, PApplet.sqrt(radius) / QUARTER_PI * PApplet.abs(delta) * 0.75f) + 1);
+ }
+
+
+ //returns the number of points per quadrant needed to approximate a circle of a given radius
+ int circleDetail(float radius) {
+ return circleDetail(radius, QUARTER_PI);
+ }
+
+
+ private class TessVertex {
+ float x, y, u, v;
+ int c;
+ float f; //1.0 if textured, 0.0 if flat
+
+ public TessVertex() {
+ //no-op
+ }
+
+ public TessVertex(float x, float y, float u, float v, int c, float f) {
+ set(x, y, u, v, c, f);
+ }
+
+ public void set(float x, float y, float u, float v, int c, float f) {
+ this.x = x;
+ this.y = y;
+ this.u = u;
+ this.v = v;
+ this.c = c;
+ this.f = f;
+ }
+
+ @Override
+ public String toString() {
+ return x + ", " + y;
+ }
+ }
+}
diff --git a/core/src/processing/opengl/PGraphics3D.java b/libs/processing-core/src/main/java/processing/opengl/PGraphics3D.java
similarity index 85%
rename from core/src/processing/opengl/PGraphics3D.java
rename to libs/processing-core/src/main/java/processing/opengl/PGraphics3D.java
index 77edcc9f0..5a5e1c3b3 100644
--- a/core/src/processing/opengl/PGraphics3D.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PGraphics3D.java
@@ -3,11 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2012 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,19 +20,15 @@
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.opengl;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
-
-import processing.core.PApplet;
-import processing.core.PConstants;
import processing.core.PGraphics;
import processing.core.PShape;
import processing.core.PShapeOBJ;
+
public class PGraphics3D extends PGraphicsOpenGL {
public PGraphics3D() {
@@ -85,12 +83,12 @@ protected void defaultCamera() {
@Override
protected void begin2D() {
pushProjection();
- ortho(0, width, 0, height, -1, +1);
+ ortho(-width/2f, width/2f, -height/2f, height/2f);
pushMatrix();
// Set camera for 2D rendering, it simply centers at (width/2, height/2)
- float centerX = width/2;
- float centerY = height/2;
+ float centerX = width/2f;
+ float centerY = height/2f;
modelview.reset();
modelview.translate(-centerX, -centerY);
@@ -111,6 +109,7 @@ protected void end2D() {
}
+
//////////////////////////////////////////////////////////////
// SHAPE I/O
@@ -127,37 +126,39 @@ static protected PShape loadShapeImpl(PGraphics pg, String filename,
if (extension.equals("obj")) {
obj = new PShapeOBJ(pg.parent, filename);
-
- } else if (extension.equals("objz")) {
- try {
- // TODO: The obj file can be read from the gzip, but if it refers to
- // a materials file and texture images, those must be contained in the
- // data folder, cannot be inside the gzip.
- InputStream input =
- new GZIPInputStream(pg.parent.createInput(filename));
- obj = new PShapeOBJ(pg.parent, PApplet.createReader(input));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if (obj != null) {
int prevTextureMode = pg.textureMode;
pg.textureMode = NORMAL;
- PShapeOpenGL p3d = PShapeOpenGL.createShape3D((PGraphicsOpenGL)pg, obj);
+ PShapeOpenGL p3d = PShapeOpenGL.createShape((PGraphicsOpenGL)pg, obj);
pg.textureMode = prevTextureMode;
return p3d;
- } else {
- return null;
}
+ return null;
}
+
//////////////////////////////////////////////////////////////
// SHAPE CREATION
+// @Override
+// protected PShape createShapeFamily(int type) {
+// PShape shape = new PShapeOpenGL(this, type);
+// shape.set3D(true);
+// return shape;
+// }
+//
+//
+// @Override
+// protected PShape createShapePrimitive(int kind, float... p) {
+// PShape shape = new PShapeOpenGL(this, kind, p);
+// shape.set3D(true);
+// return shape;
+// }
+
+
+ /*
@Override
public PShape createShape(PShape source) {
return PShapeOpenGL.createShape3D(this, source);
@@ -191,7 +192,7 @@ static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int type) {
} else if (type == PShape.GEOMETRY) {
shape = new PShapeOpenGL(pg, PShape.GEOMETRY);
}
- shape.is3D(true);
+ shape.set3D(true);
return shape;
}
@@ -250,6 +251,7 @@ static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg,
}
shape = new PShapeOpenGL(pg, PShape.PRIMITIVE);
shape.setKind(ARC);
+
} else if (kind == BOX) {
if (len != 1 && len != 3) {
showWarning("Wrong number of parameters");
@@ -272,7 +274,8 @@ static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg,
shape.setParams(p);
}
- shape.is3D(true);
+ shape.set3D(true);
return shape;
}
+ */
}
\ No newline at end of file
diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/libs/processing-core/src/main/java/processing/opengl/PGraphicsOpenGL.java
similarity index 70%
rename from core/src/processing/opengl/PGraphicsOpenGL.java
rename to libs/processing-core/src/main/java/processing/opengl/PGraphicsOpenGL.java
index fa8190ea9..478a2f13b 100644
--- a/core/src/processing/opengl/PGraphicsOpenGL.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PGraphicsOpenGL.java
@@ -3,11 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2004-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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 version 2.1 as published by the Free Software Foundation.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,32 +20,82 @@
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.opengl;
+import processing.android.AppComponent;
import processing.core.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
import java.net.URL;
import java.nio.*;
import java.util.*;
+import android.content.Context;
+import android.os.Environment;
+import android.view.SurfaceHolder;
+
+import static android.os.Environment.isExternalStorageRemovable;
+
/**
* OpenGL renderer.
- *
*/
public class PGraphicsOpenGL extends PGraphics {
/** Interface between Processing and OpenGL */
public PGL pgl;
/** The renderer currently in use. */
- protected PGraphicsOpenGL currentPG;
+ public PGraphicsOpenGL currentPG;
/** Font cache for texture objects. */
protected WeakHashMap fontMap;
// ........................................................
+ // Disposal of native resources
+ // Using the technique alternative to finalization described in:
+ // http://www.oracle.com/technetwork/articles/java/finalization-137655.html
+ private static ReferenceQueue refQueue = new ReferenceQueue<>();
+ private static List> reachableWeakReferences =
+ new LinkedList<>();
+
+ static final private int MAX_DRAIN_GLRES_ITERATIONS = 10;
+
+ static void drainRefQueueBounded() {
+ int iterations = 0;
+ while (iterations < MAX_DRAIN_GLRES_ITERATIONS) {
+ Disposable extends Object> res =
+ (Disposable extends Object>) refQueue.poll();
+ if (res == null) {
+ break;
+ }
+ res.dispose();
+ ++iterations;
+ }
+ }
+
+ private static abstract class Disposable extends WeakReference {
+ protected Disposable(T obj) {
+ super(obj, refQueue);
+ drainRefQueueBounded();
+ reachableWeakReferences.add(this);
+ }
+
+ public void dispose() {
+ reachableWeakReferences.remove(this);
+ disposeNative();
+ }
+
+ abstract public void disposeNative();
+ }
+
// Basic rendering parameters:
/** Whether the PGraphics object is ready to render or not. */
@@ -64,36 +116,40 @@ public class PGraphicsOpenGL extends PGraphics {
/** Current flush mode. */
protected int flushMode = FLUSH_WHEN_FULL;
+
// ........................................................
// VBOs for immediate rendering:
- public int glPolyVertex;
- public int glPolyColor;
- public int glPolyNormal;
- public int glPolyTexcoord;
- public int glPolyAmbient;
- public int glPolySpecular;
- public int glPolyEmissive;
- public int glPolyShininess;
- public int glPolyIndex;
+ protected VertexBuffer bufPolyVertex;
+ protected VertexBuffer bufPolyColor;
+ protected VertexBuffer bufPolyNormal;
+ protected VertexBuffer bufPolyTexcoord;
+ protected VertexBuffer bufPolyAmbient;
+ protected VertexBuffer bufPolySpecular;
+ protected VertexBuffer bufPolyEmissive;
+ protected VertexBuffer bufPolyShininess;
+ protected VertexBuffer bufPolyIndex;
protected boolean polyBuffersCreated = false;
protected int polyBuffersContext;
- public int glLineVertex;
- public int glLineColor;
- public int glLineAttrib;
- public int glLineIndex;
+ protected VertexBuffer bufLineVertex;
+ protected VertexBuffer bufLineColor;
+ protected VertexBuffer bufLineAttrib;
+ protected VertexBuffer bufLineIndex;
protected boolean lineBuffersCreated = false;
protected int lineBuffersContext;
- public int glPointVertex;
- public int glPointColor;
- public int glPointAttrib;
- public int glPointIndex;
+ protected VertexBuffer bufPointVertex;
+ protected VertexBuffer bufPointColor;
+ protected VertexBuffer bufPointAttrib;
+ protected VertexBuffer bufPointIndex;
protected boolean pointBuffersCreated = false;
protected int pointBuffersContext;
+ // Generic vertex attributes (only for polys)
+ protected AttributeMap polyAttribs;
+
static protected final int INIT_VERTEX_BUFFER_SIZE = 256;
static protected final int INIT_INDEX_BUFFER_SIZE = 512;
@@ -110,6 +166,8 @@ public class PGraphicsOpenGL extends PGraphics {
static public boolean packedDepthStencilSupported;
static public boolean anisoSamplingSupported;
static public boolean blendEqSupported;
+ static public boolean readBufferSupported;
+ static public boolean drawBufferSupported;
/** Some hardware limits */
static public int maxTextureSize;
@@ -127,54 +185,35 @@ public class PGraphicsOpenGL extends PGraphics {
// ........................................................
- // GL resources:
-
- static protected HashMap glTextureObjects =
- new HashMap();
- static protected HashMap glVertexBuffers =
- new HashMap();
- static protected HashMap glFrameBuffers =
- new HashMap();
- static protected HashMap glRenderBuffers =
- new HashMap();
- static protected HashMap glslPrograms =
- new HashMap();
- static protected HashMap glslVertexShaders =
- new HashMap();
- static protected HashMap glslFragmentShaders =
- new HashMap();
-
- // ........................................................
-
// Shaders
static protected URL defColorShaderVertURL =
- PGraphicsOpenGL.class.getResource("ColorVert.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/ColorVert.glsl");
static protected URL defTextureShaderVertURL =
- PGraphicsOpenGL.class.getResource("TextureVert.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/TexVert.glsl");
static protected URL defLightShaderVertURL =
- PGraphicsOpenGL.class.getResource("LightVert.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/LightVert.glsl");
static protected URL defTexlightShaderVertURL =
- PGraphicsOpenGL.class.getResource("TexlightVert.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/TexLightVert.glsl");
static protected URL defColorShaderFragURL =
- PGraphicsOpenGL.class.getResource("ColorFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/ColorFrag.glsl");
static protected URL defTextureShaderFragURL =
- PGraphicsOpenGL.class.getResource("TextureFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/TexFrag.glsl");
static protected URL defLightShaderFragURL =
- PGraphicsOpenGL.class.getResource("LightFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/LightFrag.glsl");
static protected URL defTexlightShaderFragURL =
- PGraphicsOpenGL.class.getResource("TexlightFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/TexLightFrag.glsl");
static protected URL defLineShaderVertURL =
- PGraphicsOpenGL.class.getResource("LineVert.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/LineVert.glsl");
static protected URL defLineShaderFragURL =
- PGraphicsOpenGL.class.getResource("LineFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/LineFrag.glsl");
static protected URL defPointShaderVertURL =
- PGraphicsOpenGL.class.getResource("PointVert.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/PointVert.glsl");
static protected URL defPointShaderFragURL =
- PGraphicsOpenGL.class.getResource("PointFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/PointFrag.glsl");
static protected URL maskShaderFragURL =
- PGraphicsOpenGL.class.getResource("MaskFrag.glsl");
+ PGraphicsOpenGL.class.getResource("/assets/shaders/MaskFrag.glsl");
protected PShader defColorShader;
protected PShader defTextureShader;
@@ -195,7 +234,30 @@ public class PGraphicsOpenGL extends PGraphics {
protected InGeometry inGeo;
protected TessGeometry tessGeo;
protected TexCache texCache;
- static protected Tessellator tessellator;
+ protected Tessellator tessellator;
+
+ // ........................................................
+
+ // Depth sorter
+
+ protected DepthSorter sorter;
+ protected boolean isDepthSortingEnabled;
+
+ // ........................................................
+
+ // Async pixel reader
+
+ protected AsyncPixelReader asyncPixelReader;
+ protected boolean asyncPixelReaderInitialized;
+
+ // Keeps track of ongoing transfers so they can be finished.
+ // Set is copied to the List when we need to iterate it
+ // so that readers can remove themselves from the Set during
+ // iteration if they don't have any ongoing transfers.
+ protected static final Set
+ ongoingPixelTransfers = new HashSet<>();
+ protected static final List
+ ongoingPixelTransfersIterable = new ArrayList<>();
// ........................................................
@@ -211,12 +273,24 @@ public class PGraphicsOpenGL extends PGraphics {
/** Aspect ratio of camera's view. */
public float cameraAspect;
- /** Actual position of the camera. */
- protected float cameraEyeX, cameraEyeY, cameraEyeZ;
+ /** Default camera properties. */
+ public float defCameraFOV;
+ public float defCameraX, defCameraY, defCameraZ;
+ public float defCameraNear, defCameraFar;
+ public float defCameraAspect;
+
+ /** Distance between camera eye and center. */
+ protected float eyeDist;
/** Flag to indicate that we are inside beginCamera/endCamera block. */
protected boolean manipulatingCamera;
+ /**
+ * Sets the coordinates to "first person" setting: Y axis up, origin at
+ * screen center
+ */
+ protected boolean cameraUp = false;
+
// ........................................................
// All the matrices required for camera and geometry transformations.
@@ -236,14 +310,17 @@ public class PGraphicsOpenGL extends PGraphics {
// Useful to have around.
static protected PMatrix3D identity = new PMatrix3D();
- protected boolean matricesAllocated = false;
-
/**
* Marks when changes to the size have occurred, so that the camera
* will be reset in beginDraw().
*/
protected boolean sized;
+ /**
+ * Marks when some changes have occurred, to the surface view.
+ */
+ protected boolean changed;
+
static protected final int MATRIX_STACK_DEPTH = 32;
protected int modelviewStackDepth;
@@ -264,6 +341,18 @@ public class PGraphicsOpenGL extends PGraphics {
/** Projection matrix stack **/
protected float[][] projectionStack = new float[MATRIX_STACK_DEPTH][16];
+
+ /** Matrix that transform coordinates to the eye coordinate system **/
+ protected PMatrix3D eyeMatrix;
+
+ /** Matrix that transform coordinates to the system that results from appling the modelview transformation **/
+ protected PMatrix3D objMatrix;
+
+ /** Vectors defining the eye axes **/
+ public float forwardX, forwardY, forwardZ;
+ public float rightX, rightY, rightZ;
+ public float upX, upY, upZ;
+
// ........................................................
// Lights:
@@ -311,8 +400,6 @@ public class PGraphicsOpenGL extends PGraphics {
public float currentLightFalloffLinear;
public float currentLightFalloffQuadratic;
- protected boolean lightsAllocated = false;
-
// ........................................................
// Texturing:
@@ -364,10 +451,10 @@ public class PGraphicsOpenGL extends PGraphics {
// Screen surface:
/** Texture containing the current frame */
- protected Texture texture;
+ protected Texture texture = null;
/** Texture containing the previous frame */
- protected Texture ptexture;
+ protected Texture ptexture = null;
/** IntBuffer wrapping the pixels array. */
protected IntBuffer pixelBuffer;
@@ -379,15 +466,11 @@ public class PGraphicsOpenGL extends PGraphics {
protected IntBuffer nativePixelBuffer;
/** texture used to apply a filter on the screen image. */
- protected Texture filterTexture;
+ protected Texture filterTexture = null;
/** PImage that wraps filterTexture. */
protected PImage filterImage;
- /** Flag to indicate that pixels array is up-to-date and
- * ready to be manipulated through the set()/get() methods */
- protected boolean arePixelsUpToDate;
-
// ........................................................
// Utility variables:
@@ -395,9 +478,6 @@ public class PGraphicsOpenGL extends PGraphics {
/** True if we are inside a beginDraw()/endDraw() block. */
protected boolean drawing = false;
- /** Used to indicate an OpenGL surface recreation */
- protected boolean restoreSurface = false;
-
/** Used to detect continuous use of the smooth/noSmooth functions */
protected boolean smoothDisabled = false;
protected int smoothCallCount = 0;
@@ -416,10 +496,6 @@ public class PGraphicsOpenGL extends PGraphics {
/** Viewport dimensions. */
protected IntBuffer viewport;
- /** Used to register calls to glClear. */
- protected boolean clearColorBuffer;
- protected boolean clearColorBuffer0;
-
protected boolean openContour = false;
protected boolean breakShape = false;
protected boolean defaultEdges = false;
@@ -456,6 +532,21 @@ public class PGraphicsOpenGL extends PGraphics {
// ........................................................
+ // Variables used in ray casting:
+
+ protected PVector[] ray;
+ protected PVector hit = new PVector();
+ protected PVector screen = new PVector();
+
+ protected PVector origInObjCoord = new PVector();
+ protected PVector hitInObjCoord = new PVector();
+ protected PVector dirInObjCoord = new PVector();
+
+ protected PVector origInWorldCoord = new PVector();
+ protected PVector dirInWorldCoord = new PVector();
+
+ // ........................................................
+
// Error strings:
static final String OPENGL_THREAD_ERROR =
@@ -509,10 +600,14 @@ public class PGraphicsOpenGL extends PGraphics {
static final String NO_COLOR_SHADER_ERROR =
"Your shader needs to be of COLOR type " +
"to render this geometry properly, using default shader instead.";
- static final String TOO_LONG_STROKE_PATH_ERROR =
- "Stroke path is too long, some bevel triangles won't be added";
static final String TESSELLATION_ERROR =
"Tessellation Error: %1$s";
+ static final String GL_THREAD_NOT_CURRENT =
+ "You are trying to draw outside OpenGL's animation thread.\n" +
+ "Place all drawing commands in the draw() function, or inside\n" +
+ "your own functions as long as they are called from draw(),\n" +
+ "but not in event handling functions such as keyPressed()\n" +
+ "or mousePressed().";
//////////////////////////////////////////////////////////////
@@ -522,10 +617,6 @@ public class PGraphicsOpenGL extends PGraphics {
public PGraphicsOpenGL() {
pgl = createPGL(this);
- if (tessellator == null) {
- tessellator = new Tessellator();
- }
-
if (intBuffer == null) {
intBuffer = PGL.allocateIntBuffer(2);
floatBuffer = PGL.allocateFloatBuffer(2);
@@ -533,14 +624,41 @@ public PGraphicsOpenGL() {
viewport = PGL.allocateIntBuffer(4);
- inGeo = newInGeometry(this, IMMEDIATE);
- tessGeo = newTessGeometry(this, IMMEDIATE);
+ polyAttribs = newAttributeMap();
+ inGeo = newInGeometry(this, polyAttribs, IMMEDIATE);
+ tessGeo = newTessGeometry(this, polyAttribs, IMMEDIATE);
texCache = newTexCache(this);
+ projection = new PMatrix3D();
+ camera = new PMatrix3D();
+ cameraInv = new PMatrix3D();
+ modelview = new PMatrix3D();
+ modelviewInv = new PMatrix3D();
+ projmodelview = new PMatrix3D();
+
+ lightType = new int[PGL.MAX_LIGHTS];
+ lightPosition = new float[4 * PGL.MAX_LIGHTS];
+ lightNormal = new float[3 * PGL.MAX_LIGHTS];
+ lightAmbient = new float[3 * PGL.MAX_LIGHTS];
+ lightDiffuse = new float[3 * PGL.MAX_LIGHTS];
+ lightSpecular = new float[3 * PGL.MAX_LIGHTS];
+ lightFalloffCoefficients = new float[3 * PGL.MAX_LIGHTS];
+ lightSpotParameters = new float[2 * PGL.MAX_LIGHTS];
+ currentLightSpecular = new float[3];
+
initialized = false;
}
+ @Override
+ public void setParent(PApplet parent) {
+ super.setParent(parent);
+ if (pgl != null) {
+ pgl.sketch = parent;
+ }
+ }
+
+
@Override
public void setPrimary(boolean primary) {
super.setPrimary(primary);
@@ -549,6 +667,9 @@ public void setPrimary(boolean primary) {
if (primary) {
fbStack = new FrameBuffer[FB_STACK_DEPTH];
fontMap = new WeakHashMap();
+ tessellator = new Tessellator();
+ } else {
+ tessellator = getPrimaryPG().tessellator;
}
}
@@ -560,654 +681,779 @@ public void setPrimary(boolean primary) {
@Override
- public void setFrameRate(float frameRate) {
- pgl.setFps(frameRate);
+ public void surfaceChanged() {
+ changed = true;
}
@Override
- public void setSize(int iwidth, int iheight) {
- width = iwidth;
- height = iheight;
-
- allocate();
-
- // init perspective projection based on new dimensions
- cameraFOV = 60 * DEG_TO_RAD; // at least for now
- cameraX = width / 2.0f;
- cameraY = height / 2.0f;
- cameraZ = cameraY / ((float) Math.tan(cameraFOV / 2.0f));
- cameraNear = cameraZ / 10.0f;
- cameraFar = cameraZ * 10.0f;
- cameraAspect = (float) width / (float) height;
-
- sized = true;
+ public void reset() {
+ pgl.resetFBOLayer();
+ restartPGL();
}
- /**
- * Called by resize(), this handles creating the actual GLCanvas the
- * first time around, or simply resizing it on subsequent calls.
- * There is no pixel array to allocate for an OpenGL canvas
- * because OpenGL's pixel buffer is all handled internally.
- */
@Override
- protected void allocate() {
- super.allocate();
+ public void setSize(int iwidth, int iheight) {
+ sized = iwidth != width || iheight != height;
+ super.setSize(iwidth, iheight);
- if (!matricesAllocated) {
- projection = new PMatrix3D();
- camera = new PMatrix3D();
- cameraInv = new PMatrix3D();
- modelview = new PMatrix3D();
- modelviewInv = new PMatrix3D();
- projmodelview = new PMatrix3D();
- matricesAllocated = true;
- }
+ updatePixelSize();
- if (!lightsAllocated) {
- lightType = new int[PGL.MAX_LIGHTS];
- lightPosition = new float[4 * PGL.MAX_LIGHTS];
- lightNormal = new float[3 * PGL.MAX_LIGHTS];
- lightAmbient = new float[3 * PGL.MAX_LIGHTS];
- lightDiffuse = new float[3 * PGL.MAX_LIGHTS];
- lightSpecular = new float[3 * PGL.MAX_LIGHTS];
- lightFalloffCoefficients = new float[3 * PGL.MAX_LIGHTS];
- lightSpotParameters = new float[2 * PGL.MAX_LIGHTS];
- currentLightSpecular = new float[3];
- lightsAllocated = true;
- }
+ // init perspective projection based on new dimensions
+ defCameraFOV = 60 * DEG_TO_RAD; // at least for now
+ defCameraX = width / 2.0f;
+ defCameraY = height / 2.0f;
+ defCameraZ = defCameraY / ((float) Math.tan(defCameraFOV / 2.0f));
+ defCameraNear = defCameraZ / 10.0f;
+ defCameraFar = defCameraZ * 10.0f;
+ defCameraAspect = (float) width / (float) height;
+
+ cameraFOV = defCameraFOV;
+ cameraX = defCameraX;
+ cameraY = defCameraY;
+ cameraZ = defCameraZ;
+ cameraNear = defCameraNear;
+ cameraFar = defCameraFar;
+ cameraAspect = defCameraAspect;
}
@Override
public void dispose() { // PGraphics
- super.dispose();
-
- if (primarySurface) {
- // Swap buffers the end to make sure that no
- // garbage is shown on the screen, this particularly
- // affects non-interactive sketches on windows that
- // render only 1 frame, so no enough rendering
- // iterations have been conducted so far to properly
- // initialize all the buffers.
- pgl.swapBuffers();
+ if (asyncPixelReader != null) {
+ asyncPixelReader.dispose();
+ asyncPixelReader = null;
}
- finalizePolyBuffers();
- finalizeLineBuffers();
- finalizePointBuffers();
-
- deleteSurfaceTextures();
- if (primarySurface) {
- deleteDefaultShaders();
- } else {
- if (offscreenFramebuffer != null) {
- offscreenFramebuffer.dispose();
+ if (!primaryGraphics) {
+ deleteSurfaceTextures();
+ FrameBuffer ofb = offscreenFramebuffer;
+ FrameBuffer mfb = multisampleFramebuffer;
+ if (ofb != null) {
+ ofb.dispose();
}
- if (multisampleFramebuffer != null) {
- multisampleFramebuffer.dispose();
+ if (mfb != null) {
+ mfb.dispose();
}
}
- deleteFinalizedGLResources(pgl);
+ pgl.dispose();
- if (primarySurface) {
- pgl.deleteSurface();
- }
+ super.dispose();
}
- @Override
- protected void finalize() throws Throwable {
- try {
- finalizePolyBuffers();
- finalizeLineBuffers();
- finalizePointBuffers();
-
- deleteSurfaceTextures();
- if (!primarySurface) {
- if (offscreenFramebuffer != null) {
- offscreenFramebuffer.dispose();
- offscreenFramebuffer = null;
- }
- if (multisampleFramebuffer != null) {
- multisampleFramebuffer.dispose();
- multisampleFramebuffer = null;
- }
- }
- } finally {
- super.finalize();
- }
- }
protected void setFlushMode(int mode) {
flushMode = mode;
}
+ protected void updatePixelSize() {
+ float f = pgl.getPixelScale();
+ pixelWidth = (int)(width * f);
+ pixelHeight = (int)(height * f);
+ }
+
+
//////////////////////////////////////////////////////////////
- // IMAGE METADATA FOR THIS RENDERER
+ // PLATFORM-SPECIFIC CODE (Java, Android, etc.). Needs to be manually edited.
+
+
+ // Factory method
+ protected PGL createPGL(PGraphicsOpenGL pg) { // ignore
+// return new PJOGL(pg);
+ return new PGLES(pg);
+ }
+ /*
@Override
- public void setCache(PImage image, Object storage) {
- getPrimaryPG().cacheMap.put(image, storage);
+ // Java only
+ public PSurface createSurface() { // ignore
+ return surface = new PSurfaceJOGL(this);
+ }
+*/
+
+ @Override
+ // Android only
+ public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore
+ if (reset) pgl.resetFBOLayer();
+ return new PSurfaceGLES(this, component, holder);
}
@Override
- public Object getCache(PImage image) {
- return getPrimaryPG().cacheMap.get(image);
+ // Android only
+ public void setFrameRate(float frameRate) {
+ pgl.setFrameRate(frameRate);
}
@Override
- public void removeCache(PImage image) {
- getPrimaryPG().cacheMap.remove(image);
+ protected boolean isLooping() { // ignore
+ return super.isLooping();
}
- //////////////////////////////////////////////////////////////
+ public boolean saveImpl(String filename) {
+ return super.save(filename); // ASYNC save frame using PBOs not yet available on Android
+ /*
+ if (getHint(DISABLE_ASYNC_SAVEFRAME)) {
+ // Act as an opaque surface for the purposes of saving.
+ if (primaryGraphics) {
+ int prevFormat = format;
+ format = RGB;
+ boolean result = super.save(filename);
+ format = prevFormat;
+ return result;
+ }
- protected void setFontTexture(PFont font, FontTexture fontTexture) {
- getPrimaryPG().fontMap.put(font, fontTexture);
- }
+ return super.save(filename);
+ }
+ if (asyncImageSaver == null) {
+ asyncImageSaver = new AsyncImageSaver();
+ }
- protected FontTexture getFontTexture(PFont font) {
- return getPrimaryPG().fontMap.get(font);
- }
+ if (!asyncPixelReaderInitialized) {
+ // First call! Get this guy initialized
+ if (pgl.hasPBOs() && pgl.hasSynchronization()) {
+ asyncPixelReader = new AsyncPixelReader();
+ }
+ asyncPixelReaderInitialized = true;
+ }
+
+ if (asyncPixelReader != null && !loaded) {
+ boolean needEndDraw = false;
+ if (!drawing) {
+ beginDraw();
+ needEndDraw = true;
+ }
+ flush();
+ updatePixelSize();
+ // get the whole async package
+ asyncPixelReader.readAndSaveAsync(filename);
- protected void removeFontTexture(PFont font) {
- getPrimaryPG().fontMap.remove(font);
+ if (needEndDraw) endDraw();
+ } else {
+ // async transfer is not supported or
+ // pixels are already in memory, just do async save
+ if (!loaded) loadPixels();
+ int format = primaryGraphics ? RGB : ARGB;
+ PImage target = asyncImageSaver.getAvailableTarget(pixelWidth, pixelHeight,
+ format);
+ if (target == null) return false;
+ int count = PApplet.min(pixels.length, target.pixels.length);
+ System.arraycopy(pixels, 0, target.pixels, 0, count);
+ asyncImageSaver.saveTargetAsync(this, target, filename);
+ }
+
+ return true;
+ */
}
//////////////////////////////////////////////////////////////
- // RESOURCE HANDLING
+ // EYE/OBJECT MATRICES
- protected static class GLResource {
- int id;
- int context;
+ @Override
+ public PMatrix3D getEyeMatrix() {
+ return getEyeMatrix(null);
+ }
- GLResource(int id, int context) {
- this.id = id;
- this.context = context;
- }
- @Override
- public boolean equals(Object obj) {
- GLResource other = (GLResource)obj;
- return other.id == id && other.context == context;
+ @Override
+ public PMatrix3D getEyeMatrix(PMatrix3D target) {
+ if (target == null) {
+ target = new PMatrix3D();
}
+ float sign = cameraUp ? +1 : -1;
+ target.set(rightX, sign * upX, forwardX, cameraX,
+ rightY, sign * upY, forwardY, cameraY,
+ rightZ, sign * upZ, forwardZ, cameraZ,
+ 0, 0, 0, 1);
+ return target;
+ }
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + id;
- result = 31 * result + context;
- return result;
+
+ @Override
+ public PMatrix3D getObjectMatrix() {
+ PMatrix3D mat = new PMatrix3D();
+ mat.set(modelviewInv);
+ mat.apply(camera);
+ return mat;
+ }
+
+
+ @Override
+ public PMatrix3D getObjectMatrix(PMatrix3D target) {
+ if (target == null) {
+ target = new PMatrix3D();
}
+ target.set(modelviewInv);
+ target.apply(camera);
+ return target;
}
- // Texture Objects -----------------------------------------------------------
+ @Override
+ public void eye() {
+ eyeMatrix = getEyeMatrix(eyeMatrix);
- protected static int createTextureObject(int context, PGL pgl) {
- deleteFinalizedTextureObjects(pgl);
+ // Erasing any previous transformation in modelview
+ modelview.set(camera);
+ modelview.apply(eyeMatrix);
- pgl.genTextures(1, intBuffer);
- int id = intBuffer.get(0);
+ // The 3x3 block of eyeMatrix is orthogonal, so taking the transpose inverts it...
+ eyeMatrix.transpose();
+ // ...and then invert the translation separately:
+ eyeMatrix.m03 = -cameraX;
+ eyeMatrix.m13 = -cameraY;
+ eyeMatrix.m23 = -cameraZ;
+ eyeMatrix.m30 = 0;
+ eyeMatrix.m31 = 0;
+ eyeMatrix.m32 = 0;
- GLResource res = new GLResource(id, context);
- if (!glTextureObjects.containsKey(res)) {
- glTextureObjects.put(res, false);
- }
+ // Applying the inverse of the previous transformations in the opposite order
+ // to compute the modelview inverse
+ modelviewInv.set(eyeMatrix);
+ modelviewInv.preApply(cameraInv);
- return id;
+ updateProjmodelview();
}
- protected static void deleteTextureObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glTextureObjects.containsKey(res)) {
- intBuffer.put(0, id);
- if (pgl.threadIsCurrent()) pgl.deleteTextures(1, intBuffer);
- glTextureObjects.remove(res);
+
+ //////////////////////////////////////////////////////////////
+
+ // RAY CASTING
+
+ @Override
+ public PVector[] getRayFromScreen(float screenX, float screenY, PVector[] ray) {
+ if (ray == null || ray.length < 2) {
+ ray = new PVector[2];
+ ray[0] = new PVector();
+ ray[1] = new PVector();
}
+ getRayFromScreen(screenX, screenY, ray[0], ray[1]);
+ return ray;
}
- protected static void deleteAllTextureObjects(PGL pgl) {
- for (GLResource res : glTextureObjects.keySet()) {
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteTextures(1, intBuffer);
- }
- glTextureObjects.clear();
+
+ @Override
+ public void getRayFromScreen(float screenX, float screenY, PVector origin, PVector direction) {
+ eyeMatrix = getEyeMatrix(eyeMatrix);
+
+ // Transforming screen coordinates to world coordinates
+ screen.x = screenX;
+ screen.y = screenY;
+ screen.z = 0;
+ eyeMatrix.mult(screen, origin);
+
+ // The direction of the ray is simply extracted from the third column of the eye matrix (the
+ // forward vector).
+ direction.set(eyeMatrix.m02, eyeMatrix.m12, eyeMatrix.m22);
}
- // This is synchronized because it is called from the GC thread.
- synchronized protected static void finalizeTextureObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glTextureObjects.containsKey(res)) {
- glTextureObjects.put(res, true);
- }
+
+ @Override
+ public boolean intersectsSphere(float r, float screenX, float screenY) {
+ ray = getRayFromScreen(screenX, screenY, ray);
+ return intersectsSphere(r, ray[0], ray[1]);
}
- protected static void deleteFinalizedTextureObjects(PGL pgl) {
- Set finalized = new HashSet();
- for (GLResource res : glTextureObjects.keySet()) {
- if (glTextureObjects.get(res)) {
- finalized.add(res);
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteTextures(1, intBuffer);
- }
- }
+ @Override
+ public boolean intersectsSphere(float r, PVector origin, PVector direction) {
+ objMatrix = getObjectMatrix(objMatrix);
+ objMatrix.mult(origin, origInObjCoord);
+ PVector.add(origin, direction, hit);
+ objMatrix.mult(hit, hitInObjCoord);
+ PVector.sub(hitInObjCoord, origInObjCoord, dirInObjCoord);
- for (GLResource res : finalized) {
- glTextureObjects.remove(res);
- }
+ return rayIntersectsSphere(origInObjCoord, dirInObjCoord, r);
}
- protected static void removeTextureObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glTextureObjects.containsKey(res)) {
- glTextureObjects.remove(res);
- }
- }
- // Vertex Buffer Objects -----------------------------------------------------
+ // Ray-sphere intersecton algorithm as described in:
+ // http://paulbourke.net/geometry/circlesphere/
+ private boolean rayIntersectsSphere(PVector orig, PVector dir, float r) {
+ float d = orig.mag();
- protected static int createVertexBufferObject(int context, PGL pgl) {
- deleteFinalizedVertexBufferObjects(pgl);
+ // The eye is inside the sphere
+ if (d <= r) return true;
- pgl.genBuffers(1, intBuffer);
- int id = intBuffer.get(0);
+ float p = PVector.dot(orig, dir);
- GLResource res = new GLResource(id, context);
- if (!glVertexBuffers.containsKey(res)) {
- glVertexBuffers.put(res, false);
- }
+ // Check if sphere is in front of ray
+ if (p > 0) return false;
- return id;
+ // Check intersection of ray with sphere
+ float b = 2 * p;
+ float c = d * d - r * r;
+ float det = b * b - 4 * c;
+ return det >= 0;
}
- protected static void deleteVertexBufferObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glVertexBuffers.containsKey(res)) {
- intBuffer.put(0, id);
- if (pgl.threadIsCurrent()) pgl.deleteBuffers(1, intBuffer);
- glVertexBuffers.remove(res);
- }
+
+ @Override
+ public boolean intersectsBox(float size, float screenX, float screenY) {
+ ray = getRayFromScreen(screenX, screenY, ray);
+ return intersectsBox(size, size, size, ray[0], ray[1]);
}
- protected static void deleteAllVertexBufferObjects(PGL pgl) {
- for (GLResource res : glVertexBuffers.keySet()) {
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteBuffers(1, intBuffer);
- }
- glVertexBuffers.clear();
+
+ @Override
+ public boolean intersectsBox(float w, float h, float d, float screenX, float screenY) {
+ ray = getRayFromScreen(screenX, screenY, ray);
+ return intersectsBox(w, h, d, ray[0], ray[1]);
}
- // This is synchronized because it is called from the GC thread.
- synchronized static protected void finalizeVertexBufferObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glVertexBuffers.containsKey(res)) {
- glVertexBuffers.put(res, true);
- }
+
+ @Override
+ public boolean intersectsBox(float size, PVector origin, PVector direction) {
+ return intersectsBox(size, size, size, origin, direction);
}
- protected static void deleteFinalizedVertexBufferObjects(PGL pgl) {
- Set finalized = new HashSet();
- for (GLResource res : glVertexBuffers.keySet()) {
- if (glVertexBuffers.get(res)) {
- finalized.add(res);
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteBuffers(1, intBuffer);
- }
- }
+ @Override
+ public boolean intersectsBox(float w, float h, float d, PVector origin, PVector direction) {
+ objMatrix = getObjectMatrix(objMatrix);
+ objMatrix.mult(origin, origInObjCoord);
+ PVector.add(origin, direction, hit);
+ objMatrix.mult(hit, hitInObjCoord);
+ PVector.sub(hitInObjCoord, origInObjCoord, dirInObjCoord);
- for (GLResource res : finalized) {
- glVertexBuffers.remove(res);
- }
+ return lineIntersectsAABB(origInObjCoord, dirInObjCoord, w, h, d);
}
- protected static void removeVertexBufferObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glVertexBuffers.containsKey(res)) {
- glVertexBuffers.remove(res);
- }
- }
- // FrameBuffer Objects -------------------------------------------------------
+ // Line intersection with an axis-aligned bounding box (AABB), calculated using the algorithm
+ // from Amy William et al: http:// dl.acm.org/citation.cfm?id=1198748
+ private boolean lineIntersectsAABB(PVector orig, PVector dir, float w, float h, float d) {
+ float minx = -w/2;
+ float miny = -h/2;
+ float minz = -d/2;
- protected static int createFrameBufferObject(int context, PGL pgl) {
- deleteFinalizedFrameBufferObjects(pgl);
+ float maxx = +w/2;
+ float maxy = +h/2;
+ float maxz = +d/2;
- pgl.genFramebuffers(1, intBuffer);
- int id = intBuffer.get(0);
+ float idx = 1/dir.x;
+ float idy = 1/dir.y;
+ float idz = 1/dir.z;
- GLResource res = new GLResource(id, context);
- if (!glFrameBuffers.containsKey(res)) {
- glFrameBuffers.put(res, false);
- }
+ boolean sdx = idx < 0;
+ boolean sdy = idy < 0;
+ boolean sdz = idz < 0;
- return id;
- }
+ float bbx = sdx ? maxx : minx;
+ float txmin = (bbx - orig.x) * idx;
+ bbx = sdx ? minx : maxx;
+ float txmax = (bbx - orig.x) * idx;
+ float bby = sdy ? maxy : miny;
+ float tymin = (bby - orig.y) * idy;
+ bby = sdy ? miny : maxy;
+ float tymax = (bby - orig.y) * idy;
- protected static void deleteFrameBufferObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glFrameBuffers.containsKey(res)) {
- intBuffer.put(0, id);
- if (pgl.threadIsCurrent()) pgl.deleteFramebuffers(1, intBuffer);
- glFrameBuffers.remove(res);
+ if ((txmin > tymax) || (tymin > txmax)) {
+ return false;
}
- }
-
- protected static void deleteAllFrameBufferObjects(PGL pgl) {
- for (GLResource res : glFrameBuffers.keySet()) {
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteFramebuffers(1, intBuffer);
+ if (tymin > txmin) {
+ txmin = tymin;
}
- glFrameBuffers.clear();
- }
-
- // This is synchronized because it is called from the GC thread.
- synchronized static protected void finalizeFrameBufferObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glFrameBuffers.containsKey(res)) {
- glFrameBuffers.put(res, true);
+ if (tymax < txmax) {
+ txmax = tymax;
}
- }
- protected static void deleteFinalizedFrameBufferObjects(PGL pgl) {
- Set finalized = new HashSet();
+ float bbz = sdz ? maxz : minz;
+ float tzmin = (bbz - orig.z) * idz;
+ bbz = sdz ? minz : maxz;
+ float tzmax = (bbz - orig.z) * idz;
- for (GLResource res : glFrameBuffers.keySet()) {
- if (glFrameBuffers.get(res)) {
- finalized.add(res);
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) {
- pgl.deleteFramebuffers(1, intBuffer);
- }
- }
+ if ((txmin > tzmax) || (tzmin > txmax)) {
+ return false;
+ }
+ if (tzmin > txmin) {
+ txmin = tzmin;
+ }
+ if (tzmax < txmax) {
+ txmax = tzmax;
}
- for (GLResource res : finalized) {
- glFrameBuffers.remove(res);
+ if ((txmin < defCameraFar) && (txmax > 0)) {
+ // The intersection coordinates:
+ // x = orig.x + txmin * dir.x;
+ // y = orig.y + txmin * dir.y;
+ // z = orig.z + txmin * dir.z;
+ return true;
}
+
+ return false;
}
- protected static void removeFrameBufferObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glFrameBuffers.containsKey(res)) {
- glFrameBuffers.remove(res);
- }
+
+ @Override
+ public PVector intersectsPlane(float screenX, float screenY) {
+ ray = getRayFromScreen(screenX, screenY, ray);
+ return intersectsPlane(ray[0], ray[1]);
}
- // RenderBuffer Objects ------------------------------------------------------
- protected static int createRenderBufferObject(int context, PGL pgl) {
- deleteFinalizedRenderBufferObjects(pgl);
+ @Override
+ public PVector intersectsPlane(PVector origin, PVector direction) {
+ modelview.mult(origin, origInWorldCoord);
+ modelview.mult(direction, dirInWorldCoord);
+ dirInWorldCoord.normalize();
- pgl.genRenderbuffers(1, intBuffer);
- int id = intBuffer.get(0);
+ // Plane representation
+ PVector point = new PVector(0, 0, 0);
+ PVector normal = new PVector(0, 0, 1);
- GLResource res = new GLResource(id, context);
- if (!glRenderBuffers.containsKey(res)) {
- glRenderBuffers.put(res, false);
- }
+ // Ray-plane intersection algorithm
+ float d = PApplet.abs(PVector.dot(normal, dirInWorldCoord));
+ if (d == 0) return null;
- return id;
- }
+ PVector w = PVector.sub(point, origInWorldCoord);
+ float k = PApplet.abs(PVector.dot(normal, w)/d);
+ PVector p = PVector.add(origInWorldCoord, dirInWorldCoord).setMag(k);
- protected static void deleteRenderBufferObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glRenderBuffers.containsKey(res)) {
- intBuffer.put(0, id);
- if (pgl.threadIsCurrent()) pgl.deleteRenderbuffers(1, intBuffer);
- glRenderBuffers.remove(res);
- }
+ return p;
}
- protected static void deleteAllRenderBufferObjects(PGL pgl) {
- for (GLResource res : glRenderBuffers.keySet()) {
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteRenderbuffers(1, intBuffer);
- }
- glRenderBuffers.clear();
- }
- // This is synchronized because it is called from the GC thread.
- synchronized static protected void finalizeRenderBufferObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glRenderBuffers.containsKey(res)) {
- glRenderBuffers.put(res, true);
- }
- }
+ //////////////////////////////////////////////////////////////
- protected static void deleteFinalizedRenderBufferObjects(PGL pgl) {
- Set finalized = new HashSet();
+ // IMAGE METADATA FOR THIS RENDERER
- for (GLResource res : glRenderBuffers.keySet()) {
- if (glRenderBuffers.get(res)) {
- finalized.add(res);
- intBuffer.put(0, res.id);
- if (pgl.threadIsCurrent()) pgl.deleteRenderbuffers(1, intBuffer);
- }
- }
- for (GLResource res : finalized) {
- glRenderBuffers.remove(res);
+ @Override
+ public void setCache(PImage image, Object storage) {
+ if (image instanceof PGraphicsOpenGL) {
+ // Prevent strong reference to the key from the value by wrapping
+ // the Texture into WeakReference (proposed solution by WeakHashMap docs)
+ getPrimaryPG().cacheMap.put(image, new WeakReference<>(storage));
+ return;
}
+ getPrimaryPG().cacheMap.put(image, storage);
}
- protected static void removeRenderBufferObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glRenderBuffers.containsKey(res)) {
- glRenderBuffers.remove(res);
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Object getCache(PImage image) {
+ Object storage = getPrimaryPG().cacheMap.get(image);
+ if (storage != null && storage.getClass() == WeakReference.class) {
+ // Unwrap the value, use getClass() for fast check
+ return ((WeakReference) storage).get();
}
+ return storage;
}
- // GLSL Program Objects ------------------------------------------------------
- protected static int createGLSLProgramObject(int context, PGL pgl) {
- deleteFinalizedGLSLProgramObjects(pgl);
+ @Override
+ public void removeCache(PImage image) {
+ getPrimaryPG().cacheMap.remove(image);
+ }
+
- int id = pgl.createProgram();
+ //////////////////////////////////////////////////////////////
- GLResource res = new GLResource(id, context);
- if (!glslPrograms.containsKey(res)) {
- glslPrograms.put(res, false);
- }
- return id;
+ protected void setFontTexture(PFont font, FontTexture fontTexture) {
+ getPrimaryPG().fontMap.put(font, fontTexture);
}
- protected static void deleteGLSLProgramObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glslPrograms.containsKey(res)) {
- if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id);
- glslPrograms.remove(res);
- }
- }
- protected static void deleteAllGLSLProgramObjects(PGL pgl) {
- for (GLResource res : glslPrograms.keySet()) {
- if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id);
- }
- glslPrograms.clear();
+ protected FontTexture getFontTexture(PFont font) {
+ return getPrimaryPG().fontMap.get(font);
}
- // This is synchronized because it is called from the GC thread.
- synchronized static protected void finalizeGLSLProgramObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glslPrograms.containsKey(res)) {
- glslPrograms.put(res, true);
- }
+
+ protected void removeFontTexture(PFont font) {
+ getPrimaryPG().fontMap.remove(font);
}
- protected static void deleteFinalizedGLSLProgramObjects(PGL pgl) {
- Set finalized = new HashSet();
- for (GLResource res : glslPrograms.keySet()) {
- if (glslPrograms.get(res)) {
- finalized.add(res);
- if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id);
+ //////////////////////////////////////////////////////////////
+
+
+ protected static class GLResourceTexture extends Disposable {
+ int glName;
+
+ private PGL pgl;
+ private int context;
+
+ public GLResourceTexture(Texture tex) {
+ super(tex);
+
+
+ pgl = tex.pg.getPrimaryPGL();
+ pgl.genTextures(1, intBuffer);
+ tex.glName = intBuffer.get(0);
+
+ this.glName = tex.glName;
+ this.context = tex.context;
+ }
+
+ @Override
+ public void disposeNative() {
+ if (pgl != null) {
+ if (glName != 0) {
+ intBuffer.put(0, glName);
+ pgl.deleteTextures(1, intBuffer);
+ glName = 0;
+ }
+ pgl = null;
}
}
- for (GLResource res : finalized) {
- glslPrograms.remove(res);
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GLResourceTexture)) {
+ return false;
+ }
+ GLResourceTexture other = (GLResourceTexture)obj;
+ return other.glName == glName &&
+ other.context == context;
}
- }
- protected static void removeGLSLProgramObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glslPrograms.containsKey(res)) {
- glslPrograms.remove(res);
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + glName;
+ result = 31 * result + context;
+ return result;
}
}
- // GLSL Vertex Shader Objects ------------------------------------------------
- protected static int createGLSLVertShaderObject(int context, PGL pgl) {
- deleteFinalizedGLSLVertShaderObjects(pgl);
+ protected static class GLResourceVertexBuffer extends Disposable {
+ int glId;
- int id = pgl.createShader(PGL.VERTEX_SHADER);
+ private PGL pgl;
+ private int context;
- GLResource res = new GLResource(id, context);
- if (!glslVertexShaders.containsKey(res)) {
- glslVertexShaders.put(res, false);
- }
+ public GLResourceVertexBuffer(VertexBuffer vbo) {
+ super(vbo);
- return id;
- }
+ pgl = vbo.pgl.graphics.getPrimaryPGL();
+ pgl.genBuffers(1, intBuffer);
+ vbo.glId = intBuffer.get(0);
- protected static void deleteGLSLVertShaderObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glslVertexShaders.containsKey(res)) {
- if (pgl.threadIsCurrent()) pgl.deleteShader(res.id);
- glslVertexShaders.remove(res);
+ this.glId = vbo.glId;
+ this.context = vbo.context;
}
- }
- protected static void deleteAllGLSLVertShaderObjects(PGL pgl) {
- for (GLResource res : glslVertexShaders.keySet()) {
- if (pgl.threadIsCurrent()) pgl.deleteShader(res.id);
+ @Override
+ public void disposeNative() {
+ if (pgl != null) {
+ if (glId != 0) {
+ intBuffer.put(0, glId);
+ pgl.deleteBuffers(1, intBuffer);
+ glId = 0;
+ }
+ pgl = null;
+ }
}
- glslVertexShaders.clear();
- }
- // This is synchronized because it is called from the GC thread.
- synchronized static protected void finalizeGLSLVertShaderObject(int id,
- int context) {
- GLResource res = new GLResource(id, context);
- if (glslVertexShaders.containsKey(res)) {
- glslVertexShaders.put(res, true);
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GLResourceVertexBuffer)) {
+ return false;
+ }
+ GLResourceVertexBuffer other = (GLResourceVertexBuffer)obj;
+ return other.glId == glId &&
+ other.context == context;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + glId;
+ result = 31 * result + context;
+ return result;
}
}
- protected static void deleteFinalizedGLSLVertShaderObjects(PGL pgl) {
- Set finalized = new HashSet();
- for (GLResource res : glslVertexShaders.keySet()) {
- if (glslVertexShaders.get(res)) {
- finalized.add(res);
- if (pgl.threadIsCurrent()) pgl.deleteShader(res.id);
+ protected static class GLResourceShader extends Disposable {
+ int glProgram;
+ int glVertex;
+ int glFragment;
+
+ private PGL pgl;
+ private int context;
+
+ public GLResourceShader(PShader sh) {
+ super(sh);
+
+ this.pgl = sh.pgl.graphics.getPrimaryPGL();
+ sh.glProgram = pgl.createProgram();
+ sh.glVertex = pgl.createShader(PGL.VERTEX_SHADER);
+ sh.glFragment = pgl.createShader(PGL.FRAGMENT_SHADER);
+
+ this.glProgram = sh.glProgram;
+ this.glVertex = sh.glVertex;
+ this.glFragment = sh.glFragment;
+
+ this.context = sh.context;
+ }
+
+ @Override
+ public void disposeNative() {
+ if (pgl != null) {
+ if (glFragment != 0) {
+ pgl.deleteShader(glFragment);
+ glFragment = 0;
+ }
+ if (glVertex != 0) {
+ pgl.deleteShader(glVertex);
+ glVertex = 0;
+ }
+ if (glProgram != 0) {
+ pgl.deleteProgram(glProgram);
+ glProgram = 0;
+ }
+ pgl = null;
}
}
- for (GLResource res : finalized) {
- glslVertexShaders.remove(res);
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GLResourceShader)) {
+ return false;
+ }
+ GLResourceShader other = (GLResourceShader)obj;
+ return other.glProgram == glProgram &&
+ other.glVertex == glVertex &&
+ other.glFragment == glFragment &&
+ other.context == context;
}
- }
- protected static void removeGLSLVertShaderObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glslVertexShaders.containsKey(res)) {
- glslVertexShaders.remove(res);
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + glProgram;
+ result = 31 * result + glVertex;
+ result = 31 * result + glFragment;
+ result = 31 * result + context;
+ return result;
}
}
- // GLSL Fragment Shader Objects ----------------------------------------------
- protected static int createGLSLFragShaderObject(int context, PGL pgl) {
- deleteFinalizedGLSLFragShaderObjects(pgl);
+ protected static class GLResourceFrameBuffer extends Disposable {
+ int glFbo;
+ int glDepth;
+ int glStencil;
+ int glDepthStencil;
+ int glMultisample;
- int id = pgl.createShader(PGL.FRAGMENT_SHADER);
+ private PGL pgl;
+ private int context;
- GLResource res = new GLResource(id, context);
- if (!glslFragmentShaders.containsKey(res)) {
- glslFragmentShaders.put(res, false);
- }
+ public GLResourceFrameBuffer(FrameBuffer fb) {
+ super(fb);
- return id;
- }
+ pgl = fb.pg.getPrimaryPGL();
+ if (!fb.screenFb) {
+ pgl.genFramebuffers(1, intBuffer);
+ fb.glFbo = intBuffer.get(0);
- protected static void deleteGLSLFragShaderObject(int id, int context, PGL pgl) {
- GLResource res = new GLResource(id, context);
- if (glslFragmentShaders.containsKey(res)) {
- if (pgl.threadIsCurrent()) pgl.deleteShader(res.id);
- glslFragmentShaders.remove(res);
- }
- }
+ if (fb.multisample) {
+ pgl.genRenderbuffers(1, intBuffer);
+ fb.glMultisample = intBuffer.get(0);
+ }
- protected static void deleteAllGLSLFragShaderObjects(PGL pgl) {
- for (GLResource res : glslFragmentShaders.keySet()) {
- if (pgl.threadIsCurrent()) pgl.deleteShader(res.id);
- }
- glslFragmentShaders.clear();
- }
+ if (fb.packedDepthStencil) {
+ pgl.genRenderbuffers(1, intBuffer);
+ fb.glDepthStencil = intBuffer.get(0);
+ } else {
+ if (0 < fb.depthBits) {
+ pgl.genRenderbuffers(1, intBuffer);
+ fb.glDepth = intBuffer.get(0);
+ }
+ if (0 < fb.stencilBits) {
+ pgl.genRenderbuffers(1, intBuffer);
+ fb.glStencil = intBuffer.get(0);
+ }
+ }
- // This is synchronized because it is called from the GC thread.
- synchronized static protected void finalizeGLSLFragShaderObject(int id,
- int context) {
- GLResource res = new GLResource(id, context);
- if (glslFragmentShaders.containsKey(res)) {
- glslFragmentShaders.put(res, true);
- }
- }
+ this.glFbo = fb.glFbo;
+ this.glDepth = fb.glDepth;
+ this.glStencil = fb.glStencil;
+ this.glDepthStencil = fb.glDepthStencil;
+ this.glMultisample = fb.glMultisample;
+ }
- protected static void deleteFinalizedGLSLFragShaderObjects(PGL pgl) {
- Set finalized = new HashSet();
+ this.context = fb.context;
+ }
- for (GLResource res : glslFragmentShaders.keySet()) {
- if (glslFragmentShaders.get(res)) {
- finalized.add(res);
- if (pgl.threadIsCurrent()) pgl.deleteShader(res.id);
+ @Override
+ public void disposeNative() {
+ if (pgl != null) {
+ if (glFbo != 0) {
+ intBuffer.put(0, glFbo);
+ pgl.deleteFramebuffers(1, intBuffer);
+ glFbo = 0;
+ }
+ if (glDepth != 0) {
+ intBuffer.put(0, glDepth);
+ pgl.deleteRenderbuffers(1, intBuffer);
+ glDepth = 0;
+ }
+ if (glStencil != 0) {
+ intBuffer.put(0, glStencil);
+ pgl.deleteRenderbuffers(1, intBuffer);
+ glStencil = 0;
+ }
+ if (glDepthStencil != 0) {
+ intBuffer.put(0, glDepthStencil);
+ pgl.deleteRenderbuffers(1, intBuffer);
+ glDepthStencil = 0;
+ }
+ if (glMultisample != 0) {
+ intBuffer.put(0, glMultisample);
+ pgl.deleteRenderbuffers(1, intBuffer);
+ glMultisample = 0;
+ }
+ pgl = null;
}
}
- for (GLResource res : finalized) {
- glslFragmentShaders.remove(res);
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GLResourceFrameBuffer)) {
+ return false;
+ }
+ GLResourceFrameBuffer other = (GLResourceFrameBuffer)obj;
+ return other.glFbo == glFbo &&
+ other.glDepth == glDepth &&
+ other.glStencil == glStencil &&
+ other.glDepthStencil == glDepthStencil &&
+ other.glMultisample == glMultisample &&
+ other.context == context;
}
- }
- protected static void removeGLSLFragShaderObject(int id, int context) {
- GLResource res = new GLResource(id, context);
- if (glslFragmentShaders.containsKey(res)) {
- glslFragmentShaders.remove(res);
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + glFbo;
+ result = 31 * result + glDepth;
+ result = 31 * result + glStencil;
+ result = 31 * result + glDepthStencil;
+ result = 31 * result + glMultisample;
+ result = 31 * result + context;
+ return result;
}
}
- // All OpenGL resources ------------------------------------------------------
-
- protected static void deleteFinalizedGLResources(PGL pgl) {
- deleteFinalizedTextureObjects(pgl);
- deleteFinalizedVertexBufferObjects(pgl);
- deleteFinalizedFrameBufferObjects(pgl);
- deleteFinalizedRenderBufferObjects(pgl);
- deleteFinalizedGLSLProgramObjects(pgl);
- deleteFinalizedGLSLVertShaderObjects(pgl);
- deleteFinalizedGLSLFragShaderObjects(pgl);
- }
-
//////////////////////////////////////////////////////////////
@@ -1262,52 +1508,30 @@ protected void createPolyBuffers() {
if (!polyBuffersCreated || polyBuffersContextIsOutdated()) {
polyBuffersContext = pgl.getCurrentContext();
- int sizef = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_FLOAT;
- int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT;
- int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX;
-
- glPolyVertex = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex);
- pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW);
-
- glPolyColor = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW);
-
- glPolyNormal = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal);
- pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW);
-
- glPolyTexcoord = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord);
- pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, null, PGL.STATIC_DRAW);
-
- glPolyAmbient = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW);
-
- glPolySpecular = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW);
-
- glPolyEmissive = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW);
-
- glPolyShininess = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizef, null, PGL.STATIC_DRAW);
-
+ bufPolyVertex = new VertexBuffer(this, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
+ bufPolyColor = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ bufPolyNormal = new VertexBuffer(this, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
+ bufPolyTexcoord = new VertexBuffer(this, PGL.ARRAY_BUFFER, 2, PGL.SIZEOF_FLOAT);
+ bufPolyAmbient = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ bufPolySpecular = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ bufPolyEmissive = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ bufPolyShininess = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_FLOAT);
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
-
- glPolyIndex = createVertexBufferObject(polyBuffersContext, pgl);
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex);
- pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW);
-
+ bufPolyIndex = new VertexBuffer(this, PGL.ELEMENT_ARRAY_BUFFER, 1, PGL.SIZEOF_INDEX, true);
pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0);
polyBuffersCreated = true;
}
+
+ boolean created = false;
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (!attrib.bufferCreated() || polyBuffersContextIsOutdated()) {
+ attrib.createBuffer(pgl);
+ created = true;
+ }
+ }
+ if (created) pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -1320,52 +1544,61 @@ protected void updatePolyBuffers(boolean lit, boolean tex,
int sizei = size * PGL.SIZEOF_INT;
tessGeo.updatePolyVerticesBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyVertex.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
tessGeo.polyVerticesBuffer, PGL.STATIC_DRAW);
tessGeo.updatePolyColorsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyColor.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
tessGeo.polyColorsBuffer, PGL.STATIC_DRAW);
if (lit) {
tessGeo.updatePolyAmbientBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyAmbient.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
tessGeo.polyAmbientBuffer, PGL.STATIC_DRAW);
tessGeo.updatePolySpecularBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolySpecular.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
tessGeo.polySpecularBuffer, PGL.STATIC_DRAW);
tessGeo.updatePolyEmissiveBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyEmissive.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
tessGeo.polyEmissiveBuffer, PGL.STATIC_DRAW);
tessGeo.updatePolyShininessBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyShininess.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizef,
tessGeo.polyShininessBuffer, PGL.STATIC_DRAW);
}
+
if (lit || needNormals) {
tessGeo.updatePolyNormalsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyNormal.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef,
tessGeo.polyNormalsBuffer, PGL.STATIC_DRAW);
}
if (tex || needTexCoords) {
tessGeo.updatePolyTexCoordsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyTexcoord.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef,
tessGeo.polyTexCoordsBuffer, PGL.STATIC_DRAW);
}
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ tessGeo.updateAttribBuffer(name);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, attrib.buf.glId);
+ pgl.bufferData(PGL.ARRAY_BUFFER, attrib.sizeInBytes(size),
+ tessGeo.polyAttribBuffers.get(name), PGL.STATIC_DRAW);
+ }
+
tessGeo.updatePolyIndicesBuffer();
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex);
+ pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, bufPolyIndex.glId);
pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER,
tessGeo.polyIndexCount * PGL.SIZEOF_INDEX, tessGeo.polyIndicesBuffer,
PGL.STATIC_DRAW);
@@ -1383,83 +1616,15 @@ protected boolean polyBuffersContextIsOutdated() {
}
- protected void finalizePolyBuffers() {
- if (glPolyVertex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyVertex, polyBuffersContext);
- glPolyVertex = 0;
- }
-
- if (glPolyColor != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyColor, polyBuffersContext);
- glPolyColor = 0;
- }
-
- if (glPolyNormal != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyNormal, polyBuffersContext);
- glPolyNormal = 0;
- }
-
- if (glPolyTexcoord != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyTexcoord, polyBuffersContext);
- glPolyTexcoord = 0;
- }
-
- if (glPolyAmbient != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyAmbient, polyBuffersContext);
- glPolyAmbient = 0;
- }
-
- if (glPolySpecular != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolySpecular, polyBuffersContext);
- glPolySpecular = 0;
- }
-
- if (glPolyEmissive != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyEmissive, polyBuffersContext);
- glPolyEmissive = 0;
- }
-
- if (glPolyShininess != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyShininess, polyBuffersContext);
- glPolyShininess = 0;
- }
-
- if (glPolyIndex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyIndex, polyBuffersContext);
- glPolyIndex = 0;
- }
-
- polyBuffersCreated = false;
- }
-
-
protected void createLineBuffers() {
if (!lineBuffersCreated || lineBufferContextIsOutdated()) {
lineBuffersContext = pgl.getCurrentContext();
- int sizef = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_FLOAT;
- int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT;
- int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX;
-
- glLineVertex = createVertexBufferObject(lineBuffersContext, pgl);
-
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex);
- pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW);
-
- glLineColor = createVertexBufferObject(lineBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW);
-
- glLineAttrib = createVertexBufferObject(lineBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib);
- pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, null, PGL.STATIC_DRAW);
-
+ bufLineVertex = new VertexBuffer(this, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
+ bufLineColor = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ bufLineAttrib = new VertexBuffer(this, PGL.ARRAY_BUFFER, 4, PGL.SIZEOF_FLOAT);
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
-
- glLineIndex = createVertexBufferObject(lineBuffersContext, pgl);
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex);
- pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW);
-
+ bufLineIndex = new VertexBuffer(this, PGL.ELEMENT_ARRAY_BUFFER, 1, PGL.SIZEOF_INDEX, true);
pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0);
lineBuffersCreated = true;
@@ -1474,23 +1639,25 @@ protected void updateLineBuffers() {
int sizef = size * PGL.SIZEOF_FLOAT;
int sizei = size * PGL.SIZEOF_INT;
+
+
tessGeo.updateLineVerticesBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineVertex.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.lineVerticesBuffer,
PGL.STATIC_DRAW);
tessGeo.updateLineColorsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineColor.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
tessGeo.lineColorsBuffer, PGL.STATIC_DRAW);
tessGeo.updateLineDirectionsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineAttrib.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
tessGeo.lineDirectionsBuffer, PGL.STATIC_DRAW);
tessGeo.updateLineIndicesBuffer();
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex);
+ pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, bufLineIndex.glId);
pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER,
tessGeo.lineIndexCount * PGL.SIZEOF_INDEX,
tessGeo.lineIndicesBuffer, PGL.STATIC_DRAW);
@@ -1508,57 +1675,15 @@ protected boolean lineBufferContextIsOutdated() {
}
- protected void finalizeLineBuffers() {
- if (glLineVertex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineVertex, lineBuffersContext);
- glLineVertex = 0;
- }
-
- if (glLineColor != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineColor, lineBuffersContext);
- glLineColor = 0;
- }
-
- if (glLineAttrib != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineAttrib, lineBuffersContext);
- glLineAttrib = 0;
- }
-
- if (glLineIndex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineIndex, lineBuffersContext);
- glLineIndex = 0;
- }
-
- lineBuffersCreated = false;
- }
-
-
protected void createPointBuffers() {
if (!pointBuffersCreated || pointBuffersContextIsOutdated()) {
pointBuffersContext = pgl.getCurrentContext();
- int sizef = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_FLOAT;
- int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT;
- int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX;
-
- glPointVertex = createVertexBufferObject(pointBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex);
- pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW);
-
- glPointColor = createVertexBufferObject(pointBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor);
- pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW);
-
- glPointAttrib = createVertexBufferObject(pointBuffersContext, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib);
- pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, null, PGL.STATIC_DRAW);
-
+ bufPointVertex = new VertexBuffer(this, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
+ bufPointColor = new VertexBuffer(this, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ bufPointAttrib = new VertexBuffer(this, PGL.ARRAY_BUFFER, 2, PGL.SIZEOF_FLOAT);
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
-
- glPointIndex = createVertexBufferObject(pointBuffersContext, pgl);
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex);
- pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW);
-
+ bufPointIndex = new VertexBuffer(this, PGL.ELEMENT_ARRAY_BUFFER, 1, PGL.SIZEOF_INDEX, true);
pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0);
pointBuffersCreated = true;
@@ -1574,22 +1699,22 @@ protected void updatePointBuffers() {
int sizei = size * PGL.SIZEOF_INT;
tessGeo.updatePointVerticesBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointVertex.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
tessGeo.pointVerticesBuffer, PGL.STATIC_DRAW);
tessGeo.updatePointColorsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointColor.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
tessGeo.pointColorsBuffer, PGL.STATIC_DRAW);
tessGeo.updatePointOffsetsBuffer();
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointAttrib.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef,
tessGeo.pointOffsetsBuffer, PGL.STATIC_DRAW);
tessGeo.updatePointIndicesBuffer();
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex);
+ pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, bufPointIndex.glId);
pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER,
tessGeo.pointIndexCount * PGL.SIZEOF_INDEX,
tessGeo.pointIndicesBuffer, PGL.STATIC_DRAW);
@@ -1607,69 +1732,23 @@ protected boolean pointBuffersContextIsOutdated() {
}
- protected void finalizePointBuffers() {
- if (glPointVertex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointVertex, pointBuffersContext);
- glPointVertex = 0;
- }
-
- if (glPointColor != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointColor, pointBuffersContext);
- glPointColor = 0;
- }
-
- if (glPointAttrib != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointAttrib, pointBuffersContext);
- glPointAttrib = 0;
- }
-
- if (glPointIndex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointIndex, pointBuffersContext);
- glPointIndex = 0;
- }
-
- pointBuffersCreated = false;
- }
-
-
- @Override
- public void requestFocus() { // ignore
- pgl.requestFocus();
- }
-
-
- /**
- * OpenGL cannot draw until a proper native peer is available, so this
- * returns the value of PApplet.isDisplayable() (inherited from Component).
- */
- @Override
- public boolean canDraw() {
- return pgl.canDraw();
- }
-
-
- @Override
- public void requestDraw() {
- if (primarySurface) {
- if (initialized) {
- if (sized) pgl.reinitSurface();
- if (parent.canDraw()) pgl.requestDraw();
- } else {
- initPrimary();
- }
- }
- }
-
-
@Override
public void beginDraw() {
- if (primarySurface) {
+ if (primaryGraphics) {
+ initPrimary();
setCurrentPG(this);
} else {
pgl.getGL(getPrimaryPGL());
getPrimaryPG().setCurrentPG(this);
}
+// if (!pgl.threadIsCurrent()) {
+// PGraphics.showWarning(GL_THREAD_NOT_CURRENT);
+// return;
+// }
+
+ // This has to go after the surface initialization, otherwise offscreen
+ // surfaces will have a null gl object.
report("top beginDraw()");
if (!checkGLThread()) {
@@ -1680,7 +1759,7 @@ public void beginDraw() {
return;
}
- if (!primarySurface && getPrimaryPG().texCache.containsTexture(this)) {
+ if (!primaryGraphics && getPrimaryPG().texCache.containsTexture(this)) {
// This offscreen surface is being used as a texture earlier in draw,
// so we should update the rendering up to this point since it will be
// modified.
@@ -1692,12 +1771,12 @@ public void beginDraw() {
}
setViewport();
- if (primarySurface) {
+ if (primaryGraphics) {
beginOnscreenDraw();
} else {
beginOffscreenDraw();
}
- setDrawDefaults(); // TODO: look at using checkSettings() instead...
+ checkSettings();
drawing = true;
@@ -1716,27 +1795,16 @@ public void endDraw() {
// Flushing any remaining geometry.
flush();
- if (PGL.SAVE_SURFACE_TO_PIXELS_HACK &&
- (!getPrimaryPG().initialized || parent.frameCount == 0)) {
- // Smooth was disabled/enabled at some point during drawing. We save
- // the current contents of the back buffer (because the buffers haven't
- // been swapped yet) to the pixels array. The frameCount == 0 condition
- // is to handle the situation when no smooth is called in setup in the
- // PDE, but the OpenGL appears to be recreated due to the size() nastiness.
- saveSurfaceToPixels();
- restoreSurface = true;
- }
-
- if (primarySurface) {
+ if (primaryGraphics) {
endOnscreenDraw();
} else {
endOffscreenDraw();
}
- if (primarySurface) {
+ if (primaryGraphics) {
setCurrentPG(null);
} else {
- getPrimaryPG().setCurrentPG(getPrimaryPG());
+ getPrimaryPG().setCurrentPG();
}
drawing = false;
@@ -1744,14 +1812,8 @@ public void endDraw() {
}
- // Factory method
- protected PGL createPGL(PGraphicsOpenGL pg) {
- return new PGLES(pg);
- }
-
-
protected PGraphicsOpenGL getPrimaryPG() {
- if (primarySurface) {
+ if (primaryGraphics) {
return this;
} else {
return (PGraphicsOpenGL)parent.g;
@@ -1762,12 +1824,16 @@ protected void setCurrentPG(PGraphicsOpenGL pg) {
currentPG = pg;
}
+ protected void setCurrentPG() {
+ currentPG = this;
+ }
+
protected PGraphicsOpenGL getCurrentPG() {
return currentPG;
}
protected PGL getPrimaryPGL() {
- if (primarySurface) {
+ if (primaryGraphics) {
return pgl;
} else {
return ((PGraphicsOpenGL)parent.g).pgl;
@@ -1811,7 +1877,7 @@ protected void restoreGL() {
}
pgl.depthFunc(PGL.LEQUAL);
- if (quality < 2) {
+ if (smooth < 1) {
pgl.disable(PGL.MULTISAMPLE);
} else {
pgl.enable(PGL.MULTISAMPLE);
@@ -1827,7 +1893,7 @@ protected void restoreGL() {
pgl.disable(PGL.SCISSOR_TEST);
}
- pgl.frontFace(PGL.CW);
+ pgl.frontFace(cameraUp ? PGL.CCW : PGL.CW);
pgl.disable(PGL.CULL_FACE);
pgl.activeTexture(PGL.TEXTURE0);
@@ -1841,7 +1907,7 @@ protected void restoreGL() {
FrameBuffer fb = getCurrentFB();
if (fb != null) {
fb.bind();
- pgl.drawBuffer(fb.getDefaultDrawBuffer());
+ if (drawBufferSupported) pgl.drawBuffer(fb.getDefaultDrawBuffer());
}
}
@@ -1868,40 +1934,56 @@ protected void endReadPixels() {
protected void beginPixelsOp(int op) {
FrameBuffer pixfb = null;
- if (primarySurface) {
- if (op == OP_READ) {
- if (pgl.isFBOBacked() && pgl.isMultisampled()) {
- // Making sure the back texture is up-to-date...
- pgl.syncBackTexture();
- // ...because the read framebuffer uses it as the color buffer (the
- // draw framebuffer is MSAA so it cannot be read from it).
- pixfb = readFramebuffer;
- } else {
- pixfb = drawFramebuffer;
+ FrameBuffer currfb = getCurrentFB();
+ if (primaryGraphics) {
+ FrameBuffer rfb = readFramebuffer;
+ FrameBuffer dfb = drawFramebuffer;
+ if ((currfb == rfb) || (currfb == dfb)) {
+ // Not user-provided FB, need to check if the correct FB is current.
+ if (op == OP_READ) {
+ if (pgl.isFBOBacked() && pgl.isMultisampled()) {
+ // Making sure the back texture is up-to-date...
+ pgl.syncBackTexture();
+ // ...because the read framebuffer uses it as the color buffer (the
+ // draw framebuffer is MSAA so it cannot be read from it).
+ pixfb = rfb;
+ } else {
+ pixfb = dfb;
+ }
+ } else if (op == OP_WRITE) {
+ // We can write to the draw framebuffer irrespective of whether is
+ // FBO-baked or multisampled.
+ pixfb = dfb;
}
- } else if (op == OP_WRITE) {
- // We can write to the draw framebuffer irrespective of whether is
- // FBO-baked or multisampled.
- pixfb = drawFramebuffer;
}
} else {
- if (op == OP_READ) {
- if (offscreenMultisample) {
- // Making sure the offscreen FBO is up-to-date
- multisampleFramebuffer.copyColor(offscreenFramebuffer);
+ FrameBuffer ofb = offscreenFramebuffer;
+ FrameBuffer mfb = multisampleFramebuffer;
+ if ((currfb == ofb) || (currfb == mfb)) {
+ // Not user-provided FB, need to check if the correct FB is current.
+ if (op == OP_READ) {
+ if (offscreenMultisample) {
+ // Making sure the offscreen FBO is up-to-date
+ int mask = PGL.COLOR_BUFFER_BIT;
+ if (hints[ENABLE_BUFFER_READING]) {
+ mask |= PGL.DEPTH_BUFFER_BIT | PGL.STENCIL_BUFFER_BIT;
+ }
+ if (ofb != null && mfb != null) {
+ mfb.copy(ofb, mask);
+ }
+ }
+ // We always read the screen pixels from the color FBO.
+ pixfb = ofb;
+ } else if (op == OP_WRITE) {
+ // We can write directly to the color FBO, or to the multisample FBO
+ // if multisampling is enabled.
+ pixfb = offscreenMultisample ? mfb : ofb;
}
- // We always read the screen pixels from the color FBO.
- pixfb = offscreenFramebuffer;
- } else if (op == OP_WRITE) {
- // We can write directly to the color FBO, or to the multisample FBO
- // if multisampling is enabled.
- pixfb = offscreenMultisample ? multisampleFramebuffer :
- offscreenFramebuffer;
}
}
// Set the framebuffer where the pixel operation shall be carried out.
- if (pixfb != getCurrentFB()) {
+ if (pixfb != null && pixfb != getCurrentFB()) {
pushFramebuffer();
setFramebuffer(pixfb);
pixOpChangedFB = true;
@@ -1909,9 +1991,9 @@ protected void beginPixelsOp(int op) {
// We read from/write to the draw buffer.
if (op == OP_READ) {
- pgl.readBuffer(getCurrentFB().getDefaultDrawBuffer());
+ if (readBufferSupported) pgl.readBuffer(getCurrentFB().getDefaultDrawBuffer());
} else if (op == OP_WRITE) {
- pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer());
+ if (drawBufferSupported) pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer());
}
pixelsOp = op;
@@ -1926,8 +2008,8 @@ protected void endPixelsOp() {
}
// Restoring default read/draw buffer configuration.
- pgl.readBuffer(getCurrentFB().getDefaultReadBuffer());
- pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer());
+ if (readBufferSupported) pgl.readBuffer(getCurrentFB().getDefaultReadBuffer());
+ if (drawBufferSupported) pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer());
pixelsOp = OP_NONE;
}
@@ -2050,8 +2132,6 @@ protected void defaultSettings() {
manipulatingCamera = false;
- clearColorBuffer = false;
-
// easiest for beginners
textureMode(IMAGE);
@@ -2115,6 +2195,23 @@ public void hint(int which) {
// We flush the geometry using the previous line setting.
flush();
}
+ } else if (which == ENABLE_DEPTH_SORT) {
+ if (is3D()) {
+ flush();
+ if (sorter == null) sorter = new DepthSorter(this);
+ isDepthSortingEnabled = true;
+ } else {
+ PGraphics.showWarning("Depth sorting can only be enabled in 3D");
+ }
+ } else if (which == DISABLE_DEPTH_SORT) {
+ if (is3D()) {
+ flush();
+ isDepthSortingEnabled = false;
+ }
+ } else if (which == ENABLE_BUFFER_READING) {
+ restartPGL();
+ } else if (which == DISABLE_BUFFER_READING) {
+ restartPGL();
}
}
@@ -2128,6 +2225,32 @@ protected boolean getHint(int which) {
}
+ //////////////////////////////////////////////////////////////
+
+ // CREATE SHAPE
+
+
+ @Override
+ protected PShape createShapeFamily(int type) {
+ PShape shape = new PShapeOpenGL(this, type);
+ if (is3D()) {
+ shape.set3D(true);
+ }
+ return shape;
+ }
+
+
+ @Override
+ protected PShape createShapePrimitive(int kind, float... p) {
+ PShape shape = new PShapeOpenGL(this, kind, p);
+ if (is3D()) {
+ shape.set3D(true);
+ }
+ return shape;
+ }
+
+
+
//////////////////////////////////////////////////////////////
// VERTEX SHAPES
@@ -2158,7 +2281,7 @@ public void endShape(int mode) {
flush();
} else {
// pixels array is not up-to-date anymore
- arePixelsUpToDate = false;
+ loaded = false;
}
}
@@ -2176,7 +2299,7 @@ protected void endShape(int[] indices) {
flush();
} else {
// pixels array is not up-to-date anymore
- arePixelsUpToDate = false;
+ loaded = false;
}
}
@@ -2239,6 +2362,81 @@ public void vertex(float x, float y, float z, float u, float v) {
}
+ @Override
+ public void attribPosition(String name, float x, float y, float z) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.POSITION,
+ PGL.FLOAT, 3);
+ if (attrib != null) attrib.set(x, y, z);
+ }
+
+
+ @Override
+ public void attribNormal(String name, float nx, float ny, float nz) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.NORMAL,
+ PGL.FLOAT, 3);
+ if (attrib != null) attrib.set(nx, ny, nz);
+ }
+
+
+ @Override
+ public void attribColor(String name, int color) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.COLOR, PGL.INT, 1);
+ if (attrib != null) attrib.set(new int[] {color});
+ }
+
+
+ @Override
+ public void attrib(String name, float... values) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER,
+ PGL.FLOAT, values.length);
+ if (attrib != null) attrib.set(values);
+ }
+
+
+ @Override
+ public void attrib(String name, int... values) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER,
+ PGL.INT, values.length);
+ if (attrib != null) attrib.set(values);
+ }
+
+
+ @Override
+ public void attrib(String name, boolean... values) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER,
+ PGL.BOOL, values.length);
+ if (attrib != null) attrib.set(values);
+ }
+
+
+ protected VertexAttribute attribImpl(String name, int kind, int type, int size) {
+ if (4 < size) {
+ PGraphics.showWarning("Vertex attributes cannot have more than 4 values");
+ return null;
+ }
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib == null) {
+ attrib = new VertexAttribute(this, name, kind, type, size);
+ polyAttribs.put(name, attrib);
+ inGeo.initAttrib(attrib);
+ tessGeo.initAttrib(attrib);
+ }
+ if (attrib.kind != kind) {
+ PGraphics.showWarning("The attribute kind cannot be changed after creation");
+ return null;
+ }
+ if (attrib.type != type) {
+ PGraphics.showWarning("The attribute type cannot be changed after creation");
+ return null;
+ }
+ if (attrib.size != size) {
+ PGraphics.showWarning("New value for vertex attribute has wrong number of values");
+ return null;
+ }
+ return attrib;
+ }
+
+
protected void vertexImpl(float x, float y, float z, float u, float v) {
boolean textured = textureImage != null;
int fcolor = 0x00;
@@ -2363,7 +2561,7 @@ protected void tessellate(int mode) {
if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadStripNormals();
tessellator.tessellateQuadStrip();
} else if (shape == POLYGON) {
- tessellator.tessellatePolygon(false, mode == CLOSE,
+ tessellator.tessellatePolygon(true, mode == CLOSE,
normalMode == NORMAL_MODE_AUTO);
}
}
@@ -2422,7 +2620,7 @@ public void flush() {
projmodelview.set(projection);
}
- if (hasPolys) {
+ if (hasPolys && !isDepthSortingEnabled) {
flushPolys();
if (raw != null) {
rawPolys();
@@ -2445,16 +2643,26 @@ public void flush() {
}
}
+ if (hasPolys && isDepthSortingEnabled) {
+ // We flush after lines so they are visible
+ // under transparent polygons
+ flushSortedPolys();
+ if (raw != null) {
+ rawSortedPolys();
+ }
+ }
+
if (flushMode == FLUSH_WHEN_FULL) {
modelview = modelview0;
modelviewInv = modelviewInv0;
updateProjmodelview();
}
+
+ loaded = false;
}
tessGeo.clear();
texCache.clear();
- arePixelsUpToDate = false;
}
@@ -2489,256 +2697,154 @@ protected void flushPolys() {
cache.indexOffset[n] + cache.indexCount[n] - ioffset;
int voffset = cache.vertexOffset[n];
- shader.setVertexAttribute(glPolyVertex, 4, PGL.FLOAT, 0,
+ shader.setVertexAttribute(bufPolyVertex.glId, 4, PGL.FLOAT, 0,
4 * voffset * PGL.SIZEOF_FLOAT);
- shader.setColorAttribute(glPolyColor, 4, PGL.UNSIGNED_BYTE, 0,
+ shader.setColorAttribute(bufPolyColor.glId, 4, PGL.UNSIGNED_BYTE, 0,
4 * voffset * PGL.SIZEOF_BYTE);
if (lights) {
- shader.setNormalAttribute(glPolyNormal, 3, PGL.FLOAT, 0,
+ shader.setNormalAttribute(bufPolyNormal.glId, 3, PGL.FLOAT, 0,
3 * voffset * PGL.SIZEOF_FLOAT);
- shader.setAmbientAttribute(glPolyAmbient, 4, PGL.UNSIGNED_BYTE, 0,
+ shader.setAmbientAttribute(bufPolyAmbient.glId, 4, PGL.UNSIGNED_BYTE, 0,
4 * voffset * PGL.SIZEOF_BYTE);
- shader.setSpecularAttribute(glPolySpecular, 4, PGL.UNSIGNED_BYTE, 0,
+ shader.setSpecularAttribute(bufPolySpecular.glId, 4, PGL.UNSIGNED_BYTE, 0,
4 * voffset * PGL.SIZEOF_BYTE);
- shader.setEmissiveAttribute(glPolyEmissive, 4, PGL.UNSIGNED_BYTE, 0,
+ shader.setEmissiveAttribute(bufPolyEmissive.glId, 4, PGL.UNSIGNED_BYTE, 0,
4 * voffset * PGL.SIZEOF_BYTE);
- shader.setShininessAttribute(glPolyShininess, 1, PGL.FLOAT, 0,
+ shader.setShininessAttribute(bufPolyShininess.glId, 1, PGL.FLOAT, 0,
voffset * PGL.SIZEOF_FLOAT);
}
if (lights || needNormals) {
- shader.setNormalAttribute(glPolyNormal, 3, PGL.FLOAT, 0,
+ shader.setNormalAttribute(bufPolyNormal.glId, 3, PGL.FLOAT, 0,
3 * voffset * PGL.SIZEOF_FLOAT);
}
if (tex != null || needTexCoords) {
- shader.setTexcoordAttribute(glPolyTexcoord, 2, PGL.FLOAT, 0,
+ shader.setTexcoordAttribute(bufPolyTexcoord.glId, 2, PGL.FLOAT, 0,
2 * voffset * PGL.SIZEOF_FLOAT);
shader.setTexture(tex);
}
- shader.draw(glPolyIndex, icount, ioffset);
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (!attrib.active(shader)) continue;
+ attrib.bind(pgl);
+ shader.setAttributeVBO(attrib.glLoc, attrib.buf.glId,
+ attrib.tessSize, attrib.type,
+ attrib.isColor(), 0, attrib.sizeInBytes(voffset));
+ }
+
+ shader.draw(bufPolyIndex.glId, icount, ioffset);
}
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (attrib.active(shader)) attrib.unbind(pgl);
+ }
shader.unbind();
}
unbindPolyBuffers();
}
+ protected void flushSortedPolys() {
+ boolean customShader = polyShader != null;
+ boolean needNormals = customShader ? polyShader.accessNormals() : false;
+ boolean needTexCoords = customShader ? polyShader.accessTexCoords() : false;
- class Triangle {
- int i0, i1, i2;
- PImage tex;
- float dist;
- Triangle(int i0, int i1, int i2, PImage tex, float dist) {
- this.i0 = i0;
- this.i1 = i1;
- this.i2 = i2;
- this.tex = tex;
- this.dist = dist;
- }
- }
- // Adapted from Ben Van Citters code:
- // http://openprocessing.org/sketch/100912
- Triangle[] sortedPolyTriangles = null;
- int sortedTriangleCount = 0;
- void sortTriangles() {
- if (sortedPolyTriangles == null) {
- sortedPolyTriangles = new Triangle[512];
- }
+ sorter.sort(tessGeo);
- float[] vertices = tessGeo.polyVertices;
- short[] indices = tessGeo.polyIndices;
- float[] src0 = {0, 0, 0, 0};
- float[] src1 = {0, 0, 0, 0};
- float[] src2 = {0, 0, 0, 0};
- float[] pt0 = {0, 0, 0, 0};
- float[] pt1 = {0, 0, 0, 0};
- float[] pt2 = {0, 0, 0, 0};
-
- sortedTriangleCount = 0;
- for (int i = 0; i < texCache.size; i++) {
- PImage textureImage = texCache.getTextureImage(i);
- int first = texCache.firstCache[i];
- int last = texCache.lastCache[i];
- IndexCache cache = tessGeo.polyIndexCache;
- for (int n = first; n <= last; n++) {
- int ioffset = n == first ? texCache.firstIndex[i] :
- cache.indexOffset[n];
- int icount = n == last ? texCache.lastIndex[i] - ioffset + 1 :
- cache.indexOffset[n] + cache.indexCount[n] -
- ioffset;
- int voffset = cache.vertexOffset[n];
- for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) {
- if (sortedPolyTriangles.length == sortedTriangleCount) {
- // expand array
- int newSize = sortedTriangleCount << 1;
- Triangle[] temp = new Triangle[newSize];
- PApplet.arrayCopy(sortedPolyTriangles, 0, temp, 0, newSize);
- sortedPolyTriangles = temp;
- }
+ int triangleCount = tessGeo.polyIndexCount / 3;
+ int[] texMap = sorter.texMap;
+ int[] voffsetMap = sorter.voffsetMap;
- int i0 = voffset + indices[3 * tr + 0];
- int i1 = voffset + indices[3 * tr + 1];
- int i2 = voffset + indices[3 * tr + 2];
- PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4);
- PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4);
- PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4);
- modelview.mult(src0, pt0);
- modelview.mult(src1, pt1);
- modelview.mult(src2, pt2);
- // add all three verts together and divide... could use another determination
- // of the 'depth' of the triangle such as min or max vert dist...
- float[] pos = new float[]{(pt0[X] + pt1[X] + pt2[X]) /3,
- (pt0[Y] + pt1[Y] + pt2[Y]) /3,
- (pt0[Z] + pt1[Z] + pt2[Z]) /3};
+ int[] vertexOffset = tessGeo.polyIndexCache.vertexOffset;
+
+ updatePolyBuffers(lights, texCache.hasTextures, needNormals, needTexCoords);
- // pt0, pt1 and pt2 are in eye coordinates since they have been
- // multiplied by the modelview matrix.
- float d = PApplet.dist(0f, 0f, 0f, pos[0], pos[1], pos[2]);
+ int ti = 0;
- Triangle tri = new Triangle(i0, i1, i2, textureImage, d);
- sortedPolyTriangles[sortedTriangleCount] = tri;
- sortedTriangleCount++;
- }
+ while (ti < triangleCount) {
+
+ int startTi = ti;
+ int texId = texMap[ti];
+ int voffsetId = voffsetMap[ti];
+
+ do {
+ ++ti;
+ } while (ti < triangleCount &&
+ texId == texMap[ti] &&
+ voffsetId == voffsetMap[ti]);
+
+ int endTi = ti;
+
+ Texture tex = texCache.getTexture(texId);
+
+ int voffset = vertexOffset[voffsetId];
+
+ int ioffset = 3 * startTi;
+ int icount = 3 * (endTi - startTi);
+
+ // If the renderer is 2D, then lights should always be false,
+ // so no need to worry about that.
+ PShader shader = getPolyShader(lights, tex != null);
+ shader.bind();
+
+ shader.setVertexAttribute(bufPolyVertex.glId, 4, PGL.FLOAT, 0,
+ 4 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setColorAttribute(bufPolyColor.glId, 4, PGL.UNSIGNED_BYTE, 0,
+ 4 * voffset * PGL.SIZEOF_BYTE);
+
+ if (lights) {
+ shader.setNormalAttribute(bufPolyNormal.glId, 3, PGL.FLOAT, 0,
+ 3 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setAmbientAttribute(bufPolyAmbient.glId, 4, PGL.UNSIGNED_BYTE, 0,
+ 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setSpecularAttribute(bufPolySpecular.glId, 4, PGL.UNSIGNED_BYTE, 0,
+ 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setEmissiveAttribute(bufPolyEmissive.glId, 4, PGL.UNSIGNED_BYTE, 0,
+ 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setShininessAttribute(bufPolyShininess.glId, 1, PGL.FLOAT, 0,
+ voffset * PGL.SIZEOF_FLOAT);
}
- }
- quickSortTris(0, sortedTriangleCount - 1);
- }
- // an 'in-place' implementation of quick I whipped together late at night
- // based off of the algorithm found on wikipedia: http://en.wikipedia.org/wiki/Quicksort
- private void quickSortTris(int leftI, int rightI) {
- if (leftI < rightI) {
- int pivotIndex = (leftI + rightI)/2;
- int newPivotIndex = partition(leftI,rightI,pivotIndex);
- quickSortTris(leftI, newPivotIndex-1);
- quickSortTris(newPivotIndex+1, rightI);
- }
- }
+ if (lights || needNormals) {
+ shader.setNormalAttribute(bufPolyNormal.glId, 3, PGL.FLOAT, 0,
+ 3 * voffset * PGL.SIZEOF_FLOAT);
+ }
- //part of quicksort
- private int partition(int leftIndex, int rightIndex, int pivotIndex) {
- float pivotVal = sortedPolyTriangles[pivotIndex].dist;
- swapTris(pivotIndex,rightIndex);
- int storeIndex = leftIndex;
- for(int i = leftIndex; i < rightIndex; i++)
- {
- if(sortedPolyTriangles[i].dist > pivotVal)
- {
- swapTris(i,storeIndex);
- storeIndex++;
+ if (tex != null || needTexCoords) {
+ shader.setTexcoordAttribute(bufPolyTexcoord.glId, 2, PGL.FLOAT, 0,
+ 2 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setTexture(tex);
}
- }
- swapTris(rightIndex,storeIndex);
- return storeIndex;
- }
- //part of quicksort
- private void swapTris(int a, int b) {
- Triangle tmp = sortedPolyTriangles[a];
- sortedPolyTriangles[a] = sortedPolyTriangles[b];
- sortedPolyTriangles[b] = tmp;
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (!attrib.active(shader)) continue;
+ attrib.bind(pgl);
+ shader.setAttributeVBO(attrib.glLoc, attrib.buf.glId,
+ attrib.tessSize, attrib.type,
+ attrib.isColor(), 0, attrib.sizeInBytes(voffset));
+ }
+
+ shader.draw(bufPolyIndex.glId, icount, ioffset);
+
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (attrib.active(shader)) attrib.unbind(pgl);
+ }
+ shader.unbind();
+ }
+ unbindPolyBuffers();
}
-
void rawPolys() {
raw.colorMode(RGB);
raw.noStroke();
raw.beginShape(TRIANGLES);
-
- //sortTriangles();
-
float[] vertices = tessGeo.polyVertices;
int[] color = tessGeo.polyColors;
float[] uv = tessGeo.polyTexCoords;
- short[] indices = tessGeo.polyIndices; // unused [fry]
-
-/*
- sortTriangles();
- for (int i = 0; i < sortedTriangleCount; i++) {
- Triangle tri = sortedPolyTriangles[i];
- int i0 = tri.i0;
- int i1 = tri.i1;
- int i2 = tri.i2;
- PImage tex = tri.tex;
-
- float[] pt0 = {0, 0, 0, 0};
- float[] pt1 = {0, 0, 0, 0};
- float[] pt2 = {0, 0, 0, 0};
- int argb0 = PGL.nativeToJavaARGB(color[i0]);
- int argb1 = PGL.nativeToJavaARGB(color[i1]);
- int argb2 = PGL.nativeToJavaARGB(color[i2]);
-
- if (flushMode == FLUSH_CONTINUOUSLY) {
- float[] src0 = {0, 0, 0, 0};
- float[] src1 = {0, 0, 0, 0};
- float[] src2 = {0, 0, 0, 0};
- PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4);
- PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4);
- PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4);
- modelview.mult(src0, pt0);
- modelview.mult(src1, pt1);
- modelview.mult(src2, pt2);
- } else {
- PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4);
- PApplet.arrayCopy(vertices, 4 * i1, pt1, 0, 4);
- PApplet.arrayCopy(vertices, 4 * i2, pt2, 0, 4);
- }
-
- if (tex != null) {
- raw.texture(tex);
- if (raw.is3D()) {
- raw.fill(argb0);
- raw.vertex(pt0[X], pt0[Y], pt0[Z], uv[2 * i0 + 0], uv[2 * i0 + 1]);
- raw.fill(argb1);
- raw.vertex(pt1[X], pt1[Y], pt1[Z], uv[2 * i1 + 0], uv[2 * i1 + 1]);
- raw.fill(argb2);
- raw.vertex(pt2[X], pt2[Y], pt2[Z], uv[2 * i2 + 0], uv[2 * i2 + 1]);
- } else if (raw.is2D()) {
- float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
- float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
- float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
- float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
- float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
- float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
- raw.fill(argb0);
- raw.vertex(sx0, sy0, uv[2 * i0 + 0], uv[2 * i0 + 1]);
- raw.fill(argb1);
- raw.vertex(sx1, sy1, uv[2 * i1 + 0], uv[2 * i1 + 1]);
- raw.fill(argb1);
- raw.vertex(sx2, sy2, uv[2 * i2 + 0], uv[2 * i2 + 1]);
- }
- } else {
- if (raw.is3D()) {
- raw.fill(argb0);
- raw.vertex(pt0[X], pt0[Y], pt0[Z]);
- raw.fill(argb1);
- raw.vertex(pt1[X], pt1[Y], pt1[Z]);
- raw.fill(argb2);
- raw.vertex(pt2[X], pt2[Y], pt2[Z]);
- } else if (raw.is2D()) {
- float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
- float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
- float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
- float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
- float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
- float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
- raw.fill(argb0);
- raw.vertex(sx0, sy0);
- raw.fill(argb1);
- raw.vertex(sx1, sy1);
- raw.fill(argb2);
- raw.vertex(sx2, sy2);
- }
- }
-
- }
-*/
-
+ short[] indices = tessGeo.polyIndices;
for (int i = 0; i < texCache.size; i++) {
PImage textureImage = texCache.getTextureImage(i);
@@ -2837,6 +2943,107 @@ void rawPolys() {
}
+ void rawSortedPolys() {
+ raw.colorMode(RGB);
+ raw.noStroke();
+ raw.beginShape(TRIANGLES);
+
+ float[] vertices = tessGeo.polyVertices;
+ int[] color = tessGeo.polyColors;
+ float[] uv = tessGeo.polyTexCoords;
+ short[] indices = tessGeo.polyIndices;
+
+ sorter.sort(tessGeo);
+ int[] triangleIndices = sorter.triangleIndices;
+ int[] texMap = sorter.texMap;
+ int[] voffsetMap = sorter.voffsetMap;
+
+ int[] vertexOffset = tessGeo.polyIndexCache.vertexOffset;
+
+ for (int i = 0; i < tessGeo.polyIndexCount/3; i++) {
+ int ti = triangleIndices[i];
+ PImage tex = texCache.getTextureImage(texMap[ti]);
+ int voffset = vertexOffset[voffsetMap[ti]];
+
+ int i0 = voffset + indices[3*ti+0];
+ int i1 = voffset + indices[3*ti+1];
+ int i2 = voffset + indices[3*ti+2];
+
+ float[] pt0 = {0, 0, 0, 0};
+ float[] pt1 = {0, 0, 0, 0};
+ float[] pt2 = {0, 0, 0, 0};
+ int argb0 = PGL.nativeToJavaARGB(color[i0]);
+ int argb1 = PGL.nativeToJavaARGB(color[i1]);
+ int argb2 = PGL.nativeToJavaARGB(color[i2]);
+
+ if (flushMode == FLUSH_CONTINUOUSLY) {
+ float[] src0 = {0, 0, 0, 0};
+ float[] src1 = {0, 0, 0, 0};
+ float[] src2 = {0, 0, 0, 0};
+ PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4);
+ PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4);
+ PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4);
+ modelview.mult(src0, pt0);
+ modelview.mult(src1, pt1);
+ modelview.mult(src2, pt2);
+ } else {
+ PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4);
+ PApplet.arrayCopy(vertices, 4 * i1, pt1, 0, 4);
+ PApplet.arrayCopy(vertices, 4 * i2, pt2, 0, 4);
+ }
+
+ if (tex != null) {
+ raw.texture(tex);
+ if (raw.is3D()) {
+ raw.fill(argb0);
+ raw.vertex(pt0[X], pt0[Y], pt0[Z], uv[2 * i0 + 0], uv[2 * i0 + 1]);
+ raw.fill(argb1);
+ raw.vertex(pt1[X], pt1[Y], pt1[Z], uv[2 * i1 + 0], uv[2 * i1 + 1]);
+ raw.fill(argb2);
+ raw.vertex(pt2[X], pt2[Y], pt2[Z], uv[2 * i2 + 0], uv[2 * i2 + 1]);
+ } else if (raw.is2D()) {
+ float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
+ float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
+ float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
+ float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
+ float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
+ float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
+ raw.fill(argb0);
+ raw.vertex(sx0, sy0, uv[2 * i0 + 0], uv[2 * i0 + 1]);
+ raw.fill(argb1);
+ raw.vertex(sx1, sy1, uv[2 * i1 + 0], uv[2 * i1 + 1]);
+ raw.fill(argb1);
+ raw.vertex(sx2, sy2, uv[2 * i2 + 0], uv[2 * i2 + 1]);
+ }
+ } else {
+ if (raw.is3D()) {
+ raw.fill(argb0);
+ raw.vertex(pt0[X], pt0[Y], pt0[Z]);
+ raw.fill(argb1);
+ raw.vertex(pt1[X], pt1[Y], pt1[Z]);
+ raw.fill(argb2);
+ raw.vertex(pt2[X], pt2[Y], pt2[Z]);
+ } else if (raw.is2D()) {
+ float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
+ float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]);
+ float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
+ float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]);
+ float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
+ float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]);
+ raw.fill(argb0);
+ raw.vertex(sx0, sy0);
+ raw.fill(argb1);
+ raw.vertex(sx1, sy1);
+ raw.fill(argb2);
+ raw.vertex(sx2, sy2);
+ }
+ }
+ }
+
+ raw.endShape();
+ }
+
+
protected void flushLines() {
updateLineBuffers();
@@ -2849,14 +3056,14 @@ protected void flushLines() {
int icount = cache.indexCount[n];
int voffset = cache.vertexOffset[n];
- shader.setVertexAttribute(glLineVertex, 4, PGL.FLOAT, 0,
+ shader.setVertexAttribute(bufLineVertex.glId, 4, PGL.FLOAT, 0,
4 * voffset * PGL.SIZEOF_FLOAT);
- shader.setColorAttribute(glLineColor, 4, PGL.UNSIGNED_BYTE, 0,
+ shader.setColorAttribute(bufLineColor.glId, 4, PGL.UNSIGNED_BYTE, 0,
4 * voffset * PGL.SIZEOF_BYTE);
- shader.setLineAttribute(glLineAttrib, 4, PGL.FLOAT, 0,
+ shader.setLineAttribute(bufLineAttrib.glId, 4, PGL.FLOAT, 0,
4 * voffset * PGL.SIZEOF_FLOAT);
- shader.draw(glLineIndex, icount, ioffset);
+ shader.draw(bufLineIndex.glId, icount, ioffset);
}
shader.unbind();
@@ -2950,14 +3157,14 @@ protected void flushPoints() {
int icount = cache.indexCount[n];
int voffset = cache.vertexOffset[n];
- shader.setVertexAttribute(glPointVertex, 4, PGL.FLOAT, 0,
+ shader.setVertexAttribute(bufPointVertex.glId, 4, PGL.FLOAT, 0,
4 * voffset * PGL.SIZEOF_FLOAT);
- shader.setColorAttribute(glPointColor, 4, PGL.UNSIGNED_BYTE, 0,
+ shader.setColorAttribute(bufPointColor.glId, 4, PGL.UNSIGNED_BYTE, 0,
4 * voffset * PGL.SIZEOF_BYTE);
- shader.setPointAttribute(glPointAttrib, 2, PGL.FLOAT, 0,
+ shader.setPointAttribute(bufPointAttrib.glId, 2, PGL.FLOAT, 0,
2 * voffset * PGL.SIZEOF_FLOAT);
- shader.draw(glPointIndex, icount, ioffset);
+ shader.draw(bufPointIndex.glId, icount, ioffset);
}
shader.unbind();
@@ -3251,7 +3458,6 @@ protected void arcImpl(float x, float y, float w, float h,
normalMode = NORMAL_MODE_SHAPE;
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
ambientColor, specularColor, emissiveColor, shininess);
-
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addArc(x, y, w, h, start, stop, fill, stroke, mode);
endShape();
@@ -3363,7 +3569,7 @@ public void sphere(float r) {
// SMOOTH
-
+/*
@Override
public void smooth() {
if (quality < 2) {
@@ -3399,13 +3605,16 @@ public void smooth(int level) {
quality = level;
- if (quality == 1) {
+ if (quality <= 1) {
quality = 0;
+ textureSampling = Texture.POINT;
+ } else {
+ textureSampling = Texture.TRILINEAR;
}
// This will trigger a surface restart next time
// requestDraw() is called.
- restartPGL();
+// restartPGL();
}
}
@@ -3415,6 +3624,7 @@ public void noSmooth() {
if (smoothDisabled) return;
smooth = false;
+ textureSampling = Texture.POINT;
if (1 < quality) {
smoothCallCount++;
@@ -3431,6 +3641,7 @@ public void noSmooth() {
restartPGL();
}
}
+ */
//////////////////////////////////////////////////////////////
@@ -3560,6 +3771,189 @@ protected boolean textModeCheck(int mode) {
// TEXT IMPL
+ @Override
+ public void text(char c, float x, float y) {
+ if (textFont == null) {
+ defaultFontOrDeath("text");
+ }
+
+ int sign = cameraUp ? -1 : +1;
+
+ if (textAlignY == CENTER) {
+ y += sign * textAscent() / 2;
+ } else if (textAlignY == TOP) {
+ y += sign * textAscent();
+ } else if (textAlignY == BOTTOM) {
+ y -= sign * textDescent();
+ //} else if (textAlignY == BASELINE) {
+ // do nothing
+ }
+
+ textBuffer[0] = c;
+ textLineAlignImpl(textBuffer, 0, 1, x, y);
+ }
+
+
+ @Override
+ public void text(String str, float x, float y) {
+ if (textFont == null) {
+ defaultFontOrDeath("text");
+ }
+
+ int sign = cameraUp ? -1 : +1;
+
+ int length = str.length();
+ if (length > textBuffer.length) {
+ textBuffer = new char[length + 10];
+ }
+ str.getChars(0, length, textBuffer, 0);
+
+ // If multiple lines, sum the height of the additional lines
+ float high = 0; //-textAscent();
+ for (int i = 0; i < length; i++) {
+ if (textBuffer[i] == '\n') {
+ high += sign * textLeading;
+ }
+ }
+ if (textAlignY == CENTER) {
+ // for a single line, this adds half the textAscent to y
+ // for multiple lines, subtract half the additional height
+ //y += (textAscent() - textDescent() - high)/2;
+ y += sign * (textAscent() - high)/2;
+ } else if (textAlignY == TOP) {
+ // for a single line, need to add textAscent to y
+ // for multiple lines, no different
+ y += sign * textAscent();
+ } else if (textAlignY == BOTTOM) {
+ // for a single line, this is just offset by the descent
+ // for multiple lines, subtract leading for each line
+ y -= sign * textDescent() + high;
+ }
+
+ int start = 0;
+ int index = 0;
+ while (index < length) {
+ if (textBuffer[index] == '\n') {
+ textLineAlignImpl(textBuffer, start, index, x, y);
+ start = index + 1;
+ y += sign * textLeading;
+ }
+ index++;
+ }
+ if (start < length) {
+ textLineAlignImpl(textBuffer, start, index, x, y);
+ }
+ }
+
+
+ @Override
+ public void text(String str, float x1, float y1, float x2, float y2) {
+ if (textFont == null) {
+ defaultFontOrDeath("text");
+ }
+
+ int sign = cameraUp ? -1 : +1;
+
+ float hradius, vradius;
+ switch (rectMode) {
+ case CORNER:
+ x2 += x1; y2 += y1;
+ break;
+ case 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;
+ }
+
+ float boxWidth = x2 - x1;
+
+ float spaceWidth = textWidth(' ');
+
+ if (textBreakStart == null) {
+ textBreakStart = new int[20];
+ textBreakStop = new int[20];
+ }
+ textBreakCount = 0;
+
+ int length = str.length();
+ if (length + 1 > textBuffer.length) {
+ textBuffer = new char[length + 1];
+ }
+ str.getChars(0, length, textBuffer, 0);
+ // add a fake newline to simplify calculations
+ textBuffer[length++] = '\n';
+
+ int sentenceStart = 0;
+ for (int i = 0; i < length; i++) {
+ if (textBuffer[i] == '\n') {
+ boolean legit =
+ textSentence(textBuffer, sentenceStart, i, boxWidth, spaceWidth);
+ if (!legit) break;
+ sentenceStart = i + 1;
+ }
+ }
+
+ // lineX is the position where the text starts, which is adjusted
+ // to left/center/right based on the current textAlign
+ float lineX = x1; //boxX1;
+ if (textAlign == CENTER) {
+ lineX = lineX + boxWidth/2f;
+ } else if (textAlign == RIGHT) {
+ lineX = x2; //boxX2;
+ }
+
+ float boxHeight = y2 - y1;
+ // incorporate textAscent() for the top (baseline will be y1 + ascent)
+ // and textDescent() for the bottom, so that lower parts of letters aren't
+ // outside the box. [0151]
+ float topAndBottom = textAscent() + textDescent();
+ int lineFitCount = 1 + PApplet.floor((boxHeight - topAndBottom) / textLeading);
+ int lineCount = Math.min(textBreakCount, lineFitCount);
+
+ if (textAlignY == CENTER) {
+ float lineHigh = textAscent() + textLeading * (lineCount - 1);
+ float y = cameraUp ? y2 - textAscent() - (boxHeight - lineHigh) / 2 :
+ y1 + textAscent() + (boxHeight - lineHigh) / 2;
+ for (int i = 0; i < lineCount; i++) {
+ textLineAlignImpl(textBuffer, textBreakStart[i], textBreakStop[i], lineX, y);
+ y += sign * textLeading;
+ }
+
+ } else if (textAlignY == BOTTOM) {
+ float y = cameraUp ? y1 + textDescent() + textLeading * (lineCount - 1) :
+ y2 - textDescent() - textLeading * (lineCount - 1);
+ for (int i = 0; i < lineCount; i++) {
+ textLineAlignImpl(textBuffer, textBreakStart[i], textBreakStop[i], lineX, y);
+ y += sign * textLeading;
+ }
+
+ } else { // TOP or BASELINE just go to the default
+ float y = cameraUp ? y2 - textAscent() : y1 + textAscent();
+ for (int i = 0; i < lineCount; i++) {
+ textLineAlignImpl(textBuffer, textBreakStart[i], textBreakStop[i], lineX, y);
+ y += sign * textLeading;
+ }
+ }
+ }
+
+
@Override
public float textAscent() {
if (textFont == null) defaultFontOrDeath("textAscent");
@@ -3573,7 +3967,7 @@ public float textAscent() {
@Override
public float textDescent() {
- if (textFont == null) defaultFontOrDeath("textAscent");
+ if (textFont == null) defaultFontOrDeath("textDescent");
Object font = textFont.getNative();
float descent = 0;
if (font != null) descent = pgl.getFontDescent(font);
@@ -3593,14 +3987,13 @@ protected float textWidthImpl(char buffer[], int start, int stop) {
@Override
- public void textSize(float size) {
- if (textFont == null) defaultFontOrDeath("textSize", size);
+ protected void handleTextSize(float size) {
Object font = textFont.getNative();
if (font != null) {
Object dfont = pgl.getDerivedFont(font, size);
- textFont.setNative(dfont);
+ if (dfont != null) textFont.setNative(dfont);
}
- super.textSize(size);
+ super.handleTextSize(size);
}
@@ -3682,10 +4075,14 @@ protected void textCharImpl(char ch, float x, float y) {
float lextent = glyph.leftExtent / (float) textFont.getSize();
float textent = glyph.topExtent / (float) textFont.getSize();
+ // The default text setting assumes an Y axis pointing down, so
+ // inverting in the the case Y points up
+ int sign = cameraUp ? -1 : +1;
+
float x1 = x + lextent * textSize;
- float y1 = y - textent * textSize;
+ float y1 = y - sign * textent * textSize;
float x2 = x1 + bwidth * textSize;
- float y2 = y1 + high * textSize;
+ float y2 = y1 + sign * high * textSize;
textCharModelImpl(tinfo, x1, y1, x2, y2);
} else if (textMode == SHAPE) {
@@ -3698,11 +4095,8 @@ protected void textCharImpl(char ch, float x, float y) {
protected void textCharModelImpl(FontTexture.TextureInfo info,
float x0, float y0,
float x1, float y1) {
- if (textTex.currentTex != info.texIndex) {
- textTex.setTexture(info.texIndex);
- }
beginShape(QUADS);
- texture(textTex.getCurrentTexture());
+ texture(textTex.getTexture(info));
vertex(x0, y0, info.u0, info.v0);
vertex(x1, y0, info.u1, info.v0);
vertex(x1, y1, info.u1, info.v1);
@@ -3861,9 +4255,40 @@ protected void translateImpl(float tx, float ty, float tz) {
static protected void invTranslate(PMatrix3D matrix,
float tx, float ty, float tz) {
matrix.preApply(1, 0, 0, -tx,
- 0, 1, 0, -ty,
- 0, 0, 1, -tz,
- 0, 0, 0, 1);
+ 0, 1, 0, -ty,
+ 0, 0, 1, -tz,
+ 0, 0, 0, 1);
+ }
+
+
+ static protected void invTranslate(PMatrix2D matrix,
+ float tx, float ty) {
+ matrix.preApply(1, 0, -tx,
+ 0, 1, -ty);
+ }
+
+
+ static protected float matrixScale(PMatrix matrix) {
+ // Volumetric scaling factor that is associated to the given
+ // transformation matrix, which is given by the absolute value of its
+ // determinant:
+ float factor = 1;
+
+ if (matrix != null) {
+ if (matrix instanceof PMatrix2D) {
+ PMatrix2D tr = (PMatrix2D)matrix;
+ float areaScaleFactor = Math.abs(tr.m00 * tr.m11 - tr.m01 * tr.m10);
+ factor = (float) Math.sqrt(areaScaleFactor);
+ } else if (matrix instanceof PMatrix3D) {
+ PMatrix3D tr = (PMatrix3D)matrix;
+ float volumeScaleFactor =
+ Math.abs(tr.m00 * (tr.m11 * tr.m22 - tr.m12 * tr.m21) +
+ tr.m01 * (tr.m12 * tr.m20 - tr.m10 * tr.m22) +
+ tr.m02 * (tr.m10 * tr.m21 - tr.m11 * tr.m20));
+ factor = (float) Math.pow(volumeScaleFactor, 1.0f / 3.0f);
+ }
+ }
+ return factor;
}
@@ -3928,16 +4353,21 @@ protected void rotateImpl(float angle, float v0, float v1, float v2) {
}
- static private void invRotate(PMatrix3D matrix, float angle,
- float v0, float v1, float v2) {
+ static protected void invRotate(PMatrix3D matrix, float angle,
+ float v0, float v1, float v2) {
float c = PApplet.cos(-angle);
float s = PApplet.sin(-angle);
float t = 1.0f - c;
matrix.preApply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0,
- (t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0,
- (t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0,
- 0, 0, 0, 1);
+ (t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0,
+ (t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0,
+ 0, 0, 0, 1);
+ }
+
+
+ static protected void invRotate(PMatrix2D matrix, float angle) {
+ matrix.rotate(-angle);
}
@@ -3982,6 +4412,11 @@ static protected void invScale(PMatrix3D matrix, float x, float y, float z) {
}
+ static protected void invScale(PMatrix2D matrix, float x, float y) {
+ matrix.preApply(1/x, 0, 0, 0, 1/y, 0);
+ }
+
+
@Override
public void shearX(float angle) {
float t = (float) Math.tan(angle);
@@ -4372,7 +4807,8 @@ public void endCamera() {
*/
@Override
public void camera() {
- camera(cameraX, cameraY, cameraZ, cameraX, cameraY, 0, 0, 1, 0);
+ camera(defCameraX, defCameraY, defCameraZ, defCameraX, defCameraY,
+ 0, 0, 1, 0);
}
@@ -4436,30 +4872,43 @@ public void camera() {
public void camera(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ) {
+ cameraX = eyeX;
+ cameraY = eyeY;
+ cameraZ = eyeZ;
+
// Calculating Z vector
float z0 = eyeX - centerX;
float z1 = eyeY - centerY;
float z2 = eyeZ - centerZ;
- float mag = PApplet.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
- if (nonZero(mag)) {
- z0 /= mag;
- z1 /= mag;
- z2 /= mag;
+ eyeDist = PApplet.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
+ if (nonZero(eyeDist)) {
+ z0 /= eyeDist;
+ z1 /= eyeDist;
+ z2 /= eyeDist;
}
- cameraEyeX = eyeX;
- cameraEyeY = eyeY;
- cameraEyeZ = eyeZ;
+
+ forwardX = z0;
+ forwardY = z1;
+ forwardZ = z2;
// Calculating Y vector
float y0 = upX;
float y1 = upY;
float y2 = upZ;
+ this.upX = upX;
+ this.upY = upY;
+ this.upZ = upZ;
+
// Computing X vector as Y cross Z
float x0 = y1 * z2 - y2 * z1;
float x1 = -y0 * z2 + y2 * z0;
float x2 = y0 * z1 - y1 * z0;
+ rightX = x0;
+ rightY = x1;
+ rightZ = x2;
+
// Recompute Y = Z cross X
y0 = z1 * x2 - z2 * x1;
y1 = -z0 * x2 + z2 * x0;
@@ -4467,18 +4916,18 @@ public void camera(float eyeX, float eyeY, float eyeZ,
// Cross product gives area of parallelogram, which is < 1.0 for
// non-perpendicular unit-length vectors; so normalize x, y here:
- mag = PApplet.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
- if (nonZero(mag)) {
- x0 /= mag;
- x1 /= mag;
- x2 /= mag;
+ float xmag = PApplet.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
+ if (nonZero(xmag)) {
+ x0 /= xmag;
+ x1 /= xmag;
+ x2 /= xmag;
}
- mag = PApplet.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
- if (nonZero(mag)) {
- y0 /= mag;
- y1 /= mag;
- y2 /= mag;
+ float ymag = PApplet.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
+ if (nonZero(ymag)) {
+ y0 /= ymag;
+ y1 /= ymag;
+ y2 /= ymag;
}
modelview.set(x0, x1, x2, 0,
@@ -4491,9 +4940,16 @@ public void camera(float eyeX, float eyeY, float eyeZ,
float tz = -eyeZ;
modelview.translate(tx, ty, tz);
- modelviewInv.set(modelview);
- modelviewInv.invert();
-
+ // The initial modelview transformation can be decomposed in a orthogonal
+ // matrix (with the inverse simply being the transpose), and a translation.
+ // The modelview inverse can then be calculated as follows, without need of
+ // employing the more general, slower, inverse() calculation.
+ modelviewInv.set(x0, y0, z0, 0,
+ x1, y1, z1, 0,
+ x2, y2, z2, 0,
+ 0, 0, 0, 1);
+ modelviewInv.translate(-tx, -ty, -tz);
+
camera.set(modelview);
cameraInv.set(modelviewInv);
@@ -4510,6 +4966,12 @@ public void printCamera() {
}
+ @Override
+ public void cameraUp() {
+ cameraUp = true;
+ }
+
+
protected void defaultCamera() {
camera();
}
@@ -4526,7 +4988,7 @@ protected void defaultCamera() {
*/
@Override
public void ortho() {
- ortho(0, width, 0, height, 0, cameraEyeZ * 10);
+ ortho(-width/2f, width/2f, -height/2f, height/2f, 0, eyeDist * 10);
}
@@ -4537,7 +4999,7 @@ public void ortho() {
@Override
public void ortho(float left, float right,
float bottom, float top) {
- ortho(left, right, bottom, top, 0, cameraEyeZ * 10);
+ ortho(left, right, bottom, top, 0, eyeDist * 10);
}
@@ -4553,13 +5015,6 @@ public void ortho(float left, float right,
float h = top - bottom;
float d = far - near;
- // Applying the camera translation (only on x and y, as near and far
- // are given as distances from the viewer)
- left -= cameraEyeX;
- right -= cameraEyeX;
- bottom -= cameraEyeY;
- top -= cameraEyeY;
-
// Flushing geometry with a different perspective configuration.
flush();
@@ -4603,7 +5058,7 @@ public void ortho(float left, float right,
*/
@Override
public void perspective() {
- perspective(cameraFOV, cameraAspect, cameraNear, cameraFar);
+ perspective(defCameraFOV, defCameraAspect, defCameraNear, defCameraFar);
}
@@ -4632,6 +5087,11 @@ public void frustum(float left, float right, float bottom, float top,
// Flushing geometry with a different perspective configuration.
flush();
+ cameraFOV = 2 * (float) Math.atan2(top, znear);
+ cameraAspect = left / bottom;
+ cameraNear = znear;
+ cameraFar = zfar;
+
float n2 = 2 * znear;
float w = right - left;
float h = top - bottom;
@@ -5206,18 +5666,14 @@ public void lightSpecular(float x, float y, float z) {
protected void enableLighting() {
- if (!lights) {
- flush(); // Flushing non-lit geometry.
- lights = true;
- }
+ flush();
+ lights = true;
}
protected void disableLighting() {
- if (lights) {
- flush(); // Flushing lit geometry.
- lights = false;
- }
+ flush();
+ lights = false;
}
@@ -5230,8 +5686,8 @@ protected void lightPosition(int num, float x, float y, float z,
lightPosition[4 * num + 2] =
x*modelview.m20 + y*modelview.m21 + z*modelview.m22 + modelview.m23;
- // Used to inicate if the light is directional or not.
- lightPosition[4 * num + 3] = dir ? 1: 0;
+ // Used to indicate if the light is directional or not.
+ lightPosition[4 * num + 3] = dir ? 0 : 1;
}
@@ -5245,10 +5701,17 @@ protected void lightNormal(int num, float dx, float dy, float dz) {
float nz =
dx*modelviewInv.m02 + dy*modelviewInv.m12 + dz*modelviewInv.m22;
- float invn = 1.0f / PApplet.dist(0, 0, 0, nx, ny, nz);
- lightNormal[3 * num + 0] = invn * nx;
- lightNormal[3 * num + 1] = invn * ny;
- lightNormal[3 * num + 2] = invn * nz;
+ float d = PApplet.dist(0, 0, 0, nx, ny, nz);
+ if (0 < d) {
+ float invn = 1.0f / d;
+ lightNormal[3 * num + 0] = invn * nx;
+ lightNormal[3 * num + 1] = invn * ny;
+ lightNormal[3 * num + 2] = invn * nz;
+ } else {
+ lightNormal[3 * num + 0] = 0;
+ lightNormal[3 * num + 1] = 0;
+ lightNormal[3 * num + 2] = 0;
+ }
}
@@ -5331,31 +5794,21 @@ protected void noLightSpot(int num) {
protected void backgroundImpl(PImage image) {
backgroundImpl();
set(0, 0, image);
- if (0 < parent.frameCount) {
- clearColorBuffer = true;
- }
// Setting the background as opaque. If this an offscreen surface, the
// alpha channel will be set to 1 in endOffscreenDraw(), even if
// blending operations during draw create translucent areas in the
// color buffer.
backgroundA = 1;
+ loaded = false;
}
@Override
protected void backgroundImpl() {
flush();
-
- if (!hints[DISABLE_DEPTH_MASK]) {
- pgl.clearDepth(1);
- pgl.clear(PGL.DEPTH_BUFFER_BIT);
- }
-
- pgl.clearColor(backgroundR, backgroundG, backgroundB, backgroundA);
- pgl.clear(PGL.COLOR_BUFFER_BIT);
- if (0 < parent.frameCount) {
- clearColorBuffer = true;
- }
+ pgl.clearBackground(backgroundR, backgroundG, backgroundB, backgroundA,
+ !hints[DISABLE_DEPTH_MASK], true);
+ loaded = false;
}
@@ -5438,7 +5891,7 @@ public boolean isGL() {
// color buffer into it.
@Override
public void loadPixels() {
- if (primarySurface && sized) {
+ if (primaryGraphics && sized) {
// Something wrong going on with threading, sized can never be true if
// all the steps in a resize happen inside the Animation thread.
return;
@@ -5450,7 +5903,7 @@ public void loadPixels() {
needEndDraw = true;
}
- if (!arePixelsUpToDate) {
+ if (!loaded) {
// Draws any remaining geometry in case the user is still not
// setting/getting new pixels.
flush();
@@ -5458,12 +5911,13 @@ public void loadPixels() {
allocatePixels();
- if (!arePixelsUpToDate) {
+ if (!loaded) {
readPixels();
}
// Pixels are now up-to-date, set the flag.
- arePixelsUpToDate = true;
+ loaded = true;
+
if (needEndDraw) {
endDraw();
@@ -5472,25 +5926,17 @@ public void loadPixels() {
protected void allocatePixels() {
- if ((pixels == null) || (pixels.length != width * height)) {
- pixels = new int[width * height];
+ updatePixelSize();
+ if ((pixels == null) || (pixels.length != pixelWidth * pixelHeight)) {
+ pixels = new int[pixelWidth * pixelHeight];
pixelBuffer = PGL.allocateIntBuffer(pixels);
+ loaded = false;
}
}
- protected void saveSurfaceToPixels() {
- allocatePixels();
- readPixels();
- }
-
-
- protected void restoreSurfaceFromPixels() {
- drawPixels(0, 0, width, height);
- }
-
-
protected void readPixels() {
+ updatePixelSize();
beginPixelsOp(OP_READ);
try {
// The readPixelsImpl() call in inside a try/catch block because it appears
@@ -5498,7 +5944,7 @@ protected void readPixels() {
// thread instead of the Animation thread right after a resize. Because
// of this the width and height might have a different size than the
// one of the pixels arrays.
- pgl.readPixelsImpl(0, 0, width, height, PGL.RGBA, PGL.UNSIGNED_BYTE,
+ pgl.readPixelsImpl(0, 0, pixelWidth, pixelHeight, PGL.RGBA, PGL.UNSIGNED_BYTE,
pixelBuffer);
} catch (IndexOutOfBoundsException e) {
// Silently catch the exception.
@@ -5507,14 +5953,20 @@ protected void readPixels() {
try {
// Idem...
PGL.getIntArray(pixelBuffer, pixels);
- PGL.nativeToJavaARGB(pixels, width, height);
+ PGL.nativeToJavaARGB(pixels, pixelWidth, pixelHeight);
} catch (ArrayIndexOutOfBoundsException e) {
}
}
protected void drawPixels(int x, int y, int w, int h) {
- int len = w * h;
+ drawPixels(pixels, x, y, w, h);
+ }
+
+
+ protected void drawPixels(int[] pixBuffer, int x, int y, int w, int h) {
+ int f = (int)pgl.getPixelScale();
+ int len = f * w * f * h;
if (nativePixels == null || nativePixels.length < len) {
nativePixels = new int[len];
nativePixelBuffer = PGL.allocateIntBuffer(nativePixels);
@@ -5525,31 +5977,32 @@ protected void drawPixels(int x, int y, int w, int h) {
// The pixels to be copied to the texture need to be consecutive, and
// they are not in the pixels array, so putting each row one after
// another in nativePixels.
- int offset0 = y * width + x;
+ int offset0 = f * (y * width + x);
int offset1 = 0;
- for (int yc = y; yc < y + h; yc++) {
- System.arraycopy(pixels, offset0, nativePixels, offset1, w);
- offset0 += width;
- offset1 += w;
+ for (int yc = f * y; yc < f * (y + h); yc++) {
+ System.arraycopy(pixBuffer, offset0, nativePixels, offset1, f * w);
+ offset0 += f * width;
+ offset1 += f * w;
}
} else {
- PApplet.arrayCopy(pixels, 0, nativePixels, 0, len);
+ PApplet.arrayCopy(pixBuffer, 0, nativePixels, 0, len);
}
- PGL.javaToNativeARGB(nativePixels, w, h);
+ PGL.javaToNativeARGB(nativePixels, f * w, f * h);
} catch (ArrayIndexOutOfBoundsException e) {
}
PGL.putIntArray(nativePixelBuffer, nativePixels);
// Copying pixel buffer to screen texture...
- if (primarySurface && !pgl.isFBOBacked()) {
+ if (primaryGraphics && !pgl.isFBOBacked()) {
// First making sure that the screen texture is valid. Only in the case
// of non-FBO-backed primary surface we might need to create the texture.
loadTextureImpl(POINT, false);
}
- boolean needToDrawTex = primarySurface && (!pgl.isFBOBacked() ||
+ boolean needToDrawTex = primaryGraphics && (!pgl.isFBOBacked() ||
(pgl.isFBOBacked() && pgl.isMultisampled())) ||
offscreenMultisample;
+ if (texture == null) return;
if (needToDrawTex) {
// The texture to screen needs to be drawn only if we are on the primary
// surface w/out FBO-layer, or with FBO-layer and multisampling. Or, we
@@ -5559,23 +6012,130 @@ protected void drawPixels(int x, int y, int w, int h) {
// (off)screen buffer.
// First, copy the pixels to the texture. We don't need to invert the
// pixel copy because the texture will be drawn inverted.
- int tw = PApplet.min(texture.glWidth - x, w);
- int th = PApplet.min(texture.glHeight - y, h);
+ int tw = PApplet.min(texture.glWidth - f * x, f * w);
+ int th = PApplet.min(texture.glHeight - f * y, f * h);
pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName,
- x, y, tw, th, nativePixelBuffer);
+ f * x, f * y, tw, th, nativePixelBuffer);
beginPixelsOp(OP_WRITE);
drawTexture(x, y, w, h);
endPixelsOp();
} else {
// We only need to copy the pixels to the back texture where we are
- // currently drawing to. Because the texture is invertex along Y, we
+ // currently drawing to. Because the texture is inverted along Y, we
// need to reflect that in the vertical arguments.
pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName,
- x, height - (y + h), w, h, nativePixelBuffer);
+ f * x, f * (height - (y + h)), f * w, f * h, nativePixelBuffer);
+ }
+ }
+
+
+ @Override
+ protected void clearState() {
+ super.clearState();
+ if (restoreFilename != null) {
+ File cacheFile = new File(restoreFilename);
+ cacheFile.delete();
+ }
+ }
+
+
+ @Override
+ protected void saveState() {
+ super.saveState();
+
+ // Queue the pixel read operation so it is performed when the surface is ready
+ pgl.queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ Context context = parent.getContext();
+ if (context == null || parent.getSurface().getComponent().isService()) return;
+ try {
+ restoreWidth = pixelWidth;
+ restoreHeight = pixelHeight;
+
+ int[] restorePixels = new int[restoreWidth * restoreHeight];
+ IntBuffer buf = IntBuffer.wrap(restorePixels);
+ buf.position(0);
+ beginPixelsOp(OP_READ);
+ pgl.readPixelsImpl(0, 0, pixelWidth, pixelHeight, PGL.RGBA, PGL.UNSIGNED_BYTE, buf);
+ endPixelsOp();
+
+ // Tries to use external but if not mounted, falls back on internal storage, as shown in
+ // https://developer.android.com/topic/performance/graphics/cache-bitmap#java
+ File cacheDir = Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !isExternalStorageRemovable() ?
+ context.getExternalCacheDir() : context.getCacheDir();
+ File cacheFile = new File(cacheDir + File.separator + "restore_pixels");
+ restoreFilename = cacheFile.getAbsolutePath();
+
+ FileOutputStream stream = new FileOutputStream(cacheFile);
+ ObjectOutputStream dout = new ObjectOutputStream(stream);
+ dout.writeObject(restorePixels);
+ dout.flush();
+ stream.getFD().sync();
+ stream.close();
+ } catch (Exception ex) {
+ PGraphics.showWarning("Could not save screen contents to cache");
+ ex.printStackTrace();
+ }
+ }
+ });
+ }
+
+
+ @Override
+ protected void restoreSurface() {
+ if (changed) {
+ changed = false;
+ if (restoreFilename != null && restoreWidth == pixelWidth && restoreHeight == pixelHeight) {
+ // Set restore count to 2 so it draws the bitmap two frames after surface change, otherwise
+ // the restoration does not work because the OpenGL renderer sometimes resizes the surface
+ // twice after restoring the app to the foreground... this may be due to broken graphics
+ // drivers, hacks in the GLSurfaceView class from the Replica Island game point to that,
+ // although those seem to be quite old:
+ // https://gamedev.stackexchange.com/questions/12629/workaround-to-losing-the-opengl-context-when-android-pauses
+ // "It fails in a very specific case: when the EGL context is lost due to resource constraints,
+ // and then recreated, if GL commands are sent within two frames of the surface being created
+ // then eglSwapBuffers() will hang."
+ // However, the same number showing up makes me thing this issue continue to exist to this day.
+ restoreCount = 2;
+ }
+ } else if (restoreCount > 0) {
+ restoreCount--;
+ if (restoreCount == 0) {
+ Context context = parent.getContext();
+ if (context == null) return;
+ try {
+ // Load cached pixels and draw
+ File cacheFile = new File(restoreFilename);
+ FileInputStream inStream = new FileInputStream(cacheFile);
+ ObjectInputStream din = new ObjectInputStream(inStream);
+ int[] restorePixels = (int[]) din.readObject();
+ if (restorePixels.length == pixelWidth * pixelHeight) {
+ PGL.nativeToJavaARGB(restorePixels, pixelWidth, pixelHeight);
+ drawPixels(restorePixels, 0, 0, pixelWidth, pixelHeight);
+ }
+ inStream.close();
+ cacheFile.delete();
+ } catch (Exception ex) {
+ PGraphics.showWarning("Could not restore screen contents from cache");
+ ex.printStackTrace();
+ } finally {
+ restoreFilename = null;
+ restoreWidth = -1;
+ restoreHeight = -1;
+ restoredSurface = true;
+ }
+ }
}
+ super.restoreSurface();
}
+ @Override
+ protected boolean requestNoLoop() {
+ return true;
+ }
+
//////////////////////////////////////////////////////////////
// GET/SET PIXELS
@@ -5610,20 +6170,264 @@ protected void setImpl(PImage sourceImage,
int sourceX, int sourceY,
int sourceWidth, int sourceHeight,
int targetX, int targetY) {
- loadPixels();
- super.setImpl(sourceImage, sourceX, sourceY, sourceWidth, sourceHeight,
- targetX, targetY);
- // do we need this?
- // see https://github.com/processing/processing/issues/2125
-// if (sourceImage.format == RGB) {
-// int targetOffset = targetY * width + targetX;
-// for (int y = sourceY; y < sourceY + sourceHeight; y++) {
-// for (int x = targetOffset; x < targetOffset + sourceWidth; x++) {
-// pixels[x] |= 0xff000000;
-// }
-// targetOffset += width;
-// }
-// }
+ updatePixelSize();
+
+ if (sourceImage.pixels == null) {
+ // Copies the pixels
+ loadPixels();
+ sourceImage.loadPixels();
+ int sourceOffset = sourceY * sourceImage.pixelWidth + sourceX;
+ int targetOffset = targetY * pixelWidth + targetX;
+ for (int y = sourceY; y < sourceY + sourceHeight; y++) {
+ System.arraycopy(sourceImage.pixels, sourceOffset, pixels, targetOffset, sourceWidth);
+ sourceOffset += sourceImage.pixelWidth;
+ targetOffset += pixelWidth;
+ }
+ }
+
+ // Draws the texture, copy() is very efficient because it simply renders
+ // the texture cache of sourceImage using OpenGL.
+ copy(sourceImage,
+ sourceX, sourceY, sourceWidth, sourceHeight,
+ targetX, targetY, sourceWidth, sourceHeight);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SAVE
+
+
+ @Override
+ public boolean save(String filename) {
+ return saveImpl(filename);
+ }
+
+
+ @Override
+ protected void processImageBeforeAsyncSave(PImage image) {
+ if (image.format == AsyncPixelReader.OPENGL_NATIVE) {
+ PGL.nativeToJavaARGB(image.pixels, image.width, image.height);
+ image.format = ARGB;
+ } else if (image.format == AsyncPixelReader.OPENGL_NATIVE_OPAQUE) {
+ PGL.nativeToJavaRGB(image.pixels, image.width, image.height);
+ image.format = RGB;
+ }
+ }
+
+
+ protected static void completeFinishedPixelTransfers() {
+ ongoingPixelTransfersIterable.addAll(ongoingPixelTransfers);
+ for (PGraphicsOpenGL.AsyncPixelReader pixelReader :
+ ongoingPixelTransfersIterable) {
+ // if the getter was not called this frame,
+ // tell it to check for completed transfers now
+ if (!pixelReader.calledThisFrame) {
+ pixelReader.completeFinishedTransfers();
+ }
+ pixelReader.calledThisFrame = false;
+ }
+ ongoingPixelTransfersIterable.clear();
+ }
+
+ protected static void completeAllPixelTransfers() {
+ ongoingPixelTransfersIterable.addAll(ongoingPixelTransfers);
+ for (PGraphicsOpenGL.AsyncPixelReader pixelReader :
+ ongoingPixelTransfersIterable) {
+ pixelReader.completeAllTransfers();
+ }
+ ongoingPixelTransfersIterable.clear();
+ }
+
+
+ protected class AsyncPixelReader {
+
+ // PImage formats used internally to offload
+ // color format conversion to save threads
+ static final int OPENGL_NATIVE = -1;
+ static final int OPENGL_NATIVE_OPAQUE = -2;
+
+ static final int BUFFER_COUNT = 3;
+
+ int[] pbos;
+ long[] fences;
+ String[] filenames;
+ int[] widths;
+ int[] heights;
+
+ int head;
+ int tail;
+ int size;
+
+ boolean supportsAsyncTransfers;
+
+ boolean calledThisFrame;
+
+
+ /// PGRAPHICS API //////////////////////////////////////////////////////////
+
+ public AsyncPixelReader() {
+ supportsAsyncTransfers = pgl.hasPBOs() && pgl.hasSynchronization();
+ if (supportsAsyncTransfers) {
+ pbos = new int[BUFFER_COUNT];
+ fences = new long[BUFFER_COUNT];
+ filenames = new String[BUFFER_COUNT];
+ widths = new int[BUFFER_COUNT];
+ heights = new int[BUFFER_COUNT];
+
+ IntBuffer intBuffer = PGL.allocateIntBuffer(BUFFER_COUNT);
+ intBuffer.rewind();
+ pgl.genBuffers(BUFFER_COUNT, intBuffer);
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ pbos[i] = intBuffer.get(i);
+ }
+ }
+ }
+
+
+ public void dispose() {
+ if (fences != null) {
+ while (size > 0) {
+ pgl.deleteSync(fences[tail]);
+ size--;
+ tail = (tail + 1) % BUFFER_COUNT;
+ }
+ fences = null;
+ }
+ if (pbos != null) {
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ IntBuffer intBuffer = PGL.allocateIntBuffer(pbos);
+ pgl.deleteBuffers(BUFFER_COUNT, intBuffer);
+ }
+ pbos = null;
+ }
+ filenames = null;
+ widths = null;
+ heights = null;
+ size = 0;
+ head = 0;
+ tail = 0;
+ calledThisFrame = false;
+ ongoingPixelTransfers.remove(this);
+ }
+
+
+ public void readAndSaveAsync(final String filename) {
+ if (size > 0) {
+ boolean shouldRead = (size == BUFFER_COUNT);
+ if (!shouldRead) shouldRead = isLastTransferComplete();
+ if (shouldRead) endTransfer();
+ } else {
+ ongoingPixelTransfers.add(this);
+ }
+ beginTransfer(filename);
+ calledThisFrame = true;
+ }
+
+
+ public void completeFinishedTransfers() {
+ if (size <= 0 || !asyncImageSaver.hasAvailableTarget()) return;
+
+ boolean needEndDraw = false;
+ if (!drawing) {
+ beginDraw();
+ needEndDraw = true;
+ }
+
+ while (asyncImageSaver.hasAvailableTarget() &&
+ isLastTransferComplete()) {
+ endTransfer();
+ }
+
+ // make sure to always unregister if there are no ongoing transfers
+ // so that PGraphics can be GC'd if needed
+ if (size <= 0) ongoingPixelTransfers.remove(this);
+
+ if (needEndDraw) endDraw();
+ }
+
+
+ protected void completeAllTransfers() {
+ if (size <= 0) return;
+
+ boolean needEndDraw = false;
+ if (!drawing) {
+ beginDraw();
+ needEndDraw = true;
+ }
+
+ while (size > 0) {
+ endTransfer();
+ }
+
+ // make sure to always unregister if there are no ongoing transfers
+ // so that PGraphics can be GC'd if needed
+ ongoingPixelTransfers.remove(this);
+
+ if (needEndDraw) endDraw();
+ }
+
+
+ /// TRANSFERS //////////////////////////////////////////////////////////////
+
+ public boolean isLastTransferComplete() {
+ if (size <= 0) return false;
+ int status = pgl.clientWaitSync(fences[tail], 0, 0);
+ return (status == PGL.ALREADY_SIGNALED) ||
+ (status == PGL.CONDITION_SATISFIED);
+ }
+
+
+ public void beginTransfer(String filename) {
+ // check the size of the buffer
+ if (widths[head] != pixelWidth || heights[head] != pixelHeight) {
+ if (widths[head] * heights[head] != pixelWidth * pixelHeight) {
+ pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, pbos[head]);
+ pgl.bufferData(PGL.PIXEL_PACK_BUFFER,
+ Integer.SIZE/8 * pixelWidth * pixelHeight,
+ null, PGL.STREAM_READ);
+ }
+ widths[head] = pixelWidth;
+ heights[head] = pixelHeight;
+ pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, 0);
+ }
+
+ pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, pbos[head]);
+ pgl.readPixels(0, 0, pixelWidth, pixelHeight, PGL.RGBA, PGL.UNSIGNED_BYTE, 0);
+ pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, 0);
+
+ fences[head] = pgl.fenceSync(PGL.SYNC_GPU_COMMANDS_COMPLETE, 0);
+ filenames[head] = filename;
+
+ head = (head + 1) % BUFFER_COUNT;
+ size++;
+ }
+
+
+ public void endTransfer() {
+ pgl.deleteSync(fences[tail]);
+ pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, pbos[tail]);
+ ByteBuffer readBuffer = pgl.mapBuffer(PGL.PIXEL_PACK_BUFFER,
+ PGL.READ_ONLY);
+ if (readBuffer != null) {
+ int format = primaryGraphics ? OPENGL_NATIVE_OPAQUE : OPENGL_NATIVE;
+ PImage target = asyncImageSaver.getAvailableTarget(widths[tail],
+ heights[tail],
+ format);
+ if (target == null) return;
+ readBuffer.rewind();
+ readBuffer.asIntBuffer().get(target.pixels);
+ pgl.unmapBuffer(PGL.PIXEL_PACK_BUFFER);
+ asyncImageSaver.saveTargetAsync(PGraphicsOpenGL.this, target,
+ filenames[tail]);
+ }
+
+ pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, 0);
+
+ size--;
+ tail = (tail + 1) % BUFFER_COUNT;
+ }
+
}
@@ -5643,7 +6447,9 @@ public void loadTexture() {
flush(); // To make sure the color buffer is updated.
- if (primarySurface) {
+ if (primaryGraphics) {
+ updatePixelSize();
+
if (pgl.isFBOBacked()) {
// In the case of MSAA, this is needed so the back buffer is in sync
// with the rendering.
@@ -5654,26 +6460,32 @@ public void loadTexture() {
// Here we go the slow route: we first copy the contents of the color
// buffer into a pixels array (but we keep it in native format) and
// then copy this array into the texture.
- if (nativePixels == null || nativePixels.length < width * height) {
- nativePixels = new int[width * height];
+ if (nativePixels == null || nativePixels.length < pixelWidth * pixelHeight) {
+ nativePixels = new int[pixelWidth * pixelHeight];
nativePixelBuffer = PGL.allocateIntBuffer(nativePixels);
}
beginPixelsOp(OP_READ);
try {
// See comments in readPixels() for the reason for this try/catch.
- pgl.readPixelsImpl(0, 0, width, height, PGL.RGBA, PGL.UNSIGNED_BYTE,
+ pgl.readPixelsImpl(0, 0, pixelWidth, pixelHeight, PGL.RGBA, PGL.UNSIGNED_BYTE,
nativePixelBuffer);
} catch (IndexOutOfBoundsException e) {
}
endPixelsOp();
- texture.setNative(nativePixelBuffer, 0, 0, width, height);
+ if (texture != null) {
+ texture.setNative(nativePixelBuffer, 0, 0, pixelWidth, pixelHeight);
+ }
}
} else if (offscreenMultisample) {
// We need to copy the contents of the multisampled buffer to the color
// buffer, so the later is up-to-date with the last drawing.
- multisampleFramebuffer.copyColor(offscreenFramebuffer);
+ FrameBuffer ofb = offscreenFramebuffer;
+ FrameBuffer mfb = multisampleFramebuffer;
+ if (ofb != null && mfb != null) {
+ mfb.copyColor(ofb);
+ }
}
if (needEndDraw) {
@@ -5684,14 +6496,18 @@ public void loadTexture() {
// Just marks the whole texture as updated
public void updateTexture() {
- texture.updateTexels();
+ if (texture != null) {
+ texture.updateTexels();
+ }
}
// Marks the specified rectanglular subregion in the texture as
// updated.
public void updateTexture(int x, int y, int w, int h) {
- texture.updateTexels(x, y, w, h);
+ if (texture != null) {
+ texture.updateTexels(x, y, w, h);
+ }
}
@@ -5705,12 +6521,13 @@ public void updateDisplay() {
protected void loadTextureImpl(int sampling, boolean mipmap) {
- if (width == 0 || height == 0) return;
+ updatePixelSize();
+ if (pixelWidth == 0 || pixelHeight == 0) return;
if (texture == null || texture.contextIsOutdated()) {
Texture.Parameters params = new Texture.Parameters(ARGB,
sampling, mipmap);
- texture = new Texture(this, width, height, params);
- texture.invertedY(true);
+ texture = new Texture(this, pixelWidth, pixelHeight, params);
+ texture.invertedY(!cameraUp);
texture.colorBuffer(true);
setCache(this, texture);
}
@@ -5718,42 +6535,51 @@ protected void loadTextureImpl(int sampling, boolean mipmap) {
protected void createPTexture() {
- ptexture = new Texture(this, width, height, texture.getParameters());
- ptexture.invertedY(true);
- ptexture.colorBuffer(true);
+ updatePixelSize();
+ if (texture != null) {
+ ptexture = new Texture(this, pixelWidth, pixelHeight, texture.getParameters());
+ ptexture.invertedY(!cameraUp);
+ ptexture.colorBuffer(true);
+ }
}
protected void swapOffscreenTextures() {
- if (ptexture != null) {
+ FrameBuffer ofb = offscreenFramebuffer;
+ if (texture != null && ptexture != null && ofb != null) {
int temp = texture.glName;
texture.glName = ptexture.glName;
ptexture.glName = temp;
- offscreenFramebuffer.setColorBuffer(texture);
+ ofb.setColorBuffer(texture);
}
}
protected void drawTexture() {
- // No blend so the texure replaces wherever is on the screen,
- // irrespective of the alpha
- pgl.disable(PGL.BLEND);
- pgl.drawTexture(texture.glTarget, texture.glName,
- texture.glWidth, texture.glHeight,
- 0, 0, width, height);
- pgl.enable(PGL.BLEND);
+ if (texture != null) {
+ // No blend so the texure replaces wherever is on the screen,
+ // irrespective of the alpha
+ pgl.disable(PGL.BLEND);
+ pgl.drawTexture(texture.glTarget, texture.glName,
+ texture.glWidth, texture.glHeight,
+ 0, 0, width, height);
+ pgl.enable(PGL.BLEND);
+ }
}
protected void drawTexture(int x, int y, int w, int h) {
- // Processing Y axis is inverted with respect to OpenGL, so we need to
- // invert the y coordinates of the screen rectangle.
- pgl.disable(PGL.BLEND);
- pgl.drawTexture(texture.glTarget, texture.glName,
- texture.glWidth, texture.glHeight, width, height,
- x, y, x + w, y + h,
- x, height - (y + h), x + w, height - y);
- pgl.enable(PGL.BLEND);
+ if (texture != null) {
+ // Processing Y axis is inverted with respect to OpenGL, so we need to
+ // invert the y coordinates of the screen rectangle.
+ pgl.disable(PGL.BLEND);
+ pgl.drawTexture(texture.glTarget, texture.glName,
+ texture.glWidth, texture.glHeight,
+ 0, 0, width, height,
+ x, y, x + w, y + h,
+ x, height - (y + h), x + w, height - y);
+ pgl.enable(PGL.BLEND);
+ }
}
@@ -5785,7 +6611,8 @@ protected void drawPTexture() {
@Override
public void mask(PImage alpha) {
- if (alpha.width != width || alpha.height != height) {
+ updatePixelSize();
+ if (alpha.width != pixelWidth || alpha.height != pixelHeight) {
throw new RuntimeException("The PImage used with mask() must be " +
"the same size as the applet.");
}
@@ -5840,8 +6667,9 @@ public void filter(PShader shader) {
}
boolean needEndDraw = false;
- if (primarySurface) pgl.requestFBOLayer();
- else if (!drawing) {
+ if (primaryGraphics) {
+ pgl.enableFBOLayer();
+ } else if (!drawing) {
beginDraw();
needEndDraw = true;
}
@@ -5850,7 +6678,7 @@ else if (!drawing) {
if (filterTexture == null || filterTexture.contextIsOutdated()) {
filterTexture = new Texture(this, texture.width, texture.height,
texture.getParameters());
- filterTexture.invertedY(true);
+ filterTexture.invertedY(!cameraUp);
filterImage = wrapTexture(filterTexture);
}
filterTexture.set(texture);
@@ -5916,12 +6744,11 @@ else if (!drawing) {
@Override
public void copy(int sx, int sy, int sw, int sh,
int dx, int dy, int dw, int dh) {
- if (primarySurface) pgl.requestFBOLayer();
+ if (primaryGraphics) pgl.enableFBOLayer();
loadTexture();
if (filterTexture == null || filterTexture.contextIsOutdated()) {
- filterTexture = new Texture(this, texture.width, texture.height,
- texture.getParameters());
- filterTexture.invertedY(true);
+ filterTexture = new Texture(this, texture.width, texture.height, texture.getParameters());
+ filterTexture.invertedY(!cameraUp);
filterImage = wrapTexture(filterTexture);
}
filterTexture.put(texture, sx, height - (sy + sh), sw, height - sy);
@@ -5947,31 +6774,31 @@ public void copy(PImage src,
int scrX0, scrX1;
int scrY0, scrY1;
if (invX) {
- scrX0 = dx + dw;
- scrX1 = dx;
+ scrX0 = (dx + dw) / src.pixelDensity;
+ scrX1 = dx / src.pixelDensity;
} else {
- scrX0 = dx;
- scrX1 = dx + dw;
+ scrX0 = dx / src.pixelDensity;
+ scrX1 = (dx + dw) / src.pixelDensity;
}
int texX0 = sx;
int texX1 = sx + sw;
int texY0, texY1;
if (invY) {
- scrY0 = height - (dy + dh);
- scrY1 = height - dy;
+ scrY0 = height - (dy + dh) / src.pixelDensity;
+ scrY1 = height - dy / src.pixelDensity;
texY0 = tex.height - (sy + sh);
texY1 = tex.height - sy;
} else {
// Because drawTexture uses bottom-to-top orientation of Y axis.
- scrY0 = height - dy;
- scrY1 = height - (dy + dh);
+ scrY0 = height - dy / src.pixelDensity;
+ scrY1 = height - (dy + dh) / src.pixelDensity;
texY0 = sy;
texY1 = sy + sh;
}
- pgl.drawTexture(tex.glTarget, tex.glName,
- tex.glWidth, tex.glHeight, width, height,
+ pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight,
+ 0, 0, width, height,
texX0, texY0, texX1, texY1,
scrX0, scrY0, scrX1, scrY1);
@@ -6145,7 +6972,7 @@ public Texture getTexture(PImage img) {
Texture tex = (Texture)initCache(img);
if (tex == null) return null;
- if (img.isModified() || img.isLoaded()) {
+ if (img.isModified()) {
if (img.width != tex.width || img.height != tex.height) {
tex.init(img.width, img.height);
}
@@ -6189,9 +7016,16 @@ protected Object initCache(PImage img) {
if (tex == null || tex.contextIsOutdated()) {
tex = addTexture(img);
if (tex != null) {
+ boolean dispose = img.pixels == null;
img.loadPixels();
tex.set(img.pixels, img.format);
- img.setLoaded(false);
+ img.setModified();
+ if (dispose) {
+ // We only used the pixels to load the image into the texture and the user did not request
+ // to load the pixels, so we should dispose the pixels array to avoid wasting memory
+ img.pixels = null;
+ img.loaded = false;
+ }
}
}
return tex;
@@ -6199,17 +7033,19 @@ protected Object initCache(PImage img) {
protected void bindFrontTexture() {
- if (primarySurface) {
+ if (primaryGraphics) {
pgl.bindFrontTexture();
} else {
- if (ptexture == null) createPTexture();
+ if (ptexture == null) {
+ createPTexture();
+ }
ptexture.bind();
}
}
protected void unbindFrontTexture() {
- if (primarySurface) {
+ if (primaryGraphics) {
pgl.unbindFrontTexture();
} else {
ptexture.unbind();
@@ -6238,7 +7074,8 @@ protected Texture addTexture(PImage img, Texture.Parameters params) {
if (img.parent == null) {
img.parent = parent;
}
- Texture tex = new Texture(this, img.width, img.height, params);
+ Texture tex = new Texture(this, img.pixelWidth, img.pixelHeight, params);
+ tex.invertedY(cameraUp); // Pixels are read upside down if camera us pointing up
setCache(img, tex);
return tex;
}
@@ -6246,7 +7083,8 @@ protected Texture addTexture(PImage img, Texture.Parameters params) {
protected void checkTexture(Texture tex) {
if (!tex.colorBuffer() &&
- tex.usingMipmaps == hints[DISABLE_TEXTURE_MIPMAPS]) {
+ (tex.usingMipmaps == hints[DISABLE_TEXTURE_MIPMAPS] ||
+ tex.currentSampling() != textureSampling)) {
if (hints[DISABLE_TEXTURE_MIPMAPS]) {
tex.usingMipmaps(false, textureSampling);
} else {
@@ -6286,12 +7124,9 @@ protected void updateTexture(PImage img, Texture tex) {
int w = img.getModifiedX2() - x;
int h = img.getModifiedY2() - y;
tex.set(img.pixels, x, y, w, h, img.format);
- } else if (img.isLoaded()) {
- tex.set(img.pixels, 0, 0, img.width, img.height, img.format);
}
}
img.setModified(false);
- img.setLoaded(false);
}
@@ -6311,12 +7146,13 @@ protected void deleteSurfaceTextures() {
protected boolean checkGLThread() {
- if (pgl.threadIsCurrent()) {
- return true;
- } else {
- PGraphics.showWarning(OPENGL_THREAD_ERROR);
- return false;
- }
+// if (pgl.threadIsCurrent()) {
+// return true;
+// } else {
+// PGraphics.showWarning(OPENGL_THREAD_ERROR);
+// return false;
+// }
+ return true;
}
@@ -6337,24 +7173,28 @@ public void resize(int wide, int high) {
protected void initPrimary() {
- pgl.initSurface(quality);
+ if (initialized) return;
+ pgl.initSurface(smooth);
if (texture != null) {
removeCache(this);
- texture = ptexture = null;
+ texture = null;
+ ptexture = null;
}
initialized = true;
}
protected void beginOnscreenDraw() {
- pgl.beginDraw(clearColorBuffer);
+ updatePixelSize();
+ restoreSurface();
+ pgl.beginRender();
if (drawFramebuffer == null) {
- drawFramebuffer = new FrameBuffer(this, width, height, true);
+ drawFramebuffer = new FrameBuffer(this, pixelWidth, pixelHeight, true);
}
drawFramebuffer.setFBO(pgl.getDrawFramebuffer());
if (readFramebuffer == null) {
- readFramebuffer = new FrameBuffer(this, width, height, true);
+ readFramebuffer = new FrameBuffer(this, pixelWidth, pixelHeight, true);
}
readFramebuffer.setFBO(pgl.getReadFramebuffer());
if (currentFramebuffer == null) {
@@ -6369,7 +7209,7 @@ protected void beginOnscreenDraw() {
protected void endOnscreenDraw() {
- pgl.endDraw(clearColorBuffer0);
+ pgl.endRender(parent.sketchWindowColor());
}
@@ -6377,43 +7217,49 @@ protected void initOffscreen() {
// Getting the context and capabilities from the main renderer.
loadTextureImpl(textureSampling, false);
- // In case of reinitialization (for example, when the smooth level
+ FrameBuffer ofb = offscreenFramebuffer;
+ FrameBuffer mfb = multisampleFramebuffer;
+
+ // In case of re-initialization (for example, when the smooth level
// is changed), we make sure that all the OpenGL resources associated
// to the surface are released by calling delete().
- if (offscreenFramebuffer != null) {
- offscreenFramebuffer.dispose();
+ if (ofb != null) {
+ ofb.dispose();
+ ofb = null;
}
- if (multisampleFramebuffer != null) {
- multisampleFramebuffer.dispose();
+ if (mfb != null) {
+ mfb.dispose();
+ mfb = null;
}
boolean packed = depthBits == 24 && stencilBits == 8 &&
packedDepthStencilSupported;
- if (PGraphicsOpenGL.fboMultisampleSupported && 1 < quality) {
- multisampleFramebuffer =
- new FrameBuffer(this, texture.glWidth, texture.glHeight, quality, 0,
- depthBits, stencilBits, packed, false);
-
- multisampleFramebuffer.clear();
+ if (PGraphicsOpenGL.fboMultisampleSupported && 1 < PGL.smoothToSamples(smooth)) {
+ mfb = new FrameBuffer(this, texture.glWidth, texture.glHeight, PGL.smoothToSamples(smooth), 0,
+ depthBits, stencilBits, packed, false);
+ mfb.clear();
+ multisampleFramebuffer = mfb;
offscreenMultisample = true;
// The offscreen framebuffer where the multisampled image is finally drawn
- // to doesn't need depth and stencil buffers since they are part of the
- // multisampled framebuffer.
- offscreenFramebuffer =
- new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, 0, 0,
- false, false);
-
+ // to. If depth reading is disabled it doesn't need depth and stencil buffers
+ // since they are part of the multisampled framebuffer.
+ if (hints[ENABLE_BUFFER_READING]) {
+ ofb = new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1,
+ depthBits, stencilBits, packed, false);
+ } else {
+ ofb = new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1,
+ 0, 0, false, false);
+ }
} else {
- quality = 0;
- offscreenFramebuffer =
- new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1,
- depthBits, stencilBits, packed, false);
+ smooth = 0;
+ ofb = new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1,
+ depthBits, stencilBits, packed, false);
offscreenMultisample = false;
}
-
- offscreenFramebuffer.setColorBuffer(texture);
- offscreenFramebuffer.clear();
+ ofb.setColorBuffer(texture);
+ ofb.clear();
+ offscreenFramebuffer = ofb;
initialized = true;
}
@@ -6423,10 +7269,10 @@ protected void beginOffscreenDraw() {
if (!initialized) {
initOffscreen();
} else {
- boolean outdated = offscreenFramebuffer != null &&
- offscreenFramebuffer.contextIsOutdated();
- boolean outdatedMulti = multisampleFramebuffer != null &&
- multisampleFramebuffer.contextIsOutdated();
+ FrameBuffer ofb = offscreenFramebuffer;
+ FrameBuffer mfb = multisampleFramebuffer;
+ boolean outdated = ofb != null && ofb.contextIsOutdated();
+ boolean outdatedMulti = mfb != null && mfb.contextIsOutdated();
if (outdated || outdatedMulti) {
restartPGL();
initOffscreen();
@@ -6440,9 +7286,15 @@ protected void beginOffscreenDraw() {
pushFramebuffer();
if (offscreenMultisample) {
- setFramebuffer(multisampleFramebuffer);
+ FrameBuffer mfb = multisampleFramebuffer;
+ if (mfb != null) {
+ setFramebuffer(mfb);
+ }
} else {
- setFramebuffer(offscreenFramebuffer);
+ FrameBuffer ofb = offscreenFramebuffer;
+ if (ofb != null) {
+ setFramebuffer(ofb);
+ }
}
// Render previous back texture (now is the front) as background
@@ -6460,7 +7312,11 @@ protected void beginOffscreenDraw() {
protected void endOffscreenDraw() {
if (offscreenMultisample) {
- multisampleFramebuffer.copyColor(offscreenFramebuffer);
+ FrameBuffer ofb = offscreenFramebuffer;
+ FrameBuffer mfb = multisampleFramebuffer;
+ if (ofb != null && mfb != null) {
+ mfb.copyColor(ofb);
+ }
}
popFramebuffer();
@@ -6475,7 +7331,9 @@ protected void endOffscreenDraw() {
pgl.colorMask(true, true, true, true);
}
- texture.updateTexels(); // Mark all texels in screen texture as modified.
+ if (texture != null) {
+ texture.updateTexels(); // Mark all texels in screen texture as modified.
+ }
getPrimaryPG().restoreGL();
}
@@ -6489,7 +7347,14 @@ protected void setViewport() {
}
- protected void setDrawDefaults() {
+ @Override
+ protected void checkSettings() {
+ super.checkSettings();
+ setGLSettings();
+ }
+
+
+ protected void setGLSettings() {
inGeo.clear();
tessGeo.clear();
texCache.clear();
@@ -6515,26 +7380,33 @@ protected void setDrawDefaults() {
flushMode = FLUSH_WHEN_FULL;
}
- if (primarySurface) {
- pgl.getIntegerv(PGL.SAMPLES, intBuffer);
- int temp = intBuffer.get(0);
- if (quality != temp && 1 < temp && 1 < quality) {
- quality = temp;
- }
+ if (primaryGraphics) {
+// pgl.getIntegerv(PGL.SAMPLES, intBuffer);
+// int temp = intBuffer.get(0);
+// if (smooth != temp && 1 < temp && 1 < smooth) {
+ // TODO check why the samples is higher that initialized smooth level.
+// quality = temp;
+// }
}
- if (quality < 2) {
+ if (smooth < 1) {
pgl.disable(PGL.MULTISAMPLE);
} else {
- pgl.enable(PGL.MULTISAMPLE);
+ // work around runtime exceptions in Broadcom's VC IV driver
+ if (false == OPENGL_RENDERER.equals("VideoCore IV HW")) {
+ pgl.enable(PGL.MULTISAMPLE);
+ }
+ }
+ // work around runtime exceptions in Broadcom's VC IV driver
+ if (false == OPENGL_RENDERER.equals("VideoCore IV HW")) {
+ pgl.disable(PGL.POLYGON_SMOOTH);
}
- pgl.disable(PGL.POLYGON_SMOOTH);
- if (sized) {
+ if (sized || parent.frameCount == 0) {
// reapplySettings();
// To avoid having garbage in the screen after a resize,
// in the case background is not called in draw().
- if (primarySurface) {
+ if (primaryGraphics) {
background(backgroundColor);
} else {
// offscreen surfaces are transparent by default.
@@ -6564,13 +7436,11 @@ protected void setDrawDefaults() {
lightSpecular(0, 0, 0);
}
- // Vertices should be specified by user in CW order (left-handed)
- // That is CCW order (right-handed). Vertex shader inverts
- // Y-axis and outputs vertices in CW order (right-handed).
- // Culling occurs after the vertex shader, so FRONT FACE
- // has to be set to CW (right-handed) for OpenGL to correctly
- // recognize FRONT and BACK faces.
- pgl.frontFace(PGL.CW);
+ // The GL coordinate system is right-handed, so that facing
+ // polygons are CCW in window coordinates, whereas are CW
+ // in the left-handed system that is Processing's default (with
+ // its Y axis pointing down)
+ pgl.frontFace(cameraUp ? PGL.CCW : PGL.CW);
pgl.disable(PGL.CULL_FACE);
// Processing uses only one texture unit.
@@ -6580,20 +7450,7 @@ protected void setDrawDefaults() {
normalX = normalY = 0;
normalZ = 1;
- // Clear depth and stencil buffers.
- pgl.depthMask(true);
- pgl.clearDepth(1);
- pgl.clearStencil(0);
- pgl.clear(PGL.DEPTH_BUFFER_BIT | PGL.STENCIL_BUFFER_BIT);
-
- if (!settingsInited) {
- defaultSettings();
- }
-
- if (restoreSurface) {
- restoreSurfaceFromPixels();
- restoreSurface = false;
- }
+ pgl.clearDepthStencil();
if (hints[DISABLE_DEPTH_MASK]) {
pgl.depthMask(false);
@@ -6603,11 +7460,8 @@ protected void setDrawDefaults() {
pixelsOp = OP_NONE;
- clearColorBuffer0 = clearColorBuffer;
- clearColorBuffer = false;
-
modified = false;
- arePixelsUpToDate = false;
+ loaded = false;
}
@@ -6623,6 +7477,8 @@ protected void getGLParameters() {
fboMultisampleSupported = pgl.hasFboMultisampleSupport();
packedDepthStencilSupported = pgl.hasPackedDepthStencilSupport();
anisoSamplingSupported = pgl.hasAnisoSamplingSupport();
+ readBufferSupported = pgl.hasReadBuffer();
+ drawBufferSupported = pgl.hasDrawBuffer();
try {
pgl.blendEquation(PGL.FUNC_ADD);
@@ -6637,14 +7493,27 @@ protected void getGLParameters() {
pgl.getIntegerv(PGL.MAX_TEXTURE_SIZE, intBuffer);
maxTextureSize = intBuffer.get(0);
- pgl.getIntegerv(PGL.MAX_SAMPLES, intBuffer);
- maxSamples = intBuffer.get(0);
+ // work around runtime exceptions in Broadcom's VC IV driver
+ if (false == OPENGL_RENDERER.equals("VideoCore IV HW")) {
+ pgl.getIntegerv(PGL.MAX_SAMPLES, intBuffer);
+ maxSamples = intBuffer.get(0);
+ }
if (anisoSamplingSupported) {
pgl.getFloatv(PGL.MAX_TEXTURE_MAX_ANISOTROPY, floatBuffer);
maxAnisoAmount = floatBuffer.get(0);
}
+ // overwrite the default shaders with vendor specific versions
+ // if needed
+ if (OPENGL_RENDERER.equals("VideoCore IV HW") || // Broadcom's binary driver for Raspberry Pi
+ OPENGL_RENDERER.equals("Gallium 0.4 on VC4")) { // Mesa driver for same hardware
+ defLightShaderVertURL =
+ PGraphicsOpenGL.class.getResource("/assets/shaders/LightVert-vc4.glsl");
+ defTexlightShaderVertURL =
+ PGraphicsOpenGL.class.getResource("/assets/shaders/TexLightVert-vc4.glsl");
+ }
+
glParamsRead = true;
}
@@ -6667,25 +7536,25 @@ public PShader loadShader(String fragFilename) {
shader.setType(type);
shader.setFragmentShader(fragFilename);
if (type == PShader.POINT) {
- String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL);
shader.setVertexShader(vertSource);
} else if (type == PShader.LINE) {
- String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL);
shader.setVertexShader(vertSource);
} else if (type == PShader.TEXLIGHT) {
- String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL);
shader.setVertexShader(vertSource);
} else if (type == PShader.LIGHT) {
- String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL);
shader.setVertexShader(vertSource);
} else if (type == PShader.TEXTURE) {
- String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL);
shader.setVertexShader(vertSource);
} else if (type == PShader.COLOR) {
- String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL);
shader.setVertexShader(vertSource);
} else {
- String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL);
shader.setVertexShader(vertSource);
}
return shader;
@@ -6694,15 +7563,15 @@ public PShader loadShader(String fragFilename) {
@Override
public PShader loadShader(String fragFilename, String vertFilename) {
+ PShader shader = null;
if (fragFilename == null || fragFilename.equals("")) {
PGraphics.showWarning(MISSING_FRAGMENT_SHADER);
- return null;
- } else if (fragFilename == null || fragFilename.equals("")) {
+ } else if (vertFilename == null || vertFilename.equals("")) {
PGraphics.showWarning(MISSING_VERTEX_SHADER);
- return null;
} else {
- return new PShader(parent, vertFilename, fragFilename);
+ shader = new PShader(parent, vertFilename, fragFilename);
}
+ return shader;
}
@@ -6710,6 +7579,7 @@ public PShader loadShader(String fragFilename, String vertFilename) {
public void shader(PShader shader) {
flush(); // Flushing geometry drawn with a different shader.
+ if (shader != null) shader.init();
if (shader.isPolyShader()) polyShader = shader;
else if (shader.isLineShader()) lineShader = shader;
else if (shader.isPointShader()) pointShader = shader;
@@ -6721,6 +7591,7 @@ public void shader(PShader shader) {
public void shader(PShader shader, int kind) {
flush(); // Flushing geometry drawn with a different shader.
+ if (shader != null) shader.init();
if (kind == TRIANGLES) polyShader = shader;
else if (kind == LINES) lineShader = shader;
else if (kind == POINTS) pointShader = shader;
@@ -6750,47 +7621,32 @@ public void resetShader(int kind) {
}
- protected void deleteDefaultShaders() {
- // The default shaders contains references to the PGraphics object that
- // creates them, so when restarting the renderer, those references should
- // dissapear.
- defColorShader = null;
- defTextureShader = null;
- defLightShader = null;
- defTexlightShader = null;
- defLineShader = null;
- defPointShader = null;
- maskShader = null;
- }
-
-
protected PShader getPolyShader(boolean lit, boolean tex) {
PShader shader;
PGraphicsOpenGL ppg = getPrimaryPG();
boolean useDefault = polyShader == null;
if (polyShader != null) {
- polyShader.setRenderer(this);
- polyShader.loadAttributes();
- polyShader.loadUniforms();
+ updateShader(polyShader);
+// polyShader.setRenderer(this);
+// polyShader.loadAttributes();
+// polyShader.loadUniforms();
}
if (lit) {
if (tex) {
- if (useDefault || !polyShader.checkPolyType(PShader.TEXLIGHT)) {
+ if (useDefault || !isPolyShaderTexLight(polyShader)) {
if (ppg.defTexlightShader == null) {
- String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL, 120);
- String[] fragSource = pgl.loadFragmentShader(defTexlightShaderFragURL, 120);
- ppg.defTexlightShader = new PShader(parent, vertSource, fragSource);
+ ppg.defTexlightShader = loadShaderFromURL(defTexlightShaderFragURL,
+ defTexlightShaderVertURL);
}
shader = ppg.defTexlightShader;
} else {
shader = polyShader;
}
} else {
- if (useDefault || !polyShader.checkPolyType(PShader.LIGHT)) {
+ if (useDefault || !isPolyShaderLight(polyShader)) {
if (ppg.defLightShader == null) {
- String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL, 120);
- String[] fragSource = pgl.loadFragmentShader(defLightShaderFragURL, 120);
- ppg.defLightShader = new PShader(parent, vertSource, fragSource);
+ ppg.defLightShader = loadShaderFromURL(defLightShaderFragURL,
+ defLightShaderVertURL);
}
shader = ppg.defLightShader;
} else {
@@ -6798,41 +7654,77 @@ protected PShader getPolyShader(boolean lit, boolean tex) {
}
}
} else {
- if (polyShader != null && polyShader.accessLightAttribs()) {
+ if (isPolyShaderUsingLights(polyShader)) {
PGraphics.showWarning(SHADER_NEED_LIGHT_ATTRIBS);
useDefault = true;
}
- if (tex) {
- if (useDefault || !polyShader.checkPolyType(PShader.TEXTURE)) {
- if (ppg.defTextureShader == null) {
- String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120);
- String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120);
- ppg.defTextureShader = new PShader(parent, vertSource, fragSource);
- }
- shader = ppg.defTextureShader;
- } else {
- shader = polyShader;
- }
- } else {
- if (useDefault || !polyShader.checkPolyType(PShader.COLOR)) {
- if (ppg.defColorShader == null) {
- String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL, 120);
- String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120);
- ppg.defColorShader = new PShader(parent, vertSource, fragSource);
- }
- shader = ppg.defColorShader;
- } else {
- shader = polyShader;
- }
- }
- }
- if (shader != polyShader) {
- shader.setRenderer(this);
- shader.loadAttributes();
- shader.loadUniforms();
- }
- return shader;
+ if (tex) {
+ if (useDefault || !isPolyShaderTex(polyShader)) {
+ if (ppg.defTextureShader == null) {
+ ppg.defTextureShader = loadShaderFromURL(defTextureShaderFragURL,
+ defTextureShaderVertURL);
+ }
+ shader = ppg.defTextureShader;
+ } else {
+ shader = polyShader;
+ }
+ } else {
+ if (useDefault || !isPolyShaderColor(polyShader)) {
+ if (ppg.defColorShader == null) {
+ ppg.defColorShader = loadShaderFromURL(defColorShaderFragURL,
+ defColorShaderVertURL);
+ }
+ shader = ppg.defColorShader;
+ } else {
+ shader = polyShader;
+ }
+ }
+ }
+ if (shader != polyShader) {
+ updateShader(shader);
+ }
+ updateShader(shader);
+ return shader;
+ }
+
+
+ protected void updateShader(PShader shader) {
+ shader.setRenderer(this);
+ shader.loadAttributes();
+ shader.loadUniforms();
+ }
+
+
+ protected PShader loadShaderFromURL(URL fragURL, URL vertURL) {
+ String[] vertSource = pgl.loadVertexShader(vertURL);
+ String[] fragSource = pgl.loadFragmentShader(fragURL);
+ return new PShader(parent, vertSource, fragSource);
+ }
+
+
+ protected boolean isPolyShaderTexLight(PShader shader) {
+ return shader.checkPolyType(PShader.TEXLIGHT);
+ }
+
+
+ protected boolean isPolyShaderLight(PShader shader) {
+ return shader.checkPolyType(PShader.LIGHT);
+ }
+
+
+ protected boolean isPolyShaderTex(PShader shader) {
+ return shader.checkPolyType(PShader.TEXTURE);
+ }
+
+
+ protected boolean isPolyShaderColor(PShader shader) {
+ return shader.checkPolyType(PShader.COLOR);
+ }
+
+
+ protected boolean isPolyShaderUsingLights(PShader shader) {
+ return shader != null && shader.accessLightAttribs();
}
@@ -6841,8 +7733,8 @@ protected PShader getLineShader() {
PGraphicsOpenGL ppg = getPrimaryPG();
if (lineShader == null) {
if (ppg.defLineShader == null) {
- String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL, 120);
- String[] fragSource = pgl.loadFragmentShader(defLineShaderFragURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL);
+ String[] fragSource = pgl.loadFragmentShader(defLineShaderFragURL);
ppg.defLineShader = new PShader(parent, vertSource, fragSource);
}
shader = ppg.defLineShader;
@@ -6861,8 +7753,8 @@ protected PShader getPointShader() {
PGraphicsOpenGL ppg = getPrimaryPG();
if (pointShader == null) {
if (ppg.defPointShader == null) {
- String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL, 120);
- String[] fragSource = pgl.loadFragmentShader(defPointShaderFragURL, 120);
+ String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL);
+ String[] fragSource = pgl.loadFragmentShader(defPointShaderFragURL);
ppg.defPointShader = new PShader(parent, vertSource, fragSource);
}
shader = ppg.defPointShader;
@@ -6888,18 +7780,221 @@ static protected int expandArraySize(int currSize, int newMinSize) {
return newSize;
}
+ //////////////////////////////////////////////////////////////
+
+ // Generic vertex attributes.
+
+
+ static protected AttributeMap newAttributeMap() {
+ return new AttributeMap();
+ }
+
+
+ static protected class AttributeMap extends HashMap {
+ public ArrayList names = new ArrayList();
+ public int numComp = 0; // number of components for a single vertex
+
+ @Override
+ public VertexAttribute put(String key, VertexAttribute value) {
+ VertexAttribute prev = super.put(key, value);
+ names.add(key);
+ if (value.kind == VertexAttribute.COLOR) numComp += 4;
+ else numComp += value.size;
+ return prev;
+ }
+
+ public VertexAttribute get(int i) {
+ return super.get(names.get(i));
+ }
+ }
+
+
+ static protected class VertexAttribute {
+ static final int POSITION = 0;
+ static final int NORMAL = 1;
+ static final int COLOR = 2;
+ static final int OTHER = 3;
+
+ PGraphicsOpenGL pg;
+ String name;
+ int kind; // POSITION, NORMAL, COLOR, OTHER
+ int type; // GL_INT, GL_FLOAT, GL_BOOL
+ int size; // number of elements (1, 2, 3, or 4)
+ int tessSize;
+ int elementSize;
+ VertexBuffer buf;
+ int glLoc;
+
+ float[] fvalues;
+ int[] ivalues;
+ byte[] bvalues;
+
+ // For use in PShape
+ boolean modified;
+ int firstModified;
+ int lastModified;
+ boolean active;
+
+ VertexAttribute(PGraphicsOpenGL pg, String name, int kind, int type, int size) {
+ this.pg = pg;
+ this.name = name;
+ this.kind = kind;
+ this.type = type;
+ this.size = size;
+
+ if (kind == POSITION) {
+ tessSize = 4; // for w
+ } else {
+ tessSize = size;
+ }
+
+ if (type == PGL.FLOAT) {
+ elementSize = PGL.SIZEOF_FLOAT;
+ fvalues = new float[size];
+ } else if (type == PGL.INT) {
+ elementSize = PGL.SIZEOF_INT;
+ ivalues = new int[size];
+ } else if (type == PGL.BOOL) {
+ elementSize = PGL.SIZEOF_INT;
+ bvalues = new byte[size];
+ }
+
+ buf = null;
+ glLoc = -1;
+
+ modified = false;
+ firstModified = PConstants.MAX_INT;
+ lastModified = PConstants.MIN_INT;
+
+ active = true;
+ }
+
+ public boolean diff(VertexAttribute attr) {
+ return !name.equals(attr.name) ||
+ kind != attr.kind ||
+ type != attr.type ||
+ size != attr.size ||
+ tessSize != attr.tessSize ||
+ elementSize != attr.elementSize;
+ }
+
+ boolean isPosition() {
+ return kind == POSITION;
+ }
+
+ boolean isNormal() {
+ return kind == NORMAL;
+ }
+
+ boolean isColor() {
+ return kind == COLOR;
+ }
+
+ boolean isOther() {
+ return kind == OTHER;
+ }
+
+ boolean isFloat() {
+ return type == PGL.FLOAT;
+ }
+
+ boolean isInt() {
+ return type == PGL.INT;
+ }
+
+ boolean isBool() {
+ return type == PGL.BOOL;
+ }
+
+ boolean bufferCreated() {
+ return buf != null && 0 < buf.glId;
+ }
+
+ void createBuffer(PGL pgl) {
+ buf = new VertexBuffer(pg, PGL.ARRAY_BUFFER, size, elementSize, false);
+ }
+
+ void deleteBuffer(PGL pgl) {
+ if (buf.glId != 0) {
+ intBuffer.put(0, buf.glId);
+ if (pgl.threadIsCurrent()) pgl.deleteBuffers(1, intBuffer);
+ }
+ }
+
+ void bind(PGL pgl) {
+ pgl.enableVertexAttribArray(glLoc);
+ }
+
+ void unbind(PGL pgl) {
+ pgl.disableVertexAttribArray(glLoc);
+ }
+
+ boolean active(PShader shader) {
+ if (active) {
+ if (glLoc == -1) {
+ glLoc = shader.getAttributeLoc(name);
+ if (glLoc == -1) active = false;
+ }
+ }
+ return active;
+ }
+
+ int sizeInBytes(int length) {
+ return length * tessSize * elementSize;
+ }
+
+ void set(float x, float y, float z) {
+ fvalues[0] = x;
+ fvalues[1] = y;
+ fvalues[2] = z;
+ }
+
+ void set(int c) {
+ ivalues[0] = c;
+ }
+
+ void set(float[] values) {
+ PApplet.arrayCopy(values, 0, fvalues, 0, size);
+ }
+
+ void set(int[] values) {
+ PApplet.arrayCopy(values, 0, ivalues, 0, size);
+ }
+
+ void set(boolean[] values) {
+ for (int i = 0; i < values.length; i++) {
+ bvalues[i] = (byte)(values[i] ? 1 : 0);
+ }
+ }
+
+ void add(float[] dstValues, int dstIdx) {
+ PApplet.arrayCopy(fvalues, 0, dstValues, dstIdx, size);
+ }
+
+ void add(int[] dstValues, int dstIdx) {
+ PApplet.arrayCopy(ivalues, 0, dstValues, dstIdx, size);
+ }
+
+ void add(byte[] dstValues, int dstIdx) {
+ PApplet.arrayCopy(bvalues, 0, dstValues, dstIdx, size);
+ }
+ }
+
+
//////////////////////////////////////////////////////////////
// Input (raw) and Tessellated geometry, tessellator.
- static protected InGeometry newInGeometry(PGraphicsOpenGL pg, int mode) {
- return new InGeometry(pg, mode);
+ static protected InGeometry newInGeometry(PGraphicsOpenGL pg, AttributeMap attr,
+ int mode) {
+ return new InGeometry(pg, attr, mode);
}
- static protected TessGeometry newTessGeometry(PGraphicsOpenGL pg, int mode) {
- return new TessGeometry(pg, mode);
+ static protected TessGeometry newTessGeometry(PGraphicsOpenGL pg,
+ AttributeMap attr, int mode) {
+ return new TessGeometry(pg, attr, mode);
}
@@ -7036,17 +8131,19 @@ static protected class IndexCache {
int[] indexOffset;
int[] vertexCount;
int[] vertexOffset;
+ int[] counter;
IndexCache() {
allocate();
}
void allocate() {
+ size = 0;
indexCount = new int[2];
indexOffset = new int[2];
vertexCount = new int[2];
vertexOffset = new int[2];
- size = 0;
+ counter = null;
}
void clear() {
@@ -7079,9 +8176,17 @@ int getLast() {
return size - 1;
}
+ void setCounter(int[] counter) {
+ this.counter = counter;
+ }
+
void incCounts(int index, int icount, int vcount) {
indexCount[index] += icount;
vertexCount[index] += vcount;
+ if (counter != null) {
+ counter[0] += icount;
+ counter[1] += vcount;
+ }
}
void init(int n) {
@@ -7138,6 +8243,7 @@ void expandVertexOffset(int n) {
static protected class InGeometry {
PGraphicsOpenGL pg;
int renderMode;
+ AttributeMap attribs;
int vertexCount;
int codeCount;
@@ -7162,6 +8268,11 @@ static protected class InGeometry {
int[] emissive;
float[] shininess;
+ // Generic attributes
+ HashMap fattribs;
+ HashMap iattribs;
+ HashMap battribs;
+
// Internally used by the addVertex() methods.
int fillColor;
int strokeColor;
@@ -7172,8 +8283,9 @@ static protected class InGeometry {
float shininessFactor;
float normalX, normalY, normalZ;
- InGeometry(PGraphicsOpenGL pg, int mode) {
+ InGeometry(PGraphicsOpenGL pg, AttributeMap attr, int mode) {
this.pg = pg;
+ this.attribs = attr;
renderMode = mode;
allocate();
}
@@ -7205,9 +8317,26 @@ void allocate() {
shininess = new float[PGL.DEFAULT_IN_VERTICES];
edges = new int[PGL.DEFAULT_IN_EDGES][3];
+ fattribs = new HashMap();
+ iattribs = new HashMap();
+ battribs = new HashMap();
+
clear();
}
+ void initAttrib(VertexAttribute attrib) {
+ if (attrib.type == PGL.FLOAT) {
+ float[] temp = new float[attrib.size * PGL.DEFAULT_IN_VERTICES];
+ fattribs.put(attrib.name, temp);
+ } else if (attrib.type == PGL.INT) {
+ int[] temp = new int[attrib.size * PGL.DEFAULT_IN_VERTICES];
+ iattribs.put(attrib.name, temp);
+ } else if (attrib.type == PGL.BOOL) {
+ byte[] temp = new byte[attrib.size * PGL.DEFAULT_IN_VERTICES];
+ battribs.put(attrib.name, temp);
+ }
+ }
+
void vertexCheck() {
if (vertexCount == vertices.length / 3) {
int newSize = vertexCount << 1;
@@ -7222,6 +8351,7 @@ void vertexCheck() {
expandSpecular(newSize);
expandEmissive(newSize);
expandShininess(newSize);
+ expandAttribs(newSize);
}
}
@@ -7283,8 +8413,11 @@ int getNumEdgeVertices(boolean bevel) {
if (bevel) {
for (int i = 0; i < edgeCount; i++) {
int[] edge = edges[i];
- if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) bevVert++;
- if (edge[2] == EDGE_CLOSE) segVert--;
+ if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) bevVert += 3;
+ if (edge[2] == EDGE_CLOSE) {
+ bevVert += 5;
+ segVert--;
+ }
}
} else {
segVert -= getNumEdgeClosures();
@@ -7299,7 +8432,10 @@ int getNumEdgeIndices(boolean bevel) {
for (int i = 0; i < edgeCount; i++) {
int[] edge = edges[i];
if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) bevInd++;
- if (edge[2] == EDGE_CLOSE) segInd--;
+ if (edge[2] == EDGE_CLOSE) {
+ bevInd++;
+ segInd--;
+ }
}
} else {
segInd -= getNumEdgeClosures();
@@ -7338,6 +8474,42 @@ int getVertexSum(PVector v) {
return vertexCount;
}
+ double[] getAttribVector(int idx) {
+ double[] vector = new double[attribs.numComp];
+ int vidx = 0;
+ for (int i = 0; i < attribs.size(); i++) {
+ VertexAttribute attrib = attribs.get(i);
+ String name = attrib.name;
+ int aidx = attrib.size * idx;
+ if (attrib.isColor()) {
+ int[] iarray = iattribs.get(name);
+ int col = iarray[aidx];
+ vector[vidx++] = (col >> 24) & 0xFF;
+ vector[vidx++] = (col >> 16) & 0xFF;
+ vector[vidx++] = (col >> 8) & 0xFF;
+ vector[vidx++] = (col >> 0) & 0xFF;
+ } else {
+ if (attrib.isFloat()) {
+ float[] farray = fattribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ vector[vidx++] = farray[aidx++];
+ }
+ } else if (attrib.isInt()) {
+ int[] iarray = iattribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ vector[vidx++] = iarray[aidx++];
+ }
+ } else if (attrib.isBool()) {
+ byte[] barray = battribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ vector[vidx++] = barray[aidx++];
+ }
+ }
+ }
+ }
+ return vector;
+ }
+
// -----------------------------------------------------------------
//
// Expand arrays
@@ -7402,6 +8574,40 @@ void expandShininess(int n) {
shininess = temp;
}
+ void expandAttribs(int n) {
+ for (String name: attribs.keySet()) {
+ VertexAttribute attrib = attribs.get(name);
+ if (attrib.type == PGL.FLOAT) {
+ expandFloatAttrib(attrib, n);
+ } else if (attrib.type == PGL.INT) {
+ expandIntAttrib(attrib, n);
+ } else if (attrib.type == PGL.BOOL) {
+ expandBoolAttrib(attrib, n);
+ }
+ }
+ }
+
+ void expandFloatAttrib(VertexAttribute attrib, int n) {
+ float[] values = fattribs.get(attrib.name);
+ float temp[] = new float[attrib.size * n];
+ PApplet.arrayCopy(values, 0, temp, 0, attrib.size * vertexCount);
+ fattribs.put(attrib.name, temp);
+ }
+
+ void expandIntAttrib(VertexAttribute attrib, int n) {
+ int[] values = iattribs.get(attrib.name);
+ int temp[] = new int[attrib.size * n];
+ PApplet.arrayCopy(values, 0, temp, 0, attrib.size * vertexCount);
+ iattribs.put(attrib.name, temp);
+ }
+
+ void expandBoolAttrib(VertexAttribute attrib, int n) {
+ byte[] values = battribs.get(attrib.name);
+ byte temp[] = new byte[attrib.size * n];
+ PApplet.arrayCopy(values, 0, temp, 0, attrib.size * vertexCount);
+ battribs.put(attrib.name, temp);
+ }
+
void expandCodes(int n) {
int temp[] = new int[n];
PApplet.arrayCopy(codes, 0, temp, 0, codeCount);
@@ -7430,6 +8636,7 @@ void trim() {
trimSpecular();
trimEmissive();
trimShininess();
+ trimAttribs();
}
if (0 < codeCount && codeCount < codes.length) {
@@ -7513,12 +8720,52 @@ void trimEdges() {
edges = temp;
}
+ void trimAttribs() {
+ for (String name: attribs.keySet()) {
+ VertexAttribute attrib = attribs.get(name);
+ if (attrib.type == PGL.FLOAT) {
+ trimFloatAttrib(attrib);
+ } else if (attrib.type == PGL.INT) {
+ trimIntAttrib(attrib);
+ } else if (attrib.type == PGL.BOOL) {
+ trimBoolAttrib(attrib);
+ }
+ }
+ }
+
+ void trimFloatAttrib(VertexAttribute attrib) {
+ float[] values = fattribs.get(attrib.name);
+ float temp[] = new float[attrib.size * vertexCount];
+ PApplet.arrayCopy(values, 0, temp, 0, attrib.size * vertexCount);
+ fattribs.put(attrib.name, temp);
+ }
+
+ void trimIntAttrib(VertexAttribute attrib) {
+ int[] values = iattribs.get(attrib.name);
+ int temp[] = new int[attrib.size * vertexCount];
+ PApplet.arrayCopy(values, 0, temp, 0, attrib.size * vertexCount);
+ iattribs.put(attrib.name, temp);
+ }
+
+ void trimBoolAttrib(VertexAttribute attrib) {
+ byte[] values = battribs.get(attrib.name);
+ byte temp[] = new byte[attrib.size * vertexCount];
+ PApplet.arrayCopy(values, 0, temp, 0, attrib.size * vertexCount);
+ battribs.put(attrib.name, temp);
+ }
+
// -----------------------------------------------------------------
//
// Vertices
int addVertex(float x, float y, boolean brk) {
- return addVertex(x, y, VERTEX, brk);
+ return addVertex(x, y, 0,
+ fillColor,
+ normalX, normalY, normalZ,
+ 0, 0,
+ strokeColor, strokeWeight,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
+ VERTEX, brk);
}
int addVertex(float x, float y,
@@ -7528,15 +8775,20 @@ int addVertex(float x, float y,
normalX, normalY, normalZ,
0, 0,
strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor,
- shininessFactor,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
code, brk);
}
int addVertex(float x, float y,
float u, float v,
boolean brk) {
- return addVertex(x, y, u, v, VERTEX, brk);
+ return addVertex(x, y, 0,
+ fillColor,
+ normalX, normalY, normalZ,
+ u, v,
+ strokeColor, strokeWeight,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
+ VERTEX, brk);
}
int addVertex(float x, float y,
@@ -7547,31 +8799,40 @@ int addVertex(float x, float y,
normalX, normalY, normalZ,
u, v,
strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor,
- shininessFactor,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
code, brk);
}
int addVertex(float x, float y, float z, boolean brk) {
- return addVertex(x, y, z, VERTEX, brk);
+ return addVertex(x, y, z,
+ fillColor,
+ normalX, normalY, normalZ,
+ 0, 0,
+ strokeColor, strokeWeight,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
+ VERTEX, brk);
}
- int addVertex(float x, float y, float z,
- int code, boolean brk) {
+ int addVertex(float x, float y, float z, int code, boolean brk) {
return addVertex(x, y, z,
fillColor,
normalX, normalY, normalZ,
0, 0,
strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor,
- shininessFactor,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
code, brk);
}
int addVertex(float x, float y, float z,
float u, float v,
boolean brk) {
- return addVertex(x, y, z, u, v, VERTEX, brk);
+ return addVertex(x, y, z,
+ fillColor,
+ normalX, normalY, normalZ,
+ u, v,
+ strokeColor, strokeWeight,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
+ VERTEX, brk);
}
int addVertex(float x, float y, float z,
@@ -7582,8 +8843,7 @@ int addVertex(float x, float y, float z,
normalX, normalY, normalZ,
u, v,
strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor,
- shininessFactor,
+ ambientColor, specularColor, emissiveColor, shininessFactor,
code, brk);
}
@@ -7621,6 +8881,21 @@ int addVertex(float x, float y, float z,
emissive[vertexCount] = PGL.javaToNativeARGB(em);
shininess[vertexCount] = shine;
+ for (String name: attribs.keySet()) {
+ VertexAttribute attrib = attribs.get(name);
+ index = attrib.size * vertexCount;
+ if (attrib.type == PGL.FLOAT) {
+ float[] values = fattribs.get(name);
+ attrib.add(values, index);
+ } else if (attrib.type == PGL.INT) {
+ int[] values = iattribs.get(name);
+ attrib.add(values, index);
+ } else if (attrib.type == PGL.BOOL) {
+ byte[] values = battribs.get(name);
+ attrib.add(values, index);
+ }
+ }
+
if (brk || (code == VERTEX && codes != null) ||
code == BEZIER_VERTEX ||
code == QUADRATIC_VERTEX ||
@@ -7651,14 +8926,16 @@ int addVertex(float x, float y, float z,
public void addBezierVertex(float x2, float y2, float z2,
float x3, float y3, float z3,
- float x4, float y4, float z4, boolean brk) {
+ float x4, float y4, float z4,
+ boolean brk) {
addVertex(x2, y2, z2, BEZIER_VERTEX, brk);
addVertex(x3, y3, z3, -1, false);
addVertex(x4, y4, z4, -1, false);
}
public void addQuadraticVertex(float cx, float cy, float cz,
- float x3, float y3, float z3, boolean brk) {
+ float x3, float y3, float z3,
+ boolean brk) {
addVertex(cx, cy, cz, QUADRATIC_VERTEX, brk);
addVertex(x3, y3, z3, -1, false);
}
@@ -7763,9 +9040,9 @@ void addTrianglesEdges() {
int i1 = 3 * i + 1;
int i2 = 3 * i + 2;
- addEdge(i0, i1, true, false);
+ addEdge(i0, i1, true, false);
addEdge(i1, i2, false, false);
- addEdge(i2, i0, false, false);
+ addEdge(i2, i0, false, false);
closeEdge(i2, i0);
}
}
@@ -7776,9 +9053,9 @@ void addTriangleFanEdges() {
int i1 = i;
int i2 = i + 1;
- addEdge(i0, i1, true, false);
+ addEdge(i0, i1, true, false);
addEdge(i1, i2, false, false);
- addEdge(i2, i0, false, false);
+ addEdge(i2, i0, false, false);
closeEdge(i2, i0);
}
}
@@ -7795,9 +9072,9 @@ void addTriangleStripEdges() {
i2 = i - 1;
}
- addEdge(i0, i1, true, false);
+ addEdge(i0, i1, true, false);
addEdge(i1, i2, false, false);
- addEdge(i2, i0, false, false);
+ addEdge(i2, i0, false, false);
closeEdge(i2, i0);
}
}
@@ -7809,10 +9086,10 @@ void addQuadsEdges() {
int i2 = 4 * i + 2;
int i3 = 4 * i + 3;
- addEdge(i0, i1, true, false);
+ addEdge(i0, i1, true, false);
addEdge(i1, i2, false, false);
- addEdge(i2, i3, false, false);
- addEdge(i3, i0, false, false);
+ addEdge(i2, i3, false, false);
+ addEdge(i3, i0, false, false);
closeEdge(i3, i0);
}
}
@@ -7824,10 +9101,10 @@ void addQuadStripEdges() {
int i2 = 2 * qd + 1;
int i3 = 2 * qd;
- addEdge(i0, i1, true, false);
+ addEdge(i0, i1, true, false);
addEdge(i1, i2, false, false);
- addEdge(i2, i3, false, false);
- addEdge(i3, i0, false, true);
+ addEdge(i2, i3, false, false);
+ addEdge(i3, i0, false, false);
closeEdge(i3, i0);
}
}
@@ -8201,7 +9478,8 @@ void addArc(float x, float y, float w, float h,
addEdge(pidx, idx, i == 0, false);
} else if (0 < i) {
// when drawing full circle, the edge is closed later
- addEdge(pidx, idx, i == inc, i == length && !fullCircle);
+ addEdge(pidx, idx, i == PApplet.min(inc, length),
+ i == length && !fullCircle);
}
}
} while (i < length);
@@ -8421,9 +9699,6 @@ int[] addSphere(float r, int detailU, int detailV,
indices[indCount + 1] = vert1 - detailU;
indices[indCount + 2] = vert1 - 1;
indCount += 3;
-
- addEdge(vert1 - detailU, vert1 - 1, true, true);
- addEdge(vert1 - 1, vert1, true, true);
}
// Northern cap -------------------------------------------------------
@@ -8447,7 +9722,6 @@ int[] addSphere(float r, int detailU, int detailV,
indices[indCount + 3 * i + 1] = i0;
indices[indCount + 3 * i + 2] = i0 + 1;
- addEdge(i0, i0 + 1, true, true);
addEdge(i0, i1, true, true);
}
indCount += 3 * detailU;
@@ -8461,6 +9735,7 @@ int[] addSphere(float r, int detailU, int detailV,
static protected class TessGeometry {
int renderMode;
PGraphicsOpenGL pg;
+ AttributeMap polyAttribs;
// Tessellated polygon data
int polyVertexCount;
@@ -8478,6 +9753,9 @@ static protected class TessGeometry {
IntBuffer polyEmissiveBuffer;
FloatBuffer polyShininessBuffer;
+ // Generic attributes
+ HashMap polyAttribBuffers = new HashMap();
+
int polyIndexCount;
int firstPolyIndex;
int lastPolyIndex;
@@ -8531,8 +9809,13 @@ static protected class TessGeometry {
float[] pointOffsets;
short[] pointIndices;
- TessGeometry(PGraphicsOpenGL pg, int mode) {
+ HashMap fpolyAttribs = new HashMap();
+ HashMap ipolyAttribs = new HashMap();
+ HashMap bpolyAttribs = new HashMap();
+
+ TessGeometry(PGraphicsOpenGL pg, AttributeMap attr, int mode) {
this.pg = pg;
+ this.polyAttribs = attr;
renderMode = mode;
allocate();
}
@@ -8585,6 +9868,22 @@ void allocate() {
clear();
}
+ void initAttrib(VertexAttribute attrib) {
+ if (attrib.type == PGL.FLOAT && !fpolyAttribs.containsKey(attrib.name)) {
+ float[] temp = new float[attrib.tessSize * PGL.DEFAULT_TESS_VERTICES];
+ fpolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateFloatBuffer(temp));
+ } else if (attrib.type == PGL.INT && !ipolyAttribs.containsKey(attrib.name)) {
+ int[] temp = new int[attrib.tessSize * PGL.DEFAULT_TESS_VERTICES];
+ ipolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateIntBuffer(temp));
+ } else if (attrib.type == PGL.BOOL && !bpolyAttribs.containsKey(attrib.name)) {
+ byte[] temp = new byte[attrib.tessSize * PGL.DEFAULT_TESS_VERTICES];
+ bpolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateByteBuffer(temp));
+ }
+ }
+
void clear() {
firstPolyVertex = lastPolyVertex = polyVertexCount = 0;
firstPolyIndex = lastPolyIndex = polyIndexCount = 0;
@@ -8612,6 +9911,7 @@ void polyVertexCheck() {
expandPolySpecular(newSize);
expandPolyEmissive(newSize);
expandPolyShininess(newSize);
+ expandAttributes(newSize);
}
firstPolyVertex = polyVertexCount;
@@ -8632,6 +9932,7 @@ void polyVertexCheck(int count) {
expandPolySpecular(newSize);
expandPolyEmissive(newSize);
expandPolyShininess(newSize);
+ expandAttributes(newSize);
}
firstPolyVertex = polyVertexCount;
@@ -8885,6 +10186,30 @@ protected void updatePolyShininessBuffer(int offset, int size) {
PGL.updateFloatBuffer(polyShininessBuffer, polyShininess, offset, size);
}
+ protected void updateAttribBuffer(String name) {
+ updateAttribBuffer(name, 0, polyVertexCount);
+ }
+
+ protected void updateAttribBuffer(String name, int offset, int size) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.type == PGL.FLOAT) {
+ FloatBuffer buffer = (FloatBuffer)polyAttribBuffers.get(name);
+ float[] array = fpolyAttribs.get(name);
+ PGL.updateFloatBuffer(buffer, array,
+ attrib.tessSize * offset, attrib.tessSize * size);
+ } else if (attrib.type == PGL.INT) {
+ IntBuffer buffer = (IntBuffer)polyAttribBuffers.get(name);
+ int[] array = ipolyAttribs.get(name);
+ PGL.updateIntBuffer(buffer, array,
+ attrib.tessSize * offset, attrib.tessSize * size);
+ } else if (attrib.type == PGL.BOOL) {
+ ByteBuffer buffer = (ByteBuffer)polyAttribBuffers.get(name);
+ byte[] array = bpolyAttribs.get(name);
+ PGL.updateByteBuffer(buffer, array,
+ attrib.tessSize * offset, attrib.tessSize * size);
+ }
+ }
+
protected void updatePolyIndicesBuffer() {
updatePolyIndicesBuffer(0, polyIndexCount);
}
@@ -9021,6 +10346,43 @@ void expandPolyShininess(int n) {
polyShininessBuffer = PGL.allocateFloatBuffer(polyShininess);
}
+ void expandAttributes(int n) {
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.type == PGL.FLOAT) {
+ expandFloatAttribute(attrib, n);
+ } else if (attrib.type == PGL.INT) {
+ expandIntAttribute(attrib, n);
+ } else if (attrib.type == PGL.BOOL) {
+ expandBoolAttribute(attrib, n);
+ }
+ }
+ }
+
+ void expandFloatAttribute(VertexAttribute attrib, int n) {
+ float[] array = fpolyAttribs.get(attrib.name);
+ float temp[] = new float[attrib.tessSize * n];
+ PApplet.arrayCopy(array, 0, temp, 0, attrib.tessSize * polyVertexCount);
+ fpolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateFloatBuffer(temp));
+ }
+
+ void expandIntAttribute(VertexAttribute attrib, int n) {
+ int[] array = ipolyAttribs.get(attrib.name);
+ int temp[] = new int[attrib.tessSize * n];
+ PApplet.arrayCopy(array, 0, temp, 0, attrib.tessSize * polyVertexCount);
+ ipolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateIntBuffer(temp));
+ }
+
+ void expandBoolAttribute(VertexAttribute attrib, int n) {
+ byte[] array = bpolyAttribs.get(attrib.name);
+ byte temp[] = new byte[attrib.tessSize * n];
+ PApplet.arrayCopy(array, 0, temp, 0, attrib.tessSize * polyVertexCount);
+ bpolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateByteBuffer(temp));
+ }
+
void expandPolyIndices(int n) {
short temp[] = new short[n];
PApplet.arrayCopy(polyIndices, 0, temp, 0, polyIndexCount);
@@ -9098,6 +10460,7 @@ void trim() {
trimPolySpecular();
trimPolyEmissive();
trimPolyShininess();
+ trimPolyAttributes();
}
if (0 < polyIndexCount && polyIndexCount < polyIndices.length) {
@@ -9181,6 +10544,43 @@ void trimPolyShininess() {
polyShininessBuffer = PGL.allocateFloatBuffer(polyShininess);
}
+ void trimPolyAttributes() {
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.type == PGL.FLOAT) {
+ trimFloatAttribute(attrib);
+ } else if (attrib.type == PGL.INT) {
+ trimIntAttribute(attrib);
+ } else if (attrib.type == PGL.BOOL) {
+ trimBoolAttribute(attrib);
+ }
+ }
+ }
+
+ void trimFloatAttribute(VertexAttribute attrib) {
+ float[] array = fpolyAttribs.get(attrib.name);
+ float temp[] = new float[attrib.tessSize * polyVertexCount];
+ PApplet.arrayCopy(array, 0, temp, 0, attrib.tessSize * polyVertexCount);
+ fpolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateFloatBuffer(temp));
+ }
+
+ void trimIntAttribute(VertexAttribute attrib) {
+ int[] array = ipolyAttribs.get(attrib.name);
+ int temp[] = new int[attrib.tessSize * polyVertexCount];
+ PApplet.arrayCopy(array, 0, temp, 0, attrib.tessSize * polyVertexCount);
+ ipolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateIntBuffer(temp));
+ }
+
+ void trimBoolAttribute(VertexAttribute attrib) {
+ byte[] array = bpolyAttribs.get(attrib.name);
+ byte temp[] = new byte[attrib.tessSize * polyVertexCount];
+ PApplet.arrayCopy(array, 0, temp, 0, attrib.tessSize * polyVertexCount);
+ bpolyAttribs.put(attrib.name, temp);
+ polyAttribBuffers.put(attrib.name, PGL.allocateByteBuffer(temp));
+ }
+
void trimPolyIndices() {
short temp[] = new short[polyIndexCount];
PApplet.arrayCopy(polyIndices, 0, temp, 0, polyIndexCount);
@@ -9444,6 +10844,104 @@ void setLineVertex(int tessIdx, float[] vertices, int inIdx0, int inIdx1,
//
// Add poly geometry
+ void addPolyVertex(double[] d, boolean clampXY) {
+ int fcolor =
+ (int)d[ 3] << 24 | (int)d[ 4] << 16 | (int)d[ 5] << 8 | (int)d[ 6];
+ int acolor =
+ (int)d[12] << 24 | (int)d[13] << 16 | (int)d[14] << 8 | (int)d[15];
+ int scolor =
+ (int)d[16] << 24 | (int)d[17] << 16 | (int)d[18] << 8 | (int)d[19];
+ int ecolor =
+ (int)d[20] << 24 | (int)d[21] << 16 | (int)d[22] << 8 | (int)d[23];
+
+ addPolyVertex((float)d[ 0], (float)d[ 1], (float)d[ 2],
+ fcolor,
+ (float)d[ 7], (float)d[ 8], (float)d[ 9],
+ (float)d[10], (float)d[11],
+ acolor, scolor, ecolor,
+ (float)d[24],
+ clampXY);
+
+ if (25 < d.length) {
+ // Add the values of the custom attributes...
+ PMatrix3D mm = pg.modelview;
+ PMatrix3D nm = pg.modelviewInv;
+ int tessIdx = polyVertexCount - 1;
+ int index;
+ int pos = 25;
+ for (int i = 0; i < polyAttribs.size(); i++) {
+ VertexAttribute attrib = polyAttribs.get(i);
+ String name = attrib.name;
+ index = attrib.tessSize * tessIdx;
+ if (attrib.isColor()) {
+ // Reconstruct color from ARGB components
+ int color =
+ (int)d[pos + 0] << 24 | (int)d[pos + 1] << 16 | (int)d[pos + 2] << 8 | (int)d[pos + 3];
+ int[] tessValues = ipolyAttribs.get(name);
+ tessValues[index] = color;
+ pos += 4;
+ } else if (attrib.isPosition()) {
+ float[] farray = fpolyAttribs.get(name);
+ float x = (float)d[pos++];
+ float y = (float)d[pos++];
+ float z = (float)d[pos++];
+ if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) {
+ if (clampXY) {
+ // ceil emulates the behavior of JAVA2D
+ farray[index++] =
+ PApplet.ceil(x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03);
+ farray[index++] =
+ PApplet.ceil(x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13);
+ } else {
+ farray[index++] = x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03;
+ farray[index++] = x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13;
+ }
+ farray[index++] = x*mm.m20 + y*mm.m21 + z*mm.m22 + mm.m23;
+ farray[index ] = x*mm.m30 + y*mm.m31 + z*mm.m32 + mm.m33;
+ } else {
+ farray[index++] = x;
+ farray[index++] = y;
+ farray[index++] = z;
+ farray[index ] = 1;
+ }
+ } else if (attrib.isNormal()) {
+ float[] farray = fpolyAttribs.get(name);
+ float x = (float)d[pos + 0];
+ float y = (float)d[pos + 1];
+ float z = (float)d[pos + 2];
+ if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) {
+ farray[index++] = x*nm.m00 + y*nm.m10 + z*nm.m20;
+ farray[index++] = x*nm.m01 + y*nm.m11 + z*nm.m21;
+ farray[index ] = x*nm.m02 + y*nm.m12 + z*nm.m22;
+ } else {
+ farray[index++] = x;
+ farray[index++] = y;
+ farray[index ] = z;
+ }
+ pos += 3;
+ } else {
+ if (attrib.isFloat()) {
+ float[] farray = fpolyAttribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ farray[index++] = (float)d[pos++];
+ }
+ } else if (attrib.isInt()) {
+ int[] iarray = ipolyAttribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ iarray[index++] = (int)d[pos++];
+ }
+ } else if (attrib.isBool()) {
+ byte[] barray = bpolyAttribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ barray[index++] = (byte)d[pos++];
+ }
+ }
+ pos += attrib.size;
+ }
+ }
+ }
+ }
+
void addPolyVertex(float x, float y, float z,
int rgba,
float nx, float ny, float nz,
@@ -9531,124 +11029,273 @@ void addPolyVertex(InGeometry in, int i, boolean clampXY) {
addPolyVertices(in, i, i, clampXY);
}
- void addPolyVertices(InGeometry in, int i0, int i1, boolean clampXY) {
- int index;
- int nvert = i1 - i0 + 1;
+ void addPolyVertices(InGeometry in, int i0, int i1, boolean clampXY) {
+ int index = 0;
+ int nvert = i1 - i0 + 1;
+
+ polyVertexCheck(nvert);
+
+ if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) {
+ modelviewCoords(in, i0, index, nvert, clampXY);
+ } else {
+ if (nvert <= PGL.MIN_ARRAYCOPY_SIZE) {
+ copyFewCoords(in, i0, index, nvert);
+ } else {
+ copyManyCoords(in, i0, index, nvert);
+ }
+ }
+
+ if (nvert <= PGL.MIN_ARRAYCOPY_SIZE) {
+ copyFewAttribs(in, i0, index, nvert);
+ } else {
+ copyManyAttribs(in, i0, index, nvert);
+ }
+ }
+
+ // Apply modelview transformation on the vertices
+ private void modelviewCoords(InGeometry in, int i0, int index, int nvert, boolean clampXY) {
+ PMatrix3D mm = pg.modelview;
+ PMatrix3D nm = pg.modelviewInv;
+
+ for (int i = 0; i < nvert; i++) {
+ int inIdx = i0 + i;
+ int tessIdx = firstPolyVertex + i;
+
+ index = 3 * inIdx;
+ float x = in.vertices[index++];
+ float y = in.vertices[index++];
+ float z = in.vertices[index ];
+
+ index = 3 * inIdx;
+ float nx = in.normals[index++];
+ float ny = in.normals[index++];
+ float nz = in.normals[index ];
+
+ index = 4 * tessIdx;
+ if (clampXY) {
+ // ceil emulates the behavior of JAVA2D
+ polyVertices[index++] =
+ PApplet.ceil(x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03);
+ polyVertices[index++] =
+ PApplet.ceil(x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13);
+ } else {
+ polyVertices[index++] = x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03;
+ polyVertices[index++] = x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13;
+ }
+ polyVertices[index++] = x*mm.m20 + y*mm.m21 + z*mm.m22 + mm.m23;
+ polyVertices[index ] = x*mm.m30 + y*mm.m31 + z*mm.m32 + mm.m33;
+
+ index = 3 * tessIdx;
+ polyNormals[index++] = nx*nm.m00 + ny*nm.m10 + nz*nm.m20;
+ polyNormals[index++] = nx*nm.m01 + ny*nm.m11 + nz*nm.m21;
+ polyNormals[index ] = nx*nm.m02 + ny*nm.m12 + nz*nm.m22;
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.isColor() || attrib.isOther()) continue;
+
+ float[] inValues = in.fattribs.get(name);
+ index = 3 * inIdx;
+ x = inValues[index++];
+ y = inValues[index++];
+ z = inValues[index ];
+
+ float[] tessValues = fpolyAttribs.get(name);
+ if (attrib.isPosition()) {
+ index = 4 * tessIdx;
+ if (clampXY) {
+ // ceil emulates the behavior of JAVA2D
+ tessValues[index++] =
+ PApplet.ceil(x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03);
+ tessValues[index++] =
+ PApplet.ceil(x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13);
+ } else {
+ tessValues[index++] = x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03;
+ tessValues[index++] = x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13;
+ }
+ tessValues[index++] = x*mm.m20 + y*mm.m21 + z*mm.m22 + mm.m23;
+ tessValues[index ] = x*mm.m30 + y*mm.m31 + z*mm.m32 + mm.m33;
+ } else {
+ index = 3 * tessIdx;
+ tessValues[index++] = x*nm.m00 + y*nm.m10 + z*nm.m20;
+ tessValues[index++] = x*nm.m01 + y*nm.m11 + z*nm.m21;
+ tessValues[index ] = x*nm.m02 + y*nm.m12 + z*nm.m22;
+ }
+ }
+ }
+ }
+
+ // Just copy vertices one by one.
+ private void copyFewCoords(InGeometry in, int i0, int index, int nvert) {
+ // Copying elements one by one instead of using arrayCopy is more
+ // efficient for few vertices...
+ for (int i = 0; i < nvert; i++) {
+ int inIdx = i0 + i;
+ int tessIdx = firstPolyVertex + i;
- polyVertexCheck(nvert);
+ index = 3 * inIdx;
+ float x = in.vertices[index++];
+ float y = in.vertices[index++];
+ float z = in.vertices[index ];
- if (renderMode == IMMEDIATE && pg.flushMode == FLUSH_WHEN_FULL) {
- PMatrix3D mm = pg.modelview;
- PMatrix3D nm = pg.modelviewInv;
+ index = 3 * inIdx;
+ float nx = in.normals[index++];
+ float ny = in.normals[index++];
+ float nz = in.normals[index ];
- for (int i = 0; i < nvert; i++) {
- int inIdx = i0 + i;
- int tessIdx = firstPolyVertex + i;
+ index = 4 * tessIdx;
+ polyVertices[index++] = x;
+ polyVertices[index++] = y;
+ polyVertices[index++] = z;
+ polyVertices[index ] = 1;
- index = 3 * inIdx;
- float x = in.vertices[index++];
- float y = in.vertices[index++];
- float z = in.vertices[index ];
+ index = 3 * tessIdx;
+ polyNormals[index++] = nx;
+ polyNormals[index++] = ny;
+ polyNormals[index ] = nz;
- index = 3 * inIdx;
- float nx = in.normals[index++];
- float ny = in.normals[index++];
- float nz = in.normals[index ];
-
- index = 4 * tessIdx;
- if (clampXY) {
- // ceil emulates the behavior of JAVA2D
- polyVertices[index++] =
- PApplet.ceil(x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03);
- polyVertices[index++] =
- PApplet.ceil(x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13);
- } else {
- polyVertices[index++] = x*mm.m00 + y*mm.m01 + z*mm.m02 + mm.m03;
- polyVertices[index++] = x*mm.m10 + y*mm.m11 + z*mm.m12 + mm.m13;
- }
- polyVertices[index++] = x*mm.m20 + y*mm.m21 + z*mm.m22 + mm.m23;
- polyVertices[index ] = x*mm.m30 + y*mm.m31 + z*mm.m32 + mm.m33;
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.isColor() || attrib.isOther()) continue;
- index = 3 * tessIdx;
- polyNormals[index++] = nx*nm.m00 + ny*nm.m10 + nz*nm.m20;
- polyNormals[index++] = nx*nm.m01 + ny*nm.m11 + nz*nm.m21;
- polyNormals[index ] = nx*nm.m02 + ny*nm.m12 + nz*nm.m22;
- }
- } else {
- if (nvert <= PGL.MIN_ARRAYCOPY_SIZE) {
- // Copying elements one by one instead of using arrayCopy is more
- // efficient for few vertices...
- for (int i = 0; i < nvert; i++) {
- int inIdx = i0 + i;
- int tessIdx = firstPolyVertex + i;
-
- index = 3 * inIdx;
- float x = in.vertices[index++];
- float y = in.vertices[index++];
- float z = in.vertices[index ];
-
- index = 3 * inIdx;
- float nx = in.normals[index++];
- float ny = in.normals[index++];
- float nz = in.normals[index ];
+ float[] inValues = in.fattribs.get(name);
+ index = 3 * inIdx;
+ x = inValues[index++];
+ y = inValues[index++];
+ z = inValues[index ];
+ float[] tessValues = fpolyAttribs.get(name);
+ if (attrib.isPosition()) {
index = 4 * tessIdx;
- polyVertices[index++] = x;
- polyVertices[index++] = y;
- polyVertices[index++] = z;
- polyVertices[index ] = 1;
-
+ tessValues[index++] = x;
+ tessValues[index++] = y;
+ tessValues[index++] = z;
+ tessValues[index ] = 1;
+ } else {
index = 3 * tessIdx;
- polyNormals[index++] = nx;
- polyNormals[index++] = ny;
- polyNormals[index ] = nz;
- }
- } else {
- for (int i = 0; i < nvert; i++) {
- int inIdx = i0 + i;
- int tessIdx = firstPolyVertex + i;
- PApplet.arrayCopy(in.vertices, 3 * inIdx,
- polyVertices, 4 * tessIdx, 3);
- polyVertices[4 * tessIdx + 3] = 1;
+ tessValues[index++] = x;
+ tessValues[index++] = y;
+ tessValues[index ] = z;
}
- PApplet.arrayCopy(in.normals, 3 * i0,
- polyNormals, 3 * firstPolyVertex, 3 * nvert);
}
}
+ }
- if (nvert <= PGL.MIN_ARRAYCOPY_SIZE) {
- for (int i = 0; i < nvert; i++) {
- int inIdx = i0 + i;
- int tessIdx = firstPolyVertex + i;
-
- index = 2 * inIdx;
- float u = in.texcoords[index++];
- float v = in.texcoords[index ];
-
- polyColors[tessIdx] = in.colors[inIdx];
-
- index = 2 * tessIdx;
- polyTexCoords[index++] = u;
- polyTexCoords[index ] = v;
-
- polyAmbient[tessIdx] = in.ambient[inIdx];
- polySpecular[tessIdx] = in.specular[inIdx];
- polyEmissive[tessIdx] = in.emissive[inIdx];
- polyShininess[tessIdx] = in.shininess[inIdx];
+ // Copy many vertices using arrayCopy
+ private void copyManyCoords(InGeometry in, int i0, int index, int nvert) {
+ for (int i = 0; i < nvert; i++) {
+ // Position data needs to be copied in batches of three, because the
+ // input vertices don't have a w coordinate.
+ int inIdx = i0 + i;
+ int tessIdx = firstPolyVertex + i;
+ PApplet.arrayCopy(in.vertices, 3 * inIdx,
+ polyVertices, 4 * tessIdx, 3);
+ polyVertices[4 * tessIdx + 3] = 1;
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (!attrib.isPosition()) continue;
+ float[] inValues = in.fattribs.get(name);
+ float[] tessValues = fpolyAttribs.get(name);
+ PApplet.arrayCopy(inValues, 3 * inIdx,
+ tessValues, 4 * tessIdx, 3);
+ tessValues[4 * tessIdx + 3] = 1;
+ }
+ }
+ PApplet.arrayCopy(in.normals, 3 * i0,
+ polyNormals, 3 * firstPolyVertex, 3 * nvert);
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (!attrib.isNormal()) continue;
+ float[] inValues = in.fattribs.get(name);
+ float[] tessValues = fpolyAttribs.get(name);
+ PApplet.arrayCopy(inValues, 3 * i0,
+ tessValues, 3 * firstPolyVertex, 3 * nvert);
+ }
+ }
+
+ // Just copy attributes one by one.
+ private void copyFewAttribs(InGeometry in, int i0, int index, int nvert) {
+ for (int i = 0; i < nvert; i++) {
+ int inIdx = i0 + i;
+ int tessIdx = firstPolyVertex + i;
+
+ index = 2 * inIdx;
+ float u = in.texcoords[index++];
+ float v = in.texcoords[index ];
+
+ polyColors[tessIdx] = in.colors[inIdx];
+
+ index = 2 * tessIdx;
+ polyTexCoords[index++] = u;
+ polyTexCoords[index ] = v;
+
+ polyAmbient[tessIdx] = in.ambient[inIdx];
+ polySpecular[tessIdx] = in.specular[inIdx];
+ polyEmissive[tessIdx] = in.emissive[inIdx];
+ polyShininess[tessIdx] = in.shininess[inIdx];
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.isPosition() || attrib.isNormal()) continue;
+ int index0 = attrib.size * inIdx;
+ int index1 = attrib.size * tessIdx;
+ if (attrib.isFloat()) {
+ float[] inValues = in.fattribs.get(name);
+ float[] tessValues = fpolyAttribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ tessValues[index1++] = inValues[index0++];
+ }
+ } else if (attrib.isInt()) {
+ int[] inValues = in.iattribs.get(name);
+ int[] tessValues = ipolyAttribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ tessValues[index1++] = inValues[index0++];
+ }
+ } else if (attrib.isBool()) {
+ byte[] inValues = in.battribs.get(name);
+ byte[] tessValues = bpolyAttribs.get(name);
+ for (int n = 0; n < attrib.size; n++) {
+ tessValues[index1++] = inValues[index0++];
+ }
+ }
}
- } else {
- PApplet.arrayCopy(in.colors, i0,
- polyColors, firstPolyVertex, nvert);
- PApplet.arrayCopy(in.texcoords, 2 * i0,
- polyTexCoords, 2 * firstPolyVertex, 2 * nvert);
- PApplet.arrayCopy(in.ambient, i0,
- polyAmbient, firstPolyVertex, nvert);
- PApplet.arrayCopy(in.specular, i0,
- polySpecular, firstPolyVertex, nvert);
- PApplet.arrayCopy(in.emissive, i0,
- polyEmissive, firstPolyVertex, nvert);
- PApplet.arrayCopy(in.shininess, i0,
- polyShininess, firstPolyVertex, nvert);
+ }
+ }
+
+ // Copy many attributes using arrayCopy()
+ private void copyManyAttribs(InGeometry in, int i0, int index, int nvert) {
+ PApplet.arrayCopy(in.colors, i0,
+ polyColors, firstPolyVertex, nvert);
+ PApplet.arrayCopy(in.texcoords, 2 * i0,
+ polyTexCoords, 2 * firstPolyVertex, 2 * nvert);
+ PApplet.arrayCopy(in.ambient, i0,
+ polyAmbient, firstPolyVertex, nvert);
+ PApplet.arrayCopy(in.specular, i0,
+ polySpecular, firstPolyVertex, nvert);
+ PApplet.arrayCopy(in.emissive, i0,
+ polyEmissive, firstPolyVertex, nvert);
+ PApplet.arrayCopy(in.shininess, i0,
+ polyShininess, firstPolyVertex, nvert);
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.isPosition() || attrib.isNormal()) continue;
+ Object inValues = null;
+ Object tessValues = null;
+ if (attrib.isFloat()) {
+ inValues = in.fattribs.get(name);
+ tessValues = fpolyAttribs.get(name);
+ } else if (attrib.isInt()) {
+ inValues = in.iattribs.get(name);
+ tessValues = ipolyAttribs.get(name);
+ } else if (attrib.isBool()) {
+ inValues = in.battribs.get(name);
+ tessValues = bpolyAttribs.get(name);
+ }
+ PApplet.arrayCopy(inValues, attrib.size * i0,
+ tessValues, attrib.tessSize * firstPolyVertex,
+ attrib.size * nvert);
}
}
@@ -9700,6 +11347,27 @@ void applyMatrixOnPolyGeometry(PMatrix2D tr, int first, int last) {
index = 3 * i;
polyNormals[index++] = nx*tr.m00 + ny*tr.m01;
polyNormals[index ] = nx*tr.m10 + ny*tr.m11;
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.isColor() || attrib.isOther()) continue;
+ float[] values = fpolyAttribs.get(name);
+ if (attrib.isPosition()) {
+ index = 4 * i;
+ x = values[index++];
+ y = values[index ];
+ index = 4 * i;
+ values[index++] = x*tr.m00 + y*tr.m01 + tr.m02;
+ values[index ] = x*tr.m10 + y*tr.m11 + tr.m12;
+ } else {
+ index = 3 * i;
+ nx = values[index++];
+ ny = values[index ];
+ index = 3 * i;
+ values[index++] = nx*tr.m00 + ny*tr.m01;
+ values[index ] = nx*tr.m10 + ny*tr.m11;
+ }
+ }
}
}
}
@@ -9708,17 +11376,15 @@ void applyMatrixOnLineGeometry(PMatrix2D tr, int first, int last) {
if (first < last) {
int index;
+ float scaleFactor = matrixScale(tr);
for (int i = first; i <= last; i++) {
index = 4 * i;
float x = lineVertices[index++];
float y = lineVertices[index ];
index = 4 * i;
- float xa = lineDirections[index++];
- float ya = lineDirections[index ];
-
- float dx = xa - x;
- float dy = ya - y;
+ float dx = lineDirections[index++];
+ float dy = lineDirections[index ];
index = 4 * i;
lineVertices[index++] = x*tr.m00 + y*tr.m01 + tr.m02;
@@ -9727,6 +11393,7 @@ void applyMatrixOnLineGeometry(PMatrix2D tr, int first, int last) {
index = 4 * i;
lineDirections[index++] = dx*tr.m00 + dy*tr.m01;
lineDirections[index ] = dx*tr.m10 + dy*tr.m11;
+ lineDirections[index + 2] *= scaleFactor;
}
}
}
@@ -9735,6 +11402,7 @@ void applyMatrixOnPointGeometry(PMatrix2D tr, int first, int last) {
if (first < last) {
int index;
+ float matrixScale = matrixScale(tr);
for (int i = first; i <= last; i++) {
index = 4 * i;
float x = pointVertices[index++];
@@ -9743,6 +11411,10 @@ void applyMatrixOnPointGeometry(PMatrix2D tr, int first, int last) {
index = 4 * i;
pointVertices[index++] = x*tr.m00 + y*tr.m01 + tr.m02;
pointVertices[index ] = x*tr.m10 + y*tr.m11 + tr.m12;
+
+ index = 2 * i;
+ pointOffsets[index++] *= matrixScale;
+ pointOffsets[index] *= matrixScale;
}
}
}
@@ -9773,6 +11445,33 @@ void applyMatrixOnPolyGeometry(PMatrix3D tr, int first, int last) {
polyNormals[index++] = nx*tr.m00 + ny*tr.m01 + nz*tr.m02;
polyNormals[index++] = nx*tr.m10 + ny*tr.m11 + nz*tr.m12;
polyNormals[index ] = nx*tr.m20 + ny*tr.m21 + nz*tr.m22;
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.isColor() || attrib.isOther()) continue;
+ float[] values = fpolyAttribs.get(name);
+ if (attrib.isPosition()) {
+ index = 4 * i;
+ x = values[index++];
+ y = values[index++];
+ z = values[index++];
+ w = values[index ];
+ index = 4 * i;
+ values[index++] = x*tr.m00 + y*tr.m01 + z*tr.m02 + w*tr.m03;
+ values[index++] = x*tr.m10 + y*tr.m11 + z*tr.m12 + w*tr.m13;
+ values[index++] = x*tr.m20 + y*tr.m21 + z*tr.m22 + w*tr.m23;
+ values[index ] = x*tr.m30 + y*tr.m31 + z*tr.m32 + w*tr.m33;
+ } else {
+ index = 3 * i;
+ nx = values[index++];
+ ny = values[index++];
+ nz = values[index ];
+ index = 3 * i;
+ values[index++] = nx*tr.m00 + ny*tr.m01 + nz*tr.m02;
+ values[index++] = nx*tr.m10 + ny*tr.m11 + nz*tr.m12;
+ values[index ] = nx*tr.m20 + ny*tr.m21 + nz*tr.m22;
+ }
+ }
}
}
}
@@ -9781,6 +11480,7 @@ void applyMatrixOnLineGeometry(PMatrix3D tr, int first, int last) {
if (first < last) {
int index;
+ float scaleFactor = matrixScale(tr);
for (int i = first; i <= last; i++) {
index = 4 * i;
float x = lineVertices[index++];
@@ -9789,13 +11489,9 @@ void applyMatrixOnLineGeometry(PMatrix3D tr, int first, int last) {
float w = lineVertices[index ];
index = 4 * i;
- float xa = lineDirections[index++];
- float ya = lineDirections[index++];
- float za = lineDirections[index ];
-
- float dx = xa - x;
- float dy = ya - y;
- float dz = za - z;
+ float dx = lineDirections[index++];
+ float dy = lineDirections[index++];
+ float dz = lineDirections[index ];
index = 4 * i;
lineVertices[index++] = x*tr.m00 + y*tr.m01 + z*tr.m02 + w*tr.m03;
@@ -9806,7 +11502,8 @@ void applyMatrixOnLineGeometry(PMatrix3D tr, int first, int last) {
index = 4 * i;
lineDirections[index++] = dx*tr.m00 + dy*tr.m01 + dz*tr.m02;
lineDirections[index++] = dx*tr.m10 + dy*tr.m11 + dz*tr.m12;
- lineDirections[index ] = dx*tr.m20 + dy*tr.m21 + dz*tr.m22;
+ lineDirections[index++] = dx*tr.m20 + dy*tr.m21 + dz*tr.m22;
+ lineDirections[index] *= scaleFactor;
}
}
}
@@ -9815,6 +11512,7 @@ void applyMatrixOnPointGeometry(PMatrix3D tr, int first, int last) {
if (first < last) {
int index;
+ float matrixScale = matrixScale(tr);
for (int i = first; i <= last; i++) {
index = 4 * i;
float x = pointVertices[index++];
@@ -9827,6 +11525,10 @@ void applyMatrixOnPointGeometry(PMatrix3D tr, int first, int last) {
pointVertices[index++] = x*tr.m10 + y*tr.m11 + z*tr.m12 + w*tr.m13;
pointVertices[index++] = x*tr.m20 + y*tr.m21 + z*tr.m22 + w*tr.m23;
pointVertices[index ] = x*tr.m30 + y*tr.m31 + z*tr.m32 + w*tr.m33;
+
+ index = 2 * i;
+ pointOffsets[index++] *= matrixScale;
+ pointOffsets[index] *= matrixScale;
}
}
}
@@ -9895,7 +11597,7 @@ public Tessellator() {
void initGluTess() {
if (gluTess == null) {
- callback = new TessellatorCallback();
+ callback = new TessellatorCallback(tess.polyAttribs);
gluTess = pg.pgl.createTessellator(callback);
}
}
@@ -10046,9 +11748,9 @@ void tessellateRoundPoints3D(int nvertTot, int nindTot, int nPtVert) {
float inc = (float) SINCOS_LENGTH / perim;
for (int k = 0; k < perim; k++) {
tess.pointOffsets[2 * attribIdx + 0] =
- 0.5f * cosLUT[(int) val] * strokeWeight;
+ 0.5f * cosLUT[(int) val] * transformScale() * strokeWeight;
tess.pointOffsets[2 * attribIdx + 1] =
- 0.5f * sinLUT[(int) val] * strokeWeight;
+ 0.5f * sinLUT[(int) val] * transformScale() * strokeWeight;
val = (val + inc) % SINCOS_LENGTH;
attribIdx++;
}
@@ -10079,6 +11781,7 @@ void tessellateRoundPoints2D(int nvertTot, int nindTot, int nPtVert) {
IndexCache cache = tess.polyIndexCache;
int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast();
firstPointIndexCache = index;
+ if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index.
for (int i = 0; i < in.vertexCount; i++) {
int count = cache.vertexCount[index];
if (PGL.MAX_VERTEX_INDEX1 <= count + nPtVert) {
@@ -10175,9 +11878,9 @@ void tessellateSquarePoints3D(int nvertTot, int nindTot) {
attribIdx++;
for (int k = 0; k < 4; k++) {
tess.pointOffsets[2 * attribIdx + 0] =
- 0.5f * QUAD_POINT_SIGNS[k][0] * strokeWeight;
+ 0.5f * QUAD_POINT_SIGNS[k][0] * transformScale() * strokeWeight;
tess.pointOffsets[2 * attribIdx + 1] =
- 0.5f * QUAD_POINT_SIGNS[k][1] * strokeWeight;
+ 0.5f * QUAD_POINT_SIGNS[k][1] * transformScale() * strokeWeight;
attribIdx++;
}
@@ -10207,6 +11910,7 @@ void tessellateSquarePoints2D(int nvertTot, int nindTot) {
IndexCache cache = tess.polyIndexCache;
int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast();
firstPointIndexCache = index;
+ if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index.
for (int i = 0; i < in.vertexCount; i++) {
int nvert = 5;
int count = cache.vertexCount[index];
@@ -10283,16 +11987,24 @@ void tessellateLines3D(int lineCount) {
// require 3 indices to specify their connectivities.
int nind = lineCount * 2 * 3;
+ int vcount0 = tess.lineVertexCount;
+ int icount0 = tess.lineIndexCount;
tess.lineVertexCheck(nvert);
tess.lineIndexCheck(nind);
int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() :
tess.lineIndexCache.getLast();
firstLineIndexCache = index;
+ int[] tmp = {0, 0};
+ tess.lineIndexCache.setCounter(tmp);
for (int ln = 0; ln < lineCount; ln++) {
int i0 = 2 * ln + 0;
int i1 = 2 * ln + 1;
- index = addLineSegment3D(i0, i1, index, null, false);
+ index = addLineSegment3D(i0, i1, i0 - 2, i1 - 1, index, null, false);
}
+ // Adjust counts of line vertices and indices to exact values
+ tess.lineIndexCache.setCounter(null);
+ tess.lineIndexCount = icount0 + tmp[0];
+ tess.lineVertexCount = vcount0 + tmp[1];
lastLineIndexCache = index;
}
@@ -10361,9 +12073,11 @@ void tessellateLineStrip() {
void tessellateLineStrip3D(int lineCount) {
int nBevelTr = noCapsJoins() ? 0 : (lineCount - 1);
- int nvert = lineCount * 4 + nBevelTr;
+ int nvert = lineCount * 4 + nBevelTr * 3;
int nind = lineCount * 2 * 3 + nBevelTr * 2 * 3;
+ int vcount0 = tess.lineVertexCount;
+ int icount0 = tess.lineIndexCount;
tess.lineVertexCheck(nvert);
tess.lineIndexCheck(nind);
int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() :
@@ -10371,15 +12085,21 @@ void tessellateLineStrip3D(int lineCount) {
firstLineIndexCache = index;
int i0 = 0;
short[] lastInd = {-1, -1};
+ int[] tmp = {0, 0};
+ tess.lineIndexCache.setCounter(tmp);
for (int ln = 0; ln < lineCount; ln++) {
int i1 = ln + 1;
if (0 < nBevelTr) {
- index = addLineSegment3D(i0, i1, index, lastInd, false);
+ index = addLineSegment3D(i0, i1, i1 - 2, i1 - 1, index, lastInd, false);
} else {
- index = addLineSegment3D(i0, i1, index, null, false);
+ index = addLineSegment3D(i0, i1, i1 - 2, i1 - 1, index, null, false);
}
i0 = i1;
}
+ // Adjust counts of line vertices and indices to exact values
+ tess.lineIndexCache.setCounter(null);
+ tess.lineIndexCount = icount0 + tmp[0];
+ tess.lineVertexCount = vcount0 + tmp[1];
lastLineIndexCache = index;
}
@@ -10445,31 +12165,38 @@ void tessellateLineLoop() {
void tessellateLineLoop3D(int lineCount) {
int nBevelTr = noCapsJoins() ? 0 : lineCount;
- int nvert = lineCount * 4 + nBevelTr;
+ int nvert = lineCount * 4 + nBevelTr * 3;
int nind = lineCount * 2 * 3 + nBevelTr * 2 * 3;
+ int vcount0 = tess.lineVertexCount;
+ int icount0 = tess.lineIndexCount;
tess.lineVertexCheck(nvert);
tess.lineIndexCheck(nind);
int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() :
tess.lineIndexCache.getLast();
firstLineIndexCache = index;
int i0 = 0;
+ int i1 = -1;
short[] lastInd = {-1, -1};
- short firstInd = -1;
+ int[] tmp = {0, 0};
+ tess.lineIndexCache.setCounter(tmp);
for (int ln = 0; ln < lineCount - 1; ln++) {
- int i1 = ln + 1;
+ i1 = ln + 1;
if (0 < nBevelTr) {
- index = addLineSegment3D(i0, i1, index, lastInd, false);
- if (ln == 0) firstInd = (short)(lastInd[0] - 2);
+ index = addLineSegment3D(i0, i1, i1 - 2, i1 - 1, index, lastInd, false);
} else {
- index = addLineSegment3D(i0, i1, index, null, false);
+ index = addLineSegment3D(i0, i1, i1 - 2, i1 - 1, index, null, false);
}
i0 = i1;
}
- index = addLineSegment3D(0, in.vertexCount - 1, index, lastInd, false);
+ index = addLineSegment3D(in.vertexCount - 1, 0, i1 - 2, i1 - 1, index, lastInd, false);
if (0 < nBevelTr) {
- index = addBevel3D(0, index, lastInd, firstInd, false);
+ index = addBevel3D(0, 1, in.vertexCount - 1, 0, index, lastInd, false);
}
+ // Adjust counts of line vertices and indices to exact values
+ tess.lineIndexCache.setCounter(null);
+ tess.lineIndexCount = icount0 + tmp[0];
+ tess.lineVertexCount = vcount0 + tmp[1];
lastLineIndexCache = index;
}
@@ -10538,32 +12265,53 @@ void tessellateEdges3D() {
int nInVert = in.getNumEdgeVertices(bevel);
int nInInd = in.getNumEdgeIndices(bevel);
+ int vcount0 = tess.lineVertexCount;
+ int icount0 = tess.lineIndexCount;
tess.lineVertexCheck(nInVert);
tess.lineIndexCheck(nInInd);
int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() :
tess.lineIndexCache.getLast();
firstLineIndexCache = index;
+ int fi0 = 0;
+ int fi1 = 0;
short[] lastInd = {-1, -1};
- short firstInd = -1;
+ int pi0 = -1;
+ int pi1 = -1;
+
+ int[] tmp = {0, 0};
+ tess.lineIndexCache.setCounter(tmp);
for (int i = 0; i <= in.edgeCount - 1; i++) {
int[] edge = in.edges[i];
int i0 = edge[0];
int i1 = edge[1];
if (bevel) {
if (edge[2] == EDGE_CLOSE) {
- index = addBevel3D(edge[1], index, lastInd, firstInd, false);
- lastInd[0] = lastInd[1] = -1; // No join with next line segment.
+ index = addBevel3D(fi0, fi1, pi0, pi1, index, lastInd, false);
} else {
- index = addLineSegment3D(i0, i1, index, lastInd, false);
- if (edge[2] == EDGE_START) firstInd = (short)(lastInd[0] - 2);
- if (edge[2] == EDGE_STOP || edge[2] == EDGE_SINGLE) {
- lastInd[0] = lastInd[1] = -1; // No join with next line segment.
- }
+ index = addLineSegment3D(i0, i1, pi0, pi1, index, lastInd, false);
}
} else if (edge[2] != EDGE_CLOSE) {
- index = addLineSegment3D(i0, i1, index, null, false);
+ index = addLineSegment3D(i0, i1, pi0, pi1, index, null, false);
+ }
+ if (edge[2] == EDGE_START) {
+ fi0 = i0;
+ fi1 = i1;
+ }
+
+ if (edge[2] == EDGE_STOP || edge[2] == EDGE_SINGLE || edge[2] == EDGE_CLOSE) {
+ // No join with next line segment.
+ lastInd[0] = lastInd[1] = -1;
+ pi1 = pi0 = -1;
+ } else {
+ pi0 = i0;
+ pi1 = i1;
}
}
+ // Adjust counts of line vertices and indices to exact values
+ tess.lineIndexCache.setCounter(null);
+ tess.lineIndexCount = icount0 + tmp[0];
+ tess.lineVertexCount = vcount0 + tmp[1];
+
lastLineIndexCache = index;
}
@@ -10644,7 +12392,7 @@ boolean clampEdges2D() {
// Adding the data that defines a quad starting at vertex i0 and
// ending at i1.
- int addLineSegment3D(int i0, int i1, int index, short[] lastInd,
+ int addLineSegment3D(int i0, int i1, int pi0, int pi1, int index, short[] lastInd,
boolean constStroke) {
IndexCache cache = tess.lineIndexCache;
int count = cache.vertexCount[index];
@@ -10690,21 +12438,31 @@ int addLineSegment3D(int i0, int i1, int index, short[] lastInd,
if (lastInd != null) {
if (-1 < lastInd[0] && -1 < lastInd[1]) {
// Adding bevel triangles
- tess.setLineVertex(vidx, strokeVertices, i0, color0);
-
if (newCache) {
- PGraphics.showWarning(TOO_LONG_STROKE_PATH_ERROR);
-
- // TODO: Fix this situation, the vertices from the previous cache
- // block should be copied in the newly created one.
- tess.lineIndices[iidx++] = (short) (count + 4);
- tess.lineIndices[iidx++] = (short) (count + 0);
- tess.lineIndices[iidx++] = (short) (count + 0);
-
- tess.lineIndices[iidx++] = (short) (count + 4);
- tess.lineIndices[iidx++] = (short) (count + 1);
- tess.lineIndices[iidx ] = (short) (count + 1);
+ if (-1 < pi0 && -1 < pi1) {
+ // Vertices used in the previous cache need to be copied to the
+ // newly created one
+ color = constStroke ? strokeColor : strokeColors[pi0];
+ weight = constStroke ? strokeWeight : strokeWeights[pi0];
+ weight *= transformScale();
+
+ tess.setLineVertex(vidx++, strokeVertices, pi1, color);
+ tess.setLineVertex(vidx++, strokeVertices, pi1, pi0, color, -weight/2); // count+2 vert from previous block
+ tess.setLineVertex(vidx, strokeVertices, pi1, pi0, color, +weight/2); // count+3 vert from previous block
+
+ tess.lineIndices[iidx++] = (short) (count + 4);
+ tess.lineIndices[iidx++] = (short) (count + 5);
+ tess.lineIndices[iidx++] = (short) (count + 0);
+
+ tess.lineIndices[iidx++] = (short) (count + 4);
+ tess.lineIndices[iidx++] = (short) (count + 6);
+ tess.lineIndices[iidx ] = (short) (count + 1);
+
+ cache.incCounts(index, 6, 3);
+ }
} else {
+ tess.setLineVertex(vidx, strokeVertices, i0, color0);
+
tess.lineIndices[iidx++] = (short) (count + 4);
tess.lineIndices[iidx++] = lastInd[0];
tess.lineIndices[iidx++] = (short) (count + 0);
@@ -10712,64 +12470,67 @@ int addLineSegment3D(int i0, int i1, int index, short[] lastInd,
tess.lineIndices[iidx++] = (short) (count + 4);
tess.lineIndices[iidx++] = lastInd[1];
tess.lineIndices[iidx ] = (short) (count + 1);
- }
- cache.incCounts(index, 6, 1);
+ cache.incCounts(index, 6, 1);
+ }
}
- // Vertices for next bevel
+ // The last two vertices of the segment will be used in the next
+ // bevel triangle
lastInd[0] = (short) (count + 2);
lastInd[1] = (short) (count + 3);
}
return index;
}
- int addBevel3D(int i0, int index, short[] lastInd, short firstInd,
+ int addBevel3D(int fi0, int fi1, int pi0 ,int pi1, int index, short[] lastInd,
boolean constStroke) {
IndexCache cache = tess.lineIndexCache;
int count = cache.vertexCount[index];
- boolean addBevel = lastInd != null && -1 < lastInd[0] && -1 < lastInd[1];
boolean newCache = false;
- if (PGL.MAX_VERTEX_INDEX1 <= count + (addBevel ? 1 : 0)) {
+ if (PGL.MAX_VERTEX_INDEX1 <= count + 3) {
// We need to start a new index block for this line.
index = cache.addNew();
count = 0;
newCache = true;
}
+
int iidx = cache.indexOffset[index] + cache.indexCount[index];
int vidx = cache.vertexOffset[index] + cache.vertexCount[index];
- int color0 = constStroke ? strokeColor : strokeColors[i0];
+ int color = constStroke ? strokeColor : strokeColors[fi0];
+ float weight = constStroke ? strokeWeight : strokeWeights[fi0];
+ weight *= transformScale();
- if (lastInd != null) {
- if (-1 < lastInd[0] && -1 < lastInd[1]) {
- tess.setLineVertex(vidx, strokeVertices, i0, color0);
+ tess.setLineVertex(vidx++, strokeVertices, fi0, color);
+ tess.setLineVertex(vidx++, strokeVertices, fi0, fi1, color, +weight/2);
+ tess.setLineVertex(vidx++, strokeVertices, fi0, fi1, color, -weight/2);
- if (newCache) {
- PGraphics.showWarning(TOO_LONG_STROKE_PATH_ERROR);
-
- // TODO: Fix this situation, the vertices from the previous cache
- // block should be copied in the newly created one.
-// tess.lineIndices[iidx++] = (short) (count + 4);
-// tess.lineIndices[iidx++] = (short) (count + 0);
-// tess.lineIndices[iidx++] = (short) (count + 0);
-//
-// tess.lineIndices[iidx++] = (short) (count + 4);
-// tess.lineIndices[iidx++] = (short) (count + 1);
-// tess.lineIndices[iidx ] = (short) (count + 1);
- } else {
- tess.lineIndices[iidx++] = (short) (count + 0);
- tess.lineIndices[iidx++] = lastInd[0];
- tess.lineIndices[iidx++] = (short) (firstInd + 0);
+ int extra = 0;
+ if (newCache && -1 < pi0 && -1 < pi1) {
+ // Vertices used in the previous cache need to be copied to the
+ // newly created one
+ color = constStroke ? strokeColor : strokeColors[pi1];
+ weight = constStroke ? strokeWeight : strokeWeights[pi1];
+ weight *= transformScale();
- tess.lineIndices[iidx++] = (short) (count + 0);
- tess.lineIndices[iidx++] = lastInd[1];
- tess.lineIndices[iidx ] = (short) (firstInd + 1);
- }
+ tess.setLineVertex(vidx++, strokeVertices, pi1, pi0, color, -weight/2);
+ tess.setLineVertex(vidx , strokeVertices, pi1, pi0, color, +weight/2);
- cache.incCounts(index, 6, 1);
- }
+ lastInd[0] = (short) (count + 3);
+ lastInd[1] = (short) (count + 4);
+ extra = 2;
}
+ tess.lineIndices[iidx++] = (short) (count + 0);
+ tess.lineIndices[iidx++] = lastInd[0];
+ tess.lineIndices[iidx++] = (short) (count + 1);
+
+ tess.lineIndices[iidx++] = (short) (count + 0);
+ tess.lineIndices[iidx++] = (short) (count + 2);
+ tess.lineIndices[iidx ] = lastInd[1];
+
+ cache.incCounts(index, 6, 3 + extra);
+
return index;
}
@@ -10909,28 +12670,7 @@ boolean noCapsJoins() {
float transformScale() {
if (-1 < transformScale) return transformScale;
-
- // Volumetric scaling factor that is associated to the current
- // transformation matrix, which is given by the absolute value of its
- // determinant:
- float factor = 1;
-
- if (transform != null) {
- if (transform instanceof PMatrix2D) {
- PMatrix2D tr = (PMatrix2D)transform;
- float areaScaleFactor = Math.abs(tr.m00 * tr.m11 - tr.m01 * tr.m10);
- factor = (float) Math.sqrt(areaScaleFactor);
- } else if (transform instanceof PMatrix3D) {
- PMatrix3D tr = (PMatrix3D)transform;
- float volumeScaleFactor =
- Math.abs(tr.m00 * (tr.m11 * tr.m22 - tr.m12 * tr.m21) +
- tr.m01 * (tr.m12 * tr.m20 - tr.m10 * tr.m22) +
- tr.m02 * (tr.m10 * tr.m21 - tr.m11 * tr.m20));
- factor = (float) Math.pow(volumeScaleFactor, 1.0f / 3.0f);
- }
- }
-
- return transformScale = factor;
+ return transformScale = matrixScale(transform);
}
boolean segmentIsAxisAligned(int i0, int i1) {
@@ -11222,7 +12962,7 @@ void splitRawIndices(boolean clamp) {
IndexCache cache = tess.polyIndexCache;
// In retained mode, each shape has with its own cache item, since
- // they should always be available to be rendererd individually, even
+ // they should always be available to be rendered individually, even
// if contained in a larger hierarchy.
int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast();
firstPolyIndexCache = index;
@@ -11413,8 +13153,8 @@ void setLastTexIndex(int lastIndex, int lastCache) {
} else {
texCache.setLastIndex(lastIndex, lastCache);
}
+ prevTexImage = newTexImage;
}
- prevTexImage = newTexImage;
}
// -----------------------------------------------------------------
@@ -11521,43 +13261,7 @@ void addBezierVertex(int i) {
strokeWeight = in.strokeWeights[i];
}
- int fcol = 0, fa = 0, fr = 0, fg = 0, fb = 0;
- int acol = 0, aa = 0, ar = 0, ag = 0, ab = 0;
- int scol = 0, sa = 0, sr = 0, sg = 0, sb = 0;
- int ecol = 0, ea = 0, er = 0, eg = 0, eb = 0;
- float nx = 0, ny = 0, nz = 0, u = 0, v = 0, sh = 0;
- if (fill) {
- fcol = in.colors[i];
- fa = (fcol >> 24) & 0xFF;
- fr = (fcol >> 16) & 0xFF;
- fg = (fcol >> 8) & 0xFF;
- fb = (fcol >> 0) & 0xFF;
-
- acol = in.ambient[i];
- aa = (acol >> 24) & 0xFF;
- ar = (acol >> 16) & 0xFF;
- ag = (acol >> 8) & 0xFF;
- ab = (acol >> 0) & 0xFF;
-
- scol = in.specular[i];
- sa = (scol >> 24) & 0xFF;
- sr = (scol >> 16) & 0xFF;
- sg = (scol >> 8) & 0xFF;
- sb = (scol >> 0) & 0xFF;
-
- ecol = in.emissive[i];
- ea = (ecol >> 24) & 0xFF;
- er = (ecol >> 16) & 0xFF;
- eg = (ecol >> 8) & 0xFF;
- eb = (ecol >> 0) & 0xFF;
-
- nx = in.normals[3*i + 0];
- ny = in.normals[3*i + 1];
- nz = in.normals[3*i + 2];
- u = in.texcoords[2*i + 0];
- v = in.texcoords[2*i + 1];
- sh = in.shininess[i];
- }
+ double[] vertexT = fill ? collectVertexAttributes(i) : null;
float x2 = in.vertices[3*i + 0];
float y2 = in.vertices[3*i + 1];
@@ -11586,12 +13290,10 @@ void addBezierVertex(int i) {
y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
if (fill) {
- double[] vertex = new double[] {
- x1, y1, z1,
- fa, fr, fg, fb,
- nx, ny, nz,
- u, v,
- aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh};
+ double[] vertex = Arrays.copyOf(vertexT, vertexT.length);
+ vertex[0] = x1;
+ vertex[1] = y1;
+ vertex[2] = z1;
gluTess.addVertex(vertex);
}
if (stroke) addStrokeVertex(x1, y1, z1, strokeColor, strokeWeight);
@@ -11617,43 +13319,7 @@ void addQuadraticVertex(int i) {
strokeWeight = in.strokeWeights[i];
}
- int fcol = 0, fa = 0, fr = 0, fg = 0, fb = 0;
- int acol = 0, aa = 0, ar = 0, ag = 0, ab = 0;
- int scol = 0, sa = 0, sr = 0, sg = 0, sb = 0;
- int ecol = 0, ea = 0, er = 0, eg = 0, eb = 0;
- float nx = 0, ny = 0, nz = 0, u = 0, v = 0, sh = 0;
- if (fill) {
- fcol = in.colors[i];
- fa = (fcol >> 24) & 0xFF;
- fr = (fcol >> 16) & 0xFF;
- fg = (fcol >> 8) & 0xFF;
- fb = (fcol >> 0) & 0xFF;
-
- acol = in.ambient[i];
- aa = (acol >> 24) & 0xFF;
- ar = (acol >> 16) & 0xFF;
- ag = (acol >> 8) & 0xFF;
- ab = (acol >> 0) & 0xFF;
-
- scol = in.specular[i];
- sa = (scol >> 24) & 0xFF;
- sr = (scol >> 16) & 0xFF;
- sg = (scol >> 8) & 0xFF;
- sb = (scol >> 0) & 0xFF;
-
- ecol = in.emissive[i];
- ea = (ecol >> 24) & 0xFF;
- er = (ecol >> 16) & 0xFF;
- eg = (ecol >> 8) & 0xFF;
- eb = (ecol >> 0) & 0xFF;
-
- nx = in.normals[3*i + 0];
- ny = in.normals[3*i + 1];
- nz = in.normals[3*i + 2];
- u = in.texcoords[2*i + 0];
- v = in.texcoords[2*i + 1];
- sh = in.shininess[i];
- }
+ double[] vertexT = fill ? collectVertexAttributes(i) : null;
float cx = in.vertices[3*i + 0];
float cy = in.vertices[3*i + 1];
@@ -11689,12 +13355,10 @@ void addQuadraticVertex(int i) {
y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
if (fill) {
- double[] vertex = new double[] {
- x1, y1, z1,
- fa, fr, fg, fb,
- nx, ny, nz,
- u, v,
- aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh};
+ double[] vertex = Arrays.copyOf(vertexT, vertexT.length);
+ vertex[0] = x1;
+ vertex[1] = y1;
+ vertex[2] = z1;
gluTess.addVertex(vertex);
}
if (stroke) addStrokeVertex(x1, y1, z1, strokeColor, strokeWeight);
@@ -11711,6 +13375,10 @@ void addCurveVertex(int i) {
pg.curveVertexCount++;
// draw a segment if there are enough points
+ if (pg.curveVertexCount == 3) {
+ float[] v = pg.curveVertices[pg.curveVertexCount - 2];
+ addCurveInitialVertex(i, v[X], v[Y], v[Z]);
+ }
if (pg.curveVertexCount > 3) {
float[] v1 = pg.curveVertices[pg.curveVertexCount - 4];
float[] v2 = pg.curveVertices[pg.curveVertexCount - 3];
@@ -11723,6 +13391,19 @@ void addCurveVertex(int i) {
}
}
+ void addCurveInitialVertex(int i, float x, float y, float z) {
+ if (fill) {
+ double[] vertex0 = collectVertexAttributes(i);
+ vertex0[0] = x;
+ vertex0[1] = y;
+ vertex0[2] = z;
+ gluTess.addVertex(vertex0);
+ }
+ if (stroke) {
+ addStrokeVertex(x, y, z, in.strokeColors[i], strokeWeight);
+ }
+ }
+
void addCurveVertexSegment(int i, float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3,
@@ -11734,43 +13415,7 @@ void addCurveVertexSegment(int i, float x1, float y1, float z1,
strokeWeight = in.strokeWeights[i];
}
- int fcol = 0, fa = 0, fr = 0, fg = 0, fb = 0;
- int acol = 0, aa = 0, ar = 0, ag = 0, ab = 0;
- int scol = 0, sa = 0, sr = 0, sg = 0, sb = 0;
- int ecol = 0, ea = 0, er = 0, eg = 0, eb = 0;
- float nx = 0, ny = 0, nz = 0, u = 0, v = 0, sh = 0;
- if (fill) {
- fcol = in.colors[i];
- fa = (fcol >> 24) & 0xFF;
- fr = (fcol >> 16) & 0xFF;
- fg = (fcol >> 8) & 0xFF;
- fb = (fcol >> 0) & 0xFF;
-
- acol = in.ambient[i];
- aa = (acol >> 24) & 0xFF;
- ar = (acol >> 16) & 0xFF;
- ag = (acol >> 8) & 0xFF;
- ab = (acol >> 0) & 0xFF;
-
- scol = in.specular[i];
- sa = (scol >> 24) & 0xFF;
- sr = (scol >> 16) & 0xFF;
- sg = (scol >> 8) & 0xFF;
- sb = (scol >> 0) & 0xFF;
-
- ecol = in.emissive[i];
- ea = (ecol >> 24) & 0xFF;
- er = (ecol >> 16) & 0xFF;
- eg = (ecol >> 8) & 0xFF;
- eb = (ecol >> 0) & 0xFF;
-
- nx = in.normals[3*i + 0];
- ny = in.normals[3*i + 1];
- nz = in.normals[3*i + 2];
- u = in.texcoords[2*i + 0];
- v = in.texcoords[2*i + 1];
- sh = in.shininess[i];
- }
+ double[] vertexT = fill ? collectVertexAttributes(i) : null;
float x = x2;
float y = y2;
@@ -11790,28 +13435,15 @@ void addCurveVertexSegment(int i, float x1, float y1, float z1,
float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4;
float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4;
- if (fill) {
- double[] vertex0 = new double[] {
- x, y, z,
- fa, fr, fg, fb,
- nx, ny, nz,
- u, v,
- aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh};
- gluTess.addVertex(vertex0);
- }
- if (stroke) addStrokeVertex(x, y, z, strokeColor, strokeWeight);
-
for (int j = 0; j < pg.curveDetail; j++) {
x += xplot1; xplot1 += xplot2; xplot2 += xplot3;
y += yplot1; yplot1 += yplot2; yplot2 += yplot3;
z += zplot1; zplot1 += zplot2; zplot2 += zplot3;
if (fill) {
- double[] vertex1 = new double[] {
- x, y, z,
- fa, fr, fg, fb,
- nx, ny, nz,
- u, v,
- aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh};
+ double[] vertex1 = Arrays.copyOf(vertexT, vertexT.length);
+ vertex1[0] = x;
+ vertex1[1] = y;
+ vertex1[2] = z;
gluTess.addVertex(vertex1);
}
if (stroke) addStrokeVertex(x, y, z, strokeColor, strokeWeight);
@@ -11825,55 +13457,64 @@ void addVertex(int i) {
float y = in.vertices[3*i + 1];
float z = in.vertices[3*i + 2];
- int strokeColor = 0;
- float strokeWeight = 0;
- if (stroke) {
- strokeColor = in.strokeColors[i];
- strokeWeight = in.strokeWeights[i];
- }
-
if (fill) {
- // Separating colors into individual rgba components for interpolation.
- int fcol = in.colors[i];
- int fa = (fcol >> 24) & 0xFF;
- int fr = (fcol >> 16) & 0xFF;
- int fg = (fcol >> 8) & 0xFF;
- int fb = (fcol >> 0) & 0xFF;
-
- int acol = in.ambient[i];
- int aa = (acol >> 24) & 0xFF;
- int ar = (acol >> 16) & 0xFF;
- int ag = (acol >> 8) & 0xFF;
- int ab = (acol >> 0) & 0xFF;
-
- int scol = in.specular[i];
- int sa = (scol >> 24) & 0xFF;
- int sr = (scol >> 16) & 0xFF;
- int sg = (scol >> 8) & 0xFF;
- int sb = (scol >> 0) & 0xFF;
-
- int ecol = in.emissive[i];
- int ea = (ecol >> 24) & 0xFF;
- int er = (ecol >> 16) & 0xFF;
- int eg = (ecol >> 8) & 0xFF;
- int eb = (ecol >> 0) & 0xFF;
-
- float nx = in.normals[3*i + 0];
- float ny = in.normals[3*i + 1];
- float nz = in.normals[3*i + 2];
- float u = in.texcoords[2*i + 0];
- float v = in.texcoords[2*i + 1];
- float sh = in.shininess[i];
-
- double[] vertex = new double[] {
- x, y, z,
- fa, fr, fg, fb,
- nx, ny, nz,
- u, v,
- aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, sh};
+ double[] vertex = collectVertexAttributes(i);
+ vertex[0] = x;
+ vertex[1] = y;
+ vertex[2] = z;
gluTess.addVertex(vertex);
}
- if (stroke) addStrokeVertex(x, y, z, strokeColor, strokeWeight);
+ if (stroke) {
+ addStrokeVertex(x, y, z, in.strokeColors[i], in.strokeWeights[i]);
+ }
+ }
+
+ double[] collectVertexAttributes(int i) {
+ final int COORD_COUNT = 3;
+ final int ATTRIB_COUNT = 22;
+
+ double[] avect = in.getAttribVector(i);
+
+ double[] r = new double[COORD_COUNT + ATTRIB_COUNT + avect.length];
+
+ int j = COORD_COUNT;
+
+ int fcol = in.colors[i];
+ r[j++] = (fcol >> 24) & 0xFF; // fa
+ r[j++] = (fcol >> 16) & 0xFF; // fr
+ r[j++] = (fcol >> 8) & 0xFF; // fg
+ r[j++] = (fcol >> 0) & 0xFF; // fb
+
+ r[j++] = in.normals[3*i + 0]; // nx
+ r[j++] = in.normals[3*i + 1]; // ny
+ r[j++] = in.normals[3*i + 2]; // nz
+
+ r[j++] = in.texcoords[2*i + 0]; // u
+ r[j++] = in.texcoords[2*i + 1]; // v
+
+ int acol = in.ambient[i];
+ r[j++] = (acol >> 24) & 0xFF; // aa
+ r[j++] = (acol >> 16) & 0xFF; // ar
+ r[j++] = (acol >> 8) & 0xFF; // ag
+ r[j++] = (acol >> 0) & 0xFF; // ab
+
+ int scol = in.specular[i];
+ r[j++] = (scol >> 24) & 0xFF; // sa
+ r[j++] = (scol >> 16) & 0xFF; // sr
+ r[j++] = (scol >> 8) & 0xFF; // sg
+ r[j++] = (scol >> 0) & 0xFF; // sb
+
+ int ecol = in.emissive[i];
+ r[j++] = (ecol >> 24) & 0xFF; // ea
+ r[j++] = (ecol >> 16) & 0xFF; // er
+ r[j++] = (ecol >> 8) & 0xFF; // eg
+ r[j++] = (ecol >> 0) & 0xFF; // eb
+
+ r[j++] = in.shininess[i]; // sh
+
+ System.arraycopy(avect, 0, r, j, avect.length);
+
+ return r;
}
void beginPolygonStroke() {
@@ -12040,6 +13681,7 @@ boolean clampLinePath() {
// http://code.google.com/p/glues/
// to eventually come up with an optimized GLU tessellator in native code.
protected class TessellatorCallback implements PGL.TessellatorCallback {
+ AttributeMap attribs;
boolean calcNormals;
boolean strokeTess;
boolean clampXY;
@@ -12050,6 +13692,10 @@ protected class TessellatorCallback implements PGL.TessellatorCallback {
int vertOffset;
int primitive;
+ public TessellatorCallback(AttributeMap attribs) {
+ this.attribs = attribs;
+ }
+
public void init(boolean addCache, boolean strokeTess, boolean calcNorm,
boolean clampXY) {
this.strokeTess = strokeTess;
@@ -12160,28 +13806,12 @@ public void vertex(Object data) {
double[] d = (double[]) data;
int l = d.length;
if (l < 25) {
- throw new RuntimeException("TessCallback vertex() data is not " +
- "of length 25");
+ throw new RuntimeException("TessCallback vertex() data is " +
+ "too small");
}
if (vertCount < PGL.MAX_VERTEX_INDEX1) {
- // Combining individual rgba components back into int color values
- int fcolor =
- ((int)d[ 3]<<24) | ((int)d[ 4]<<16) | ((int)d[ 5]<<8) | (int)d[ 6];
- int acolor =
- ((int)d[12]<<24) | ((int)d[13]<<16) | ((int)d[14]<<8) | (int)d[15];
- int scolor =
- ((int)d[16]<<24) | ((int)d[17]<<16) | ((int)d[18]<<8) | (int)d[19];
- int ecolor =
- ((int)d[20]<<24) | ((int)d[21]<<16) | ((int)d[22]<<8) | (int)d[23];
-
- tess.addPolyVertex((float) d[ 0], (float) d[ 1], (float) d[ 2],
- fcolor,
- (float) d[ 7], (float) d[ 8], (float) d[ 9],
- (float) d[10], (float) d[11],
- acolor, scolor, ecolor,
- (float) d[24], clampXY);
-
+ tess.addPolyVertex(d, clampXY);
vertCount++;
} else {
throw new RuntimeException("The tessellator is generating too " +
@@ -12215,7 +13845,8 @@ public void error(int errnum) {
*/
public void combine(double[] coords, Object[] data,
float[] weight, Object[] outData) {
- double[] vertex = new double[25 + 8];
+ int n = ((double[])data[0]).length;
+ double[] vertex = new double[n];
vertex[0] = coords[0];
vertex[1] = coords[1];
vertex[2] = coords[2];
@@ -12223,7 +13854,7 @@ public void combine(double[] coords, Object[] data,
// Calculating the rest of the vertex parameters (color,
// normal, texcoords) as the linear combination of the
// combined vertices.
- for (int i = 3; i < 25; i++) {
+ for (int i = 3; i < n; i++) {
vertex[i] = 0;
for (int j = 0; j < 4; j++) {
double[] vertData = (double[])data[j];
@@ -12233,19 +13864,417 @@ public void combine(double[] coords, Object[] data,
}
}
- // Normalizing normal vector, since the weighted
+ // Normalizing normal vectors, since the weighted
// combination of normal vectors is not necessarily
// normal.
- double sum = vertex[7] * vertex[7] +
- vertex[8] * vertex[8] +
- vertex[9] * vertex[9];
- double len = Math.sqrt(sum);
- vertex[7] /= len;
- vertex[8] /= len;
- vertex[9] /= len;
+ normalize(vertex, 7);
+ if (25 < n) {
+ // We have custom attributes, look for normal attributes
+ int pos = 25;
+ for (int i = 0; i < attribs.size(); i++) {
+ VertexAttribute attrib = attribs.get(i);
+ if (attrib.isNormal()) {
+ normalize(vertex, pos);
+ pos += 3;
+ } else {
+ pos += attrib.size;
+ }
+ }
+ }
outData[0] = vertex;
}
+
+ private void normalize(double[] v, int i) {
+ double sum = v[i ] * v[i ] +
+ v[i + 1] * v[i + 1] +
+ v[i + 2] * v[i + 2];
+ double len = Math.sqrt(sum);
+ if (0 < len) {
+ v[i ] /= len;
+ v[i + 1] /= len;
+ v[i + 2] /= len;
+ }
+ }
+ }
+ }
+
+
+ static protected class DepthSorter {
+
+ static final int X = 0;
+ static final int Y = 1;
+ static final int Z = 2;
+ static final int W = 3;
+
+ static final int X0 = 0;
+ static final int Y0 = 1;
+ static final int Z0 = 2;
+ static final int X1 = 3;
+ static final int Y1 = 4;
+ static final int Z1 = 5;
+ static final int X2 = 6;
+ static final int Y2 = 7;
+ static final int Z2 = 8;
+
+ int[] triangleIndices = new int[0];
+ int[] texMap = new int[0];
+ int[] voffsetMap = new int[0];
+
+ float[] minXBuffer = new float[0];
+ float[] minYBuffer = new float[0];
+ float[] minZBuffer = new float[0];
+ float[] maxXBuffer = new float[0];
+ float[] maxYBuffer = new float[0];
+ float[] maxZBuffer = new float[0];
+
+ float[] screenVertices = new float[0];
+
+ float[] triA = new float[9];
+ float[] triB = new float[9];
+
+ BitSet marked = new BitSet();
+ BitSet swapped = new BitSet();
+
+ PGraphicsOpenGL pg;
+
+ DepthSorter (PGraphicsOpenGL pg) {
+ this.pg = pg;
+ }
+
+ void checkIndexBuffers(int newTriangleCount) {
+ if (triangleIndices.length < newTriangleCount) {
+ int newSize = (newTriangleCount / 4 + 1) * 5;
+ triangleIndices = new int[newSize];
+ texMap = new int[newSize];
+ voffsetMap = new int[newSize];
+ minXBuffer = new float[newSize];
+ minYBuffer = new float[newSize];
+ minZBuffer = new float[newSize];
+ maxXBuffer = new float[newSize];
+ maxYBuffer = new float[newSize];
+ maxZBuffer = new float[newSize];
+ }
+ }
+
+ void checkVertexBuffer(int newVertexCount) {
+ int coordCount = 3*newVertexCount;
+ if (screenVertices.length < coordCount) {
+ int newSize = (coordCount / 4 + 1) * 5;
+ screenVertices = new float[newSize];
+ }
+ }
+
+ // Sorting --------------------------------------------
+
+ void sort(TessGeometry tessGeo) {
+
+ int triangleCount = tessGeo.polyIndexCount / 3;
+ checkIndexBuffers(triangleCount);
+ int[] triangleIndices = this.triangleIndices;
+ int[] texMap = this.texMap;
+ int[] voffsetMap = this.voffsetMap;
+
+ { // Initialize triangle indices
+ for (int i = 0; i < triangleCount; i++) {
+ triangleIndices[i] = i;
+ }
+ }
+
+ { // Map caches to triangles
+ TexCache texCache = pg.texCache;
+ IndexCache indexCache = tessGeo.polyIndexCache;
+ for (int i = 0; i < texCache.size; i++) {
+ int first = texCache.firstCache[i];
+ int last = texCache.lastCache[i];
+ for (int n = first; n <= last; n++) {
+ int ioffset = n == first
+ ? texCache.firstIndex[i]
+ : indexCache.indexOffset[n];
+ int icount = n == last
+ ? texCache.lastIndex[i] - ioffset + 1
+ : indexCache.indexOffset[n] + indexCache.indexCount[n] - ioffset;
+
+ for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) {
+ texMap[tr] = i;
+ voffsetMap[tr] = n;
+ }
+ }
+ }
+ }
+
+ { // Map vertices to screen
+ int polyVertexCount = tessGeo.polyVertexCount;
+ checkVertexBuffer(polyVertexCount);
+ float[] screenVertices = this.screenVertices;
+
+ float[] polyVertices = tessGeo.polyVertices;
+
+ PMatrix3D projection = pg.projection;
+
+ for (int i = 0; i < polyVertexCount; i++) {
+ float x = polyVertices[4*i+X];
+ float y = polyVertices[4*i+Y];
+ float z = polyVertices[4*i+Z];
+ float w = polyVertices[4*i+W];
+
+ float ox = projection.m00 * x + projection.m01 * y +
+ projection.m02 * z + projection.m03 * w;
+ float oy = projection.m10 * x + projection.m11 * y +
+ projection.m12 * z + projection.m13 * w;
+ float oz = projection.m20 * x + projection.m21 * y +
+ projection.m22 * z + projection.m23 * w;
+ float ow = projection.m30 * x + projection.m31 * y +
+ projection.m32 * z + projection.m33 * w;
+ if (nonZero(ow)) {
+ ox /= ow;
+ oy /= ow;
+ oz /= ow;
+ }
+ screenVertices[3*i+X] = ox;
+ screenVertices[3*i+Y] = oy;
+ screenVertices[3*i+Z] = -oz;
+ }
+ }
+ float[] screenVertices = this.screenVertices;
+
+ int[] vertexOffset = tessGeo.polyIndexCache.vertexOffset;
+ short[] polyIndices = tessGeo.polyIndices;
+
+ float[] triA = this.triA;
+ float[] triB = this.triB;
+
+ for (int i = 0; i < triangleCount; i++) {
+ fetchTriCoords(triA, i, vertexOffset, voffsetMap, screenVertices, polyIndices);
+ minXBuffer[i] = PApplet.min(triA[X0], triA[X1], triA[X2]);
+ maxXBuffer[i] = PApplet.max(triA[X0], triA[X1], triA[X2]);
+ minYBuffer[i] = PApplet.min(triA[Y0], triA[Y1], triA[Y2]);
+ maxYBuffer[i] = PApplet.max(triA[Y0], triA[Y1], triA[Y2]);
+ minZBuffer[i] = PApplet.min(triA[Z0], triA[Z1], triA[Z2]);
+ maxZBuffer[i] = PApplet.max(triA[Z0], triA[Z1], triA[Z2]);
+ }
+
+ sortByMinZ(0, triangleCount - 1, triangleIndices, minZBuffer);
+
+ int activeTid = 0;
+
+ BitSet marked = this.marked;
+ BitSet swapped = this.swapped;
+
+ marked.clear();
+
+ while (activeTid < triangleCount) {
+ int testTid = activeTid + 1;
+ boolean draw = false;
+
+ swapped.clear();
+
+ int ati = triangleIndices[activeTid];
+ float minXA = minXBuffer[ati];
+ float maxXA = maxXBuffer[ati];
+ float minYA = minYBuffer[ati];
+ float maxYA = maxYBuffer[ati];
+ float maxZA = maxZBuffer[ati];
+
+ fetchTriCoords(triA, ati, vertexOffset, voffsetMap, screenVertices, polyIndices);
+
+ while (!draw && testTid < triangleCount) {
+ int tti = triangleIndices[testTid];
+
+ // TEST 1 // Z overlap
+ if (maxZA <= minZBuffer[tti] && !marked.get(tti)) {
+ draw = true; // pass, not overlapping in Z, draw it
+
+ // TEST 2 // XY overlap using square window
+ } else if (maxXA <= minXBuffer[tti] || maxYA <= minYBuffer[tti] ||
+ minXA >= maxXBuffer[tti] || minYA >= maxYBuffer[tti]) {
+ testTid++; // pass, not overlapping in XY
+
+ // TEST 3 // test on which side ACTIVE is relative to TEST
+ } else {
+ fetchTriCoords(triB, tti, vertexOffset, voffsetMap,
+ screenVertices, polyIndices);
+ if (side(triB, triA, -1) > 0) {
+ testTid++; // pass, ACTIVE is in halfspace behind current TEST
+
+ // TEST 4 // test on which side TEST is relative to ACTIVE
+ } else if (side(triA, triB, 1) > 0) {
+ testTid++; // pass, current TEST is in halfspace in front of ACTIVE
+
+ // FAIL, wrong depth order, swap
+ } else {
+ if (!swapped.get(tti)) {
+ swapped.set(ati);
+ marked.set(tti);
+ rotateRight(triangleIndices, activeTid, testTid);
+
+ ati = tti;
+ System.arraycopy(triB, 0, triA, 0, 9);
+ minXA = minXBuffer[ati];
+ maxXA = maxXBuffer[ati];
+ minYA = minYBuffer[ati];
+ maxYA = maxYBuffer[ati];
+ maxZA = maxZBuffer[ati];
+
+ testTid = activeTid + 1;
+ } else {
+ // oops, we already tested this one, either in one plane or
+ // interlocked in loop with others, just ignore it for now :(
+ testTid++;
+ }
+ }
+ }
+ }
+ activeTid++;
+ }
+
+ { // Reorder the buffers
+ for (int id = 0; id < triangleCount; id++) {
+ int mappedId = triangleIndices[id];
+ if (id != mappedId) {
+
+ // put the first index aside
+ short i0 = polyIndices[3*id+0];
+ short i1 = polyIndices[3*id+1];
+ short i2 = polyIndices[3*id+2];
+ int texId = texMap[id];
+ int voffsetId = voffsetMap[id];
+
+ // process the whole permutation cycle
+ int currId = id;
+ int nextId = mappedId;
+ do {
+ triangleIndices[currId] = currId;
+ polyIndices[3*currId+0] = polyIndices[3*nextId+0];
+ polyIndices[3*currId+1] = polyIndices[3*nextId+1];
+ polyIndices[3*currId+2] = polyIndices[3*nextId+2];
+ texMap[currId] = texMap[nextId];
+ voffsetMap[currId] = voffsetMap[nextId];
+
+ currId = nextId;
+ nextId = triangleIndices[nextId];
+ } while (nextId != id);
+
+ // place the first index at the end
+ triangleIndices[currId] = currId;
+ polyIndices[3*currId+0] = i0;
+ polyIndices[3*currId+1] = i1;
+ polyIndices[3*currId+2] = i2;
+ texMap[currId] = texId;
+ voffsetMap[currId] = voffsetId;
+ }
+ }
+ }
+
+ }
+
+ static void fetchTriCoords(float[] tri, int ti, int[] vertexOffset,
+ int[] voffsetMap, float[] screenVertices, short[] polyIndices) {
+ int voffset = vertexOffset[voffsetMap[ti]];
+ int i0 = 3 * (voffset + polyIndices[3*ti+0]);
+ int i1 = 3 * (voffset + polyIndices[3*ti+1]);
+ int i2 = 3 * (voffset + polyIndices[3*ti+2]);
+ tri[X0] = screenVertices[i0+X];
+ tri[Y0] = screenVertices[i0+Y];
+ tri[Z0] = screenVertices[i0+Z];
+ tri[X1] = screenVertices[i1+X];
+ tri[Y1] = screenVertices[i1+Y];
+ tri[Z1] = screenVertices[i1+Z];
+ tri[X2] = screenVertices[i2+X];
+ tri[Y2] = screenVertices[i2+Y];
+ tri[Z2] = screenVertices[i2+Z];
+ }
+
+ static void sortByMinZ(int leftTid, int rightTid, int[] triangleIndices,
+ float[] minZBuffer) {
+
+ // swap pivot to the front
+ swap(triangleIndices, leftTid, ((leftTid + rightTid) / 2));
+
+ int k = leftTid;
+ float leftMinZ = minZBuffer[triangleIndices[leftTid]];
+
+ // sort by min z
+ for (int tid = leftTid+1; tid <= rightTid; tid++) {
+ float minZ = minZBuffer[triangleIndices[tid]];
+ if (minZ < leftMinZ) {
+ swap(triangleIndices, ++k, tid);
+ }
+ }
+
+ // swap pivot back to the middle
+ swap(triangleIndices, leftTid, k);
+
+ if (leftTid < k - 1) sortByMinZ(leftTid, k - 1, triangleIndices,
+ minZBuffer);
+ if (k + 1 < rightTid) sortByMinZ(k + 1, rightTid, triangleIndices,
+ minZBuffer);
+ }
+
+ // Math -----------------------------------------------
+
+ static int side(float[] tri1, float[] tri2, float tz) {
+ float Dx, Dy, Dz, Dw;
+ { // Get the equation of the plane
+ float
+ ABx = tri1[X1] - tri1[X0], ACx = tri1[X2] - tri1[X0],
+ ABy = tri1[Y1] - tri1[Y0], ACy = tri1[Y2] - tri1[Y0],
+ ABz = tri1[Z1] - tri1[Z0], ACz = tri1[Z2] - tri1[Z0];
+
+ Dx = ABy*ACz - ABz*ACy; Dy = ABz*ACx - ABx*ACz; Dz = ABx*ACy - ABy*ACx;
+
+ // Normalize normal vector
+ float rMag = 1.0f/(float) Math.sqrt(Dx * Dx + Dy * Dy + Dz * Dz);
+ Dx *= rMag; Dy *= rMag; Dz *= rMag;
+
+ Dw = -dot(Dx, Dy, Dz, tri1[X0], tri1[Y0], tri1[Z0]);
+ }
+
+ float distTest = dot(Dx, Dy, Dz,
+ tri1[X0], tri1[Y0], tri1[Z0] + 100*tz) + Dw;
+
+ float distA = dot(Dx, Dy, Dz, tri2[X0], tri2[Y0], tri2[Z0]) + Dw;
+ float distB = dot(Dx, Dy, Dz, tri2[X1], tri2[Y1], tri2[Z1]) + Dw;
+ float distC = dot(Dx, Dy, Dz, tri2[X2], tri2[Y2], tri2[Z2]) + Dw;
+
+ // Ignore relatively close vertices to get stable results
+ // when some parts of polygons are close to each other
+ float absA = PApplet.abs(distA);
+ float absB = PApplet.abs(distB);
+ float absC = PApplet.abs(distC);
+ float eps = PApplet.max(absA, absB, absC) * 0.1f;
+
+ float sideA = ((absA < eps) ? 0.0f : distA) * distTest;
+ float sideB = ((absB < eps) ? 0.0f : distB) * distTest;
+ float sideC = ((absC < eps) ? 0.0f : distC) * distTest;
+
+ boolean sameSide = sideA >= 0 && sideB >= 0 && sideC >= 0;
+ boolean notSameSide = sideA <= 0 && sideB <= 0 && sideC <= 0;
+
+ return sameSide ? 1 : notSameSide ? -1 : 0;
+ }
+
+ static float dot(float a1, float a2, float a3,
+ float b1, float b2, float b3) {
+ return a1 * b1 + a2 * b2 + a3 * b3;
}
+
+
+ // Array utils ---------------------------------------
+
+ static void swap(int[] array, int i1, int i2) {
+ int temp = array[i1];
+ array[i1] = array[i2];
+ array[i2] = temp;
+ }
+
+ static void rotateRight(int[] array, int i1, int i2) {
+ if (i1 == i2) return;
+ int temp = array[i2];
+ System.arraycopy(array, i1, array, i1 + 1, i2 - i1);
+ array[i1] = temp;
+ }
+
}
+
}
diff --git a/core/src/processing/opengl/PShader.java b/libs/processing-core/src/main/java/processing/opengl/PShader.java
similarity index 93%
rename from core/src/processing/opengl/PShader.java
rename to libs/processing-core/src/main/java/processing/opengl/PShader.java
index a6e37b84e..c3e47ead5 100644
--- a/core/src/processing/opengl/PShader.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PShader.java
@@ -3,12 +3,13 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-13 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -24,6 +25,7 @@
package processing.opengl;
import processing.core.*;
+import processing.opengl.PGraphicsOpenGL.GLResourceShader;
import java.net.URL;
import java.nio.FloatBuffer;
@@ -49,8 +51,12 @@ public class PShader implements PConstants {
static protected String pointShaderAttrRegexp =
"attribute *vec2 *offset";
+ static protected String pointShaderInRegexp =
+ "in *vec2 *offset;";
static protected String lineShaderAttrRegexp =
"attribute *vec4 *direction";
+ static protected String lineShaderInRegexp =
+ "in *vec4 *direction";
static protected String pointShaderDefRegexp =
"#define *PROCESSING_POINT_SHADER";
static protected String lineShaderDefRegexp =
@@ -88,6 +94,7 @@ public class PShader implements PConstants {
public int glProgram;
public int glVertex;
public int glFragment;
+ private GLResourceShader glres;
protected URL vertexURL;
protected URL fragmentURL;
@@ -119,6 +126,7 @@ public class PShader implements PConstants {
protected int ppixelsLoc;
protected int ppixelsUnit;
protected int viewportLoc;
+ protected int resolutionLoc;
// Uniforms only for lines and points
protected int perspectiveLoc;
@@ -303,27 +311,9 @@ public PShader(PApplet parent, String[] vertSource, String[] fragSource) {
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (glVertex != 0) {
- PGraphicsOpenGL.finalizeGLSLVertShaderObject(glVertex, context);
- }
- if (glFragment != 0) {
- PGraphicsOpenGL.finalizeGLSLFragShaderObject(glFragment, context);
- }
- if (glProgram != 0) {
- PGraphicsOpenGL.finalizeGLSLProgramObject(glProgram, context);
- }
- } finally {
- super.finalize();
- }
- }
-
-
public void setVertexShader(String vertFilename) {
this.vertexFilename = vertFilename;
- vertexShaderSource = pgl.loadFragmentShader(vertFilename);
+ vertexShaderSource = pgl.loadVertexShader(vertFilename);
}
@@ -340,13 +330,13 @@ public void setVertexShader(String[] vertSource) {
public void setFragmentShader(String fragFilename) {
this.fragmentFilename = fragFilename;
- fragmentShaderSource = pgl.loadVertexShader(fragFilename);
+ fragmentShaderSource = pgl.loadFragmentShader(fragFilename);
}
public void setFragmentShader(URL fragURL) {
this.fragmentURL = fragURL;
- fragmentShaderSource = pgl.loadVertexShader(fragURL);
+ fragmentShaderSource = pgl.loadFragmentShader(fragURL);
}
public void setFragmentShader(String[] fragSource) {
@@ -753,7 +743,8 @@ protected void setUniformImpl(String name, int type, Object value) {
uniformValues.put(loc, new UniformValue(type, value));
} else {
PGraphics.showWarning("The shader doesn't have a uniform called \"" +
- name + "\"");
+ name + "\" OR the uniform was removed during " +
+ "compilation because it was unused.");
}
}
@@ -896,61 +887,71 @@ protected void unbindTextures() {
}
}
- protected void init() {
- if (glProgram == 0 || contextIsOutdated()) {
- context = pgl.getCurrentContext();
- glProgram = PGraphicsOpenGL.createGLSLProgramObject(context, pgl);
- boolean vertRes = true;
- if (hasVertexShader()) {
- vertRes = compileVertexShader();
- } else {
- PGraphics.showException("Doesn't have a vertex shader");
- }
-
- boolean fragRes = true;
- if (hasFragmentShader()) {
- fragRes = compileFragmentShader();
- } else {
- PGraphics.showException("Doesn't have a fragment shader");
- }
-
- if (vertRes && fragRes) {
+ public void init() {
+ if (glProgram == 0 || contextIsOutdated()) {
+ create();
+ if (compile()) {
pgl.attachShader(glProgram, glVertex);
pgl.attachShader(glProgram, glFragment);
+
setup();
pgl.linkProgram(glProgram);
- pgl.getProgramiv(glProgram, PGL.LINK_STATUS, intBuffer);
- boolean linked = intBuffer.get(0) == 0 ? false : true;
- if (!linked) {
- PGraphics.showException("Cannot link shader program:\n" +
- pgl.getProgramInfoLog(glProgram));
- }
-
- pgl.validateProgram(glProgram);
- pgl.getProgramiv(glProgram, PGL.VALIDATE_STATUS, intBuffer);
- boolean validated = intBuffer.get(0) == 0 ? false : true;
- if (!validated) {
- PGraphics.showException("Cannot validate shader program:\n" +
- pgl.getProgramInfoLog(glProgram));
- }
+ validate();
}
}
}
+ protected void create() {
+ context = pgl.getCurrentContext();
+ glres = new GLResourceShader(this);
+ }
+
+
+ protected boolean compile() {
+ boolean vertRes = true;
+ if (hasVertexShader()) {
+ vertRes = compileVertexShader();
+ } else {
+ PGraphics.showException("Doesn't have a vertex shader");
+ }
+
+ boolean fragRes = true;
+ if (hasFragmentShader()) {
+ fragRes = compileFragmentShader();
+ } else {
+ PGraphics.showException("Doesn't have a fragment shader");
+ }
+
+ return vertRes && fragRes;
+ }
+
+
+ protected void validate() {
+ pgl.getProgramiv(glProgram, PGL.LINK_STATUS, intBuffer);
+ boolean linked = intBuffer.get(0) == 0 ? false : true;
+ if (!linked) {
+ PGraphics.showException("Cannot link shader program:\n" +
+ pgl.getProgramInfoLog(glProgram));
+ }
+
+ pgl.validateProgram(glProgram);
+ pgl.getProgramiv(glProgram, PGL.VALIDATE_STATUS, intBuffer);
+ boolean validated = intBuffer.get(0) == 0 ? false : true;
+ if (!validated) {
+ PGraphics.showException("Cannot validate shader program:\n" +
+ pgl.getProgramInfoLog(glProgram));
+ }
+ }
+
+
protected boolean contextIsOutdated() {
boolean outdated = !pgl.contextIsCurrent(context);
if (outdated) {
- PGraphicsOpenGL.removeGLSLProgramObject(glProgram, context);
- PGraphicsOpenGL.removeGLSLVertShaderObject(glVertex, context);
- PGraphicsOpenGL.removeGLSLFragShaderObject(glFragment, context);
-
- glProgram = 0;
- glVertex = 0;
- glFragment = 0;
+ dispose();
}
return outdated;
}
@@ -961,16 +962,16 @@ protected boolean hasVertexShader() {
return vertexShaderSource != null && 0 < vertexShaderSource.length;
}
+
protected boolean hasFragmentShader() {
return fragmentShaderSource != null && 0 < fragmentShaderSource.length;
}
+
/**
* @param shaderSource a string containing the shader's code
*/
protected boolean compileVertexShader() {
- glVertex = PGraphicsOpenGL.createGLSLVertShaderObject(context, pgl);
-
pgl.shaderSource(glVertex, PApplet.join(vertexShaderSource, "\n"));
pgl.compileShader(glVertex);
@@ -990,8 +991,6 @@ protected boolean compileVertexShader() {
* @param shaderSource a string containing the shader's code
*/
protected boolean compileFragmentShader() {
- glFragment = PGraphicsOpenGL.createGLSLFragShaderObject(context, pgl);
-
pgl.shaderSource(glFragment, PApplet.join(fragmentShaderSource, "\n"));
pgl.compileShader(glFragment);
@@ -1008,32 +1007,21 @@ protected boolean compileFragmentShader() {
protected void dispose() {
- if (glVertex != 0) {
- PGraphicsOpenGL.deleteGLSLVertShaderObject(glVertex, context, pgl);
+ if (glres != null) {
+ glres.dispose();
glVertex = 0;
- }
- if (glFragment != 0) {
- PGraphicsOpenGL.deleteGLSLFragShaderObject(glFragment, context, pgl);
glFragment = 0;
- }
- if (glProgram != 0) {
- PGraphicsOpenGL.deleteGLSLProgramObject(glProgram, context, pgl);
glProgram = 0;
+ glres = null;
}
}
+
static protected int getShaderType(String[] source, int defaultType) {
for (int i = 0; i < source.length; i++) {
String line = source[i].trim();
- if (PApplet.match(line, pointShaderAttrRegexp) != null)
- return PShader.POINT;
- else if (PApplet.match(line, lineShaderAttrRegexp) != null)
- return PShader.LINE;
- else if (PApplet.match(line, pointShaderDefRegexp) != null)
- return PShader.POINT;
- else if (PApplet.match(line, lineShaderDefRegexp) != null)
- return PShader.LINE;
- else if (PApplet.match(line, colorShaderDefRegexp) != null)
+
+ if (PApplet.match(line, colorShaderDefRegexp) != null)
return PShader.COLOR;
else if (PApplet.match(line, lightShaderDefRegexp) != null)
return PShader.LIGHT;
@@ -1047,6 +1035,18 @@ else if (PApplet.match(line, triShaderAttrRegexp) != null)
return PShader.POLY;
else if (PApplet.match(line, quadShaderAttrRegexp) != null)
return PShader.POLY;
+ else if (PApplet.match(line, pointShaderDefRegexp) != null)
+ return PShader.POINT;
+ else if (PApplet.match(line, lineShaderDefRegexp) != null)
+ return PShader.LINE;
+ else if (PApplet.match(line, pointShaderAttrRegexp) != null)
+ return PShader.POINT;
+ else if (PApplet.match(line, pointShaderInRegexp) != null)
+ return PShader.POINT;
+ else if (PApplet.match(line, lineShaderAttrRegexp) != null)
+ return PShader.LINE;
+ else if (PApplet.match(line, lineShaderInRegexp) != null)
+ return PShader.LINE;
}
return defaultType;
}
@@ -1158,6 +1158,7 @@ protected void loadUniforms() {
projectionMatLoc = getUniformLoc("projectionMatrix");
viewportLoc = getUniformLoc("viewport");
+ resolutionLoc = getUniformLoc("resolution");
ppixelsLoc = getUniformLoc("ppixels");
normalMatLoc = getUniformLoc("normalMatrix");
@@ -1209,6 +1210,12 @@ protected void setCommonUniforms() {
setUniformValue(viewportLoc, x, y, w, h);
}
+ if (-1 < resolutionLoc) {
+ float w = currentPG.viewport.get(2);
+ float h = currentPG.viewport.get(3);
+ setUniformValue(resolutionLoc, w, h);
+ }
+
if (-1 < ppixelsLoc) {
ppixelsUnit = getLastTexUnit() + 1;
setUniformValue(ppixelsLoc, ppixelsUnit);
@@ -1219,6 +1226,7 @@ protected void setCommonUniforms() {
}
}
+
protected void bindTyped() {
if (currentPG == null) {
setRenderer(primaryPG.getCurrentPG());
@@ -1305,7 +1313,7 @@ protected void unbindTyped() {
if (-1 < normalLoc) pgl.disableVertexAttribArray(normalLoc);
if (-1 < ppixelsLoc) {
- pgl.requestFBOLayer();
+ pgl.enableFBOLayer();
pgl.activeTexture(PGL.TEXTURE0 + ppixelsUnit);
currentPG.unbindFrontTexture();
pgl.activeTexture(PGL.TEXTURE0);
@@ -1437,7 +1445,7 @@ protected void setPointAttribute(int vboId, int size, int type,
//
// Class to store a user-specified value for a uniform parameter
// in the shader
- protected class UniformValue {
+ protected static class UniformValue {
static final int INT1 = 0;
static final int INT2 = 1;
static final int INT3 = 2;
diff --git a/core/src/processing/opengl/PShapeOpenGL.java b/libs/processing-core/src/main/java/processing/opengl/PShapeOpenGL.java
similarity index 77%
rename from core/src/processing/opengl/PShapeOpenGL.java
rename to libs/processing-core/src/main/java/processing/opengl/PShapeOpenGL.java
index a771ff053..3f65727b4 100644
--- a/core/src/processing/opengl/PShapeOpenGL.java
+++ b/libs/processing-core/src/main/java/processing/opengl/PShapeOpenGL.java
@@ -3,7 +3,9 @@
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-12 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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
@@ -31,14 +33,16 @@
import processing.core.PMatrix3D;
import processing.core.PShape;
import processing.core.PVector;
+import processing.opengl.PGraphicsOpenGL.AttributeMap;
import processing.opengl.PGraphicsOpenGL.IndexCache;
import processing.opengl.PGraphicsOpenGL.InGeometry;
import processing.opengl.PGraphicsOpenGL.TessGeometry;
import processing.opengl.PGraphicsOpenGL.Tessellator;
+import processing.opengl.PGraphicsOpenGL.VertexAttribute;
+import java.nio.Buffer;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.Stack;
/**
* This class holds a 3D model composed of vertices, normals, colors
@@ -84,36 +88,39 @@ public class PShapeOpenGL extends PShape {
protected TessGeometry tessGeo;
protected Tessellator tessellator;
+ protected AttributeMap polyAttribs;
+
// ........................................................
// Texturing
protected HashSet textures;
protected boolean strokedTexture;
+ protected boolean untexChild;
// ........................................................
// OpenGL buffers
- public int glPolyVertex;
- public int glPolyColor;
- public int glPolyNormal;
- public int glPolyTexcoord;
- public int glPolyAmbient;
- public int glPolySpecular;
- public int glPolyEmissive;
- public int glPolyShininess;
- public int glPolyIndex;
-
- public int glLineVertex;
- public int glLineColor;
- public int glLineAttrib;
- public int glLineIndex;
-
- public int glPointVertex;
- public int glPointColor;
- public int glPointAttrib;
- public int glPointIndex;
+ protected VertexBuffer bufPolyVertex;
+ protected VertexBuffer bufPolyColor;
+ protected VertexBuffer bufPolyNormal;
+ protected VertexBuffer bufPolyTexcoord;
+ protected VertexBuffer bufPolyAmbient;
+ protected VertexBuffer bufPolySpecular;
+ protected VertexBuffer bufPolyEmissive;
+ protected VertexBuffer bufPolyShininess;
+ protected VertexBuffer bufPolyIndex;
+
+ protected VertexBuffer bufLineVertex;
+ protected VertexBuffer bufLineColor;
+ protected VertexBuffer bufLineAttrib;
+ protected VertexBuffer bufLineIndex;
+
+ protected VertexBuffer bufPointVertex;
+ protected VertexBuffer bufPointColor;
+ protected VertexBuffer bufPointAttrib;
+ protected VertexBuffer bufPointIndex;
// Testing this field, not use as it might go away...
public int glUsage = PGL.STATIC_DRAW;
@@ -163,7 +170,8 @@ public class PShapeOpenGL extends PShape {
// Geometric transformations.
protected PMatrix transform;
- protected Stack transformStack;
+ protected PMatrix transformInv;
+ protected PMatrix matrixInv;
// ........................................................
@@ -173,7 +181,7 @@ public class PShapeOpenGL extends PShape {
protected boolean needBufferInit = false;
// Flag to indicate if the shape can have holes or not.
- protected boolean solid;
+ protected boolean solid = true;
protected boolean breakShape = false;
protected boolean shapeCreated = false;
@@ -302,44 +310,46 @@ public class PShapeOpenGL extends PShape {
public PShapeOpenGL(PGraphicsOpenGL pg, int family) {
this.pg = pg;
+ this.family = family;
+
pgl = pg.pgl;
context = pgl.createEmptyContext();
- glPolyVertex = 0;
- glPolyColor = 0;
- glPolyNormal = 0;
- glPolyTexcoord = 0;
- glPolyAmbient = 0;
- glPolySpecular = 0;
- glPolyEmissive = 0;
- glPolyShininess = 0;
- glPolyIndex = 0;
-
- glLineVertex = 0;
- glLineColor = 0;
- glLineAttrib = 0;
- glLineIndex = 0;
-
- glPointVertex = 0;
- glPointColor = 0;
- glPointAttrib = 0;
- glPointIndex = 0;
-
- this.tessellator = PGraphicsOpenGL.tessellator;
- this.family = family;
+ bufPolyVertex = null;
+ bufPolyColor = null;
+ bufPolyNormal = null;
+ bufPolyTexcoord = null;
+ bufPolyAmbient = null;
+ bufPolySpecular = null;
+ bufPolyEmissive = null;
+ bufPolyShininess = null;
+ bufPolyIndex = null;
+
+ bufLineVertex = null;
+ bufLineColor = null;
+ bufLineAttrib = null;
+ bufLineIndex = null;
+
+ bufPointVertex = null;
+ bufPointColor = null;
+ bufPointAttrib = null;
+ bufPointIndex = null;
+
+ this.tessellator = pg.tessellator;
this.root = this;
this.parent = null;
this.tessellated = false;
if (family == GEOMETRY || family == PRIMITIVE || family == PATH) {
- inGeo = PGraphicsOpenGL.newInGeometry(pg, PGraphicsOpenGL.RETAINED);
+ polyAttribs = PGraphicsOpenGL.newAttributeMap();
+ inGeo = PGraphicsOpenGL.newInGeometry(pg, polyAttribs, PGraphicsOpenGL.RETAINED);
}
// Style parameters are retrieved from the current values in the renderer.
textureMode = pg.textureMode;
colorMode(pg.colorMode,
- pg.colorModeX, pg.colorModeY, pg.colorModeZ, pg.colorModeA);
+ pg.colorModeX, pg.colorModeY, pg.colorModeZ, pg.colorModeA);
// Initial values for fill, stroke and tint colors are also imported from
// the renderer. This is particular relevant for primitive shapes, since is
@@ -371,10 +381,8 @@ public PShapeOpenGL(PGraphicsOpenGL pg, int family) {
curveDetail = pg.curveDetail;
curveTightness = pg.curveTightness;
- // The rect and ellipse modes are set to CORNER since it is the expected
- // mode for svg shapes.
- rectMode = CORNER;
- ellipseMode = CORNER;
+ rectMode = pg.rectMode;
+ ellipseMode = pg.ellipseMode;
normalX = normalY = 0;
normalZ = 1;
@@ -389,6 +397,17 @@ public PShapeOpenGL(PGraphicsOpenGL pg, int family) {
// GROUP shapes are always marked as ended.
shapeCreated = true;
}
+
+ // OpenGL supports per-vertex coloring (unlike Java2D)
+ perVertexStyles = true;
+ }
+
+
+ /** Create a shape from the PRIMITIVE family, using this kind and these params */
+ public PShapeOpenGL(PGraphicsOpenGL pg, int kind, float... p) {
+ this(pg, PRIMITIVE);
+ setKind(kind);
+ setParams(p);
}
@@ -407,6 +426,8 @@ public void addChild(PShape who) {
for (PImage tex: c3d.textures) {
addTexture(tex);
}
+ } else {
+ untexChild(true);
}
if (c3d.strokedTexture) {
strokedTexture(true);
@@ -417,6 +438,8 @@ public void addChild(PShape who) {
if (c3d.stroke) {
strokedTexture(true);
}
+ } else {
+ untexChild(true);
}
}
@@ -444,6 +467,8 @@ public void addChild(PShape who, int idx) {
for (PImage tex: c3d.textures) {
addTexture(tex);
}
+ } else {
+ untexChild(true);
}
if (c3d.strokedTexture) {
strokedTexture(true);
@@ -454,6 +479,8 @@ public void addChild(PShape who, int idx) {
if (c3d.stroke) {
strokedTexture(true);
}
+ } else {
+ untexChild(true);
}
}
@@ -469,6 +496,8 @@ public void addChild(PShape who, int idx) {
@Override
public void removeChild(int idx) {
super.removeChild(idx);
+ strokedTexture(false);
+ untexChild(false);
markForTessellation();
}
@@ -484,113 +513,29 @@ protected void updateRoot(PShape root) {
}
- @Override
- protected void finalize() throws Throwable {
- try {
- finalizePolyBuffers();
- finalizeLineBuffers();
- finalizePointBuffers();
- } finally {
- super.finalize();
- }
- }
-
-
- protected void finalizePolyBuffers() {
- if (glPolyVertex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyVertex, context);
- }
-
- if (glPolyColor != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyColor, context);
- }
-
- if (glPolyNormal != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyNormal, context);
- }
-
- if (glPolyTexcoord != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyTexcoord, context);
- }
-
- if (glPolyAmbient != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyAmbient, context);
- }
-
- if (glPolySpecular != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolySpecular, context);
- }
-
- if (glPolyEmissive != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyEmissive, context);
- }
-
- if (glPolyShininess != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyShininess, context);
- }
-
- if (glPolyIndex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPolyIndex, context);
- }
- }
-
-
- protected void finalizeLineBuffers() {
- if (glLineVertex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineVertex, context);
- }
-
- if (glLineColor != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineColor, context);
- }
-
- if (glLineAttrib != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineAttrib, context);
- }
-
- if (glLineIndex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glLineIndex, context);
- }
- }
-
-
- protected void finalizePointBuffers() {
- if (glPointVertex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointVertex, context);
- }
-
- if (glPointColor != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointColor, context);
- }
-
- if (glPointAttrib != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointAttrib, context);
- }
-
- if (glPointIndex != 0) {
- PGraphicsOpenGL.finalizeVertexBufferObject(glPointIndex, context);
- }
- }
-
///////////////////////////////////////////////////////////
//
// Shape creation (temporary hack)
- public static PShapeOpenGL createShape3D(PGraphicsOpenGL pg, PShape src) {
+ public static PShapeOpenGL createShape(PGraphicsOpenGL pg, PShape src) {
PShapeOpenGL dest = null;
if (src.getFamily() == GROUP) {
- dest = PGraphics3D.createShapeImpl(pg, GROUP);
- copyGroup3D(pg, src, dest);
+ //dest = PGraphics3D.createShapeImpl(pg, GROUP);
+ dest = (PShapeOpenGL) pg.createShapeFamily(GROUP);
+ copyGroup(pg, src, dest);
} else if (src.getFamily() == PRIMITIVE) {
- dest = PGraphics3D.createShapeImpl(pg, src.getKind(), src.getParams());
+ //dest = PGraphics3D.createShapeImpl(pg, src.getKind(), src.getParams());
+ dest = (PShapeOpenGL) pg.createShapePrimitive(src.getKind(), src.getParams());
PShape.copyPrimitive(src, dest);
} else if (src.getFamily() == GEOMETRY) {
- dest = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY);
+ //dest = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY);
+ dest = (PShapeOpenGL) pg.createShapeFamily(PShape.GEOMETRY);
PShape.copyGeometry(src, dest);
} else if (src.getFamily() == PATH) {
- dest = PGraphics3D.createShapeImpl(pg, PATH);
+ dest = (PShapeOpenGL) pg.createShapeFamily(PShape.PATH);
+ //dest = PGraphics3D.createShapeImpl(pg, PATH);
PShape.copyPath(src, dest);
}
dest.setName(src.getName());
@@ -601,19 +546,24 @@ public static PShapeOpenGL createShape3D(PGraphicsOpenGL pg, PShape src) {
}
+ /*
static public PShapeOpenGL createShape2D(PGraphicsOpenGL pg, PShape src) {
PShapeOpenGL dest = null;
if (src.getFamily() == GROUP) {
- dest = PGraphics2D.createShapeImpl(pg, GROUP);
+ //dest = PGraphics2D.createShapeImpl(pg, GROUP);
+ dest = (PShapeOpenGL) pg.createShapeFamily(GROUP);
copyGroup2D(pg, src, dest);
} else if (src.getFamily() == PRIMITIVE) {
- dest = PGraphics2D.createShapeImpl(pg, src.getKind(), src.getParams());
+ //dest = PGraphics2D.createShapeImpl(pg, src.getKind(), src.getParams());
+ dest = (PShapeOpenGL) pg.createShapePrimitive(src.getKind(), src.getParams());
PShape.copyPrimitive(src, dest);
} else if (src.getFamily() == GEOMETRY) {
- dest = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY);
+ //dest = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY);
+ dest = (PShapeOpenGL) pg.createShapeFamily(PShape.GEOMETRY);
PShape.copyGeometry(src, dest);
} else if (src.getFamily() == PATH) {
- dest = PGraphics2D.createShapeImpl(pg, PATH);
+ //dest = PGraphics2D.createShapeImpl(pg, PATH);
+ dest = (PShapeOpenGL) pg.createShapeFamily(PShape.PATH);
PShape.copyPath(src, dest);
}
dest.setName(src.getName());
@@ -621,20 +571,21 @@ static public PShapeOpenGL createShape2D(PGraphicsOpenGL pg, PShape src) {
dest.height = src.height;
return dest;
}
+*/
-
- static public void copyGroup3D(PGraphicsOpenGL pg, PShape src, PShape dest) {
+ static public void copyGroup(PGraphicsOpenGL pg, PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
for (int i = 0; i < src.getChildCount(); i++) {
- PShape c = createShape3D(pg, src.getChild(i));
+ PShape c = createShape(pg, src.getChild(i));
dest.addChild(c);
}
}
+ /*
static public void copyGroup2D(PGraphicsOpenGL pg, PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
@@ -645,7 +596,7 @@ static public void copyGroup2D(PGraphicsOpenGL pg, PShape src, PShape dest) {
dest.addChild(c);
}
}
-
+*/
///////////////////////////////////////////////////////////
@@ -657,9 +608,9 @@ static public void copyGroup2D(PGraphicsOpenGL pg, PShape src, PShape dest) {
@Override
public float getWidth() {
PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
- Float.POSITIVE_INFINITY);
+ Float.POSITIVE_INFINITY);
PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
- Float.NEGATIVE_INFINITY);
+ Float.NEGATIVE_INFINITY);
if (shapeCreated) {
getVertexMin(min);
getVertexMax(max);
@@ -672,9 +623,9 @@ public float getWidth() {
@Override
public float getHeight() {
PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
- Float.POSITIVE_INFINITY);
+ Float.POSITIVE_INFINITY);
PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
- Float.NEGATIVE_INFINITY);
+ Float.NEGATIVE_INFINITY);
if (shapeCreated) {
getVertexMin(min);
getVertexMax(max);
@@ -687,9 +638,9 @@ public float getHeight() {
@Override
public float getDepth() {
PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
- Float.POSITIVE_INFINITY);
+ Float.POSITIVE_INFINITY);
PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY,
- Float.NEGATIVE_INFINITY);
+ Float.NEGATIVE_INFINITY);
if (shapeCreated) {
getVertexMin(min);
getVertexMax(max);
@@ -762,11 +713,11 @@ protected int getVertexSum(PVector sum, int count) {
if (is3D()) {
if (hasLines) {
count += tessGeo.getLineVertexSum(sum, firstLineVertex,
- lastLineVertex);
+ lastLineVertex);
}
if (hasPoints) {
count += tessGeo.getPointVertexSum(sum, firstPointVertex,
- lastPointVertex);
+ lastPointVertex);
}
}
}
@@ -852,7 +803,7 @@ protected void setTextureImpl(PImage tex) {
}
if (image0 != tex && parent != null) {
- ((PShapeOpenGL)parent).removeTexture(tex);
+ ((PShapeOpenGL)parent).removeTexture(image0, this);
}
if (parent != null) {
((PShapeOpenGL)parent).addTexture(image);
@@ -896,7 +847,7 @@ protected void scaleTextureUV(float uFactor, float vFactor) {
protected void addTexture(PImage tex) {
if (textures == null) {
- textures = new HashSet();
+ textures = new HashSet<>();
}
textures.add(tex);
if (parent != null) {
@@ -905,14 +856,14 @@ protected void addTexture(PImage tex) {
}
- protected void removeTexture(PImage tex) {
+ protected void removeTexture(PImage tex, PShapeOpenGL caller) {
if (textures == null || !textures.contains(tex)) return; // Nothing to remove.
- // First check that none of the child shapes
- // have texture tex...
+ // First check that none of the child shapes have texture tex...
boolean childHasTex = false;
for (int i = 0; i < childCount; i++) {
PShapeOpenGL child = (PShapeOpenGL) children[i];
+ if (child == caller) continue;
if (child.hasTexture(tex)) {
childHasTex = true;
break;
@@ -930,38 +881,76 @@ protected void removeTexture(PImage tex) {
// Since this shape and all its child shapes don't contain
// tex anymore, we now can remove it from the parent.
if (parent != null) {
- ((PShapeOpenGL)parent).removeTexture(tex);
+ ((PShapeOpenGL)parent).removeTexture(tex, this);
}
}
protected void strokedTexture(boolean newValue) {
+ strokedTexture(newValue, null);
+ }
+
+
+ protected void strokedTexture(boolean newValue, PShapeOpenGL caller) {
if (strokedTexture == newValue) return; // Nothing to change.
if (newValue) {
strokedTexture = true;
} else {
- // First check that none of the child shapes
- // have have a stroked texture...
- boolean childHasStrokedTex = false;
+ // Check that none of the child shapes have a stroked texture...
+ strokedTexture = false;
for (int i = 0; i < childCount; i++) {
PShapeOpenGL child = (PShapeOpenGL) children[i];
+ if (child == caller) continue;
if (child.hasStrokedTexture()) {
- childHasStrokedTex = true;
+ strokedTexture = true;
break;
}
}
+ }
+
+ // Now we can update the parent shape.
+ if (parent != null) {
+ ((PShapeOpenGL)parent).strokedTexture(newValue, this);
+ }
+ }
- if (!childHasStrokedTex) {
- // ...if not, it is safe to mark this shape as without
- // stroked texture.
- strokedTexture = false;
+
+ protected void untexChild(boolean newValue) {
+ untexChild(newValue, null);
+ }
+
+
+ protected void untexChild(boolean newValue, PShapeOpenGL caller) {
+ if (untexChild == newValue) return; // Nothing to change.
+
+ if (newValue) {
+ untexChild = true;
+ } else {
+ // Check if any of the child shapes is not textured...
+ untexChild = false;
+ for (int i = 0; i < childCount; i++) {
+ PShapeOpenGL child = (PShapeOpenGL) children[i];
+ if (child == caller) continue;
+ if (!child.hasTexture()) {
+ untexChild = true;
+ break;
+ }
}
}
// Now we can update the parent shape.
if (parent != null) {
- ((PShapeOpenGL)parent).strokedTexture(newValue);
+ ((PShapeOpenGL)parent).untexChild(newValue, this);
+ }
+ }
+
+
+ protected boolean hasTexture() {
+ if (family == GROUP) {
+ return textures != null && 0 < textures.size();
+ } else {
+ return image != null;
}
}
@@ -1074,12 +1063,12 @@ protected void vertexImpl(float x, float y, float z, float u, float v) {
}
inGeo.addVertex(x, y, z,
- fcolor,
- normalX, normalY, normalZ,
- u, v,
- scolor, sweight,
- ambientColor, specularColor, emissiveColor, shininess,
- VERTEX, vertexBreak());
+ fcolor,
+ normalX, normalY, normalZ,
+ u, v,
+ scolor, sweight,
+ ambientColor, specularColor, emissiveColor, shininess,
+ VERTEX, vertexBreak());
markForTessellation();
}
@@ -1122,6 +1111,80 @@ public void normal(float nx, float ny, float nz) {
}
+ @Override
+ public void attribPosition(String name, float x, float y, float z) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.POSITION,
+ PGL.FLOAT, 3);
+ if (attrib != null) attrib.set(x, y, z);
+ }
+
+
+ @Override
+ public void attribNormal(String name, float nx, float ny, float nz) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.NORMAL,
+ PGL.FLOAT, 3);
+ if (attrib != null) attrib.set(nx, ny, nz);
+ }
+
+
+ @Override
+ public void attribColor(String name, int color) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.COLOR, PGL.INT, 1);
+ if (attrib != null) attrib.set(new int[] {color});
+ }
+
+
+ @Override
+ public void attrib(String name, float... values) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.FLOAT,
+ values.length);
+ if (attrib != null) attrib.set(values);
+ }
+
+
+ @Override
+ public void attrib(String name, int... values) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.INT,
+ values.length);
+ if (attrib != null) attrib.set(values);
+ }
+
+
+ @Override
+ public void attrib(String name, boolean... values) {
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.BOOL,
+ values.length);
+ if (attrib != null) attrib.set(values);
+ }
+
+
+ protected VertexAttribute attribImpl(String name, int kind, int type, int size) {
+ if (4 < size) {
+ PGraphics.showWarning("Vertex attributes cannot have more than 4 values");
+ return null;
+ }
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib == null) {
+ attrib = new VertexAttribute(pg, name, kind, type, size);
+ polyAttribs.put(name, attrib);
+ inGeo.initAttrib(attrib);
+ }
+ if (attrib.kind != kind) {
+ PGraphics.showWarning("The attribute kind cannot be changed after creation");
+ return null;
+ }
+ if (attrib.type != type) {
+ PGraphics.showWarning("The attribute type cannot be changed after creation");
+ return null;
+ }
+ if (attrib.size != size) {
+ PGraphics.showWarning("New value for vertex attribute has wrong number of values");
+ return null;
+ }
+ return attrib;
+ }
+
+
@Override
public void endShape(int mode) {
super.endShape(mode);
@@ -1153,7 +1216,7 @@ public void setParams(float[] source) {
public void setPath(int vcount, float[][] verts, int ccount, int[] codes) {
if (family != PATH) {
PGraphics.showWarning("Vertex coordinates and codes can only be set to " +
- "PATH shapes");
+ "PATH shapes");
return;
}
@@ -1188,7 +1251,11 @@ public void translate(float tx, float ty, float tz) {
@Override
public void rotate(float angle) {
- transform(ROTATE, angle);
+ if (is3D) {
+ transform(ROTATE, angle, 0, 0, 1);
+ } else {
+ transform(ROTATE, angle);
+ }
}
@@ -1245,7 +1312,7 @@ public void scale(float x, float y, float z) {
@Override
public void applyMatrix(PMatrix2D source) {
transform(MATRIX, source.m00, source.m01, source.m02,
- source.m10, source.m11, source.m12);
+ source.m10, source.m11, source.m12);
}
@@ -1253,7 +1320,7 @@ public void applyMatrix(PMatrix2D source) {
public void applyMatrix(float n00, float n01, float n02,
float n10, float n11, float n12) {
transform(MATRIX, n00, n01, n02,
- n10, n11, n12);
+ n10, n11, n12);
}
@@ -1263,47 +1330,42 @@ public void applyMatrix(float n00, float n01, float n02, float n03,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
transform(MATRIX, n00, n01, n02, n03,
- n10, n11, n12, n13,
- n20, n21, n22, n23,
- n30, n31, n32, n33);
+ n10, n11, n12, n13,
+ n20, n21, n22, n23,
+ n30, n31, n32, n33);
}
@Override
public void resetMatrix() {
- if (shapeCreated && matrix != null && transformStack != null) {
+ if (shapeCreated && matrix != null && matrixInv != null) {
if (family == GROUP) {
updateTessellation();
}
if (tessellated) {
- PMatrix mat = popTransform();
- while (mat != null) {
- boolean res = mat.invert();
- if (res) {
- applyMatrixImpl(mat);
- } else {
- PGraphics.showWarning("Transformation applied on the shape cannot be inverted");
- }
- mat = popTransform();
- }
+ applyMatrixImpl(matrixInv);
}
matrix.reset();
- transformStack.clear();
+ matrixInv.reset();
}
}
protected void transform(int type, float... args) {
int dimensions = is3D ? 3 : 2;
+ boolean invertible = true;
checkMatrix(dimensions);
if (transform == null) {
if (dimensions == 2) {
transform = new PMatrix2D();
+ transformInv = new PMatrix2D();
} else {
transform = new PMatrix3D();
+ transformInv = new PMatrix3D();
}
} else {
transform.reset();
+ transformInv.reset();
}
int ncoords = args.length;
@@ -1314,88 +1376,105 @@ protected void transform(int type, float... args) {
}
switch (type) {
- case TRANSLATE:
- if (ncoords == 3) {
- transform.translate(args[0], args[1], args[2]);
- } else {
- transform.translate(args[0], args[1]);
- }
- break;
- case ROTATE:
- if (ncoords == 3) {
- transform.rotate(args[0], args[1], args[2], args[3]);
- } else {
- transform.rotate(args[0]);
- }
- break;
- case SCALE:
- if (ncoords == 3) {
- transform.scale(args[0], args[1], args[2]);
- } else {
- transform.scale(args[0], args[1]);
- }
- break;
- case MATRIX:
- if (ncoords == 3) {
- transform.set(args[ 0], args[ 1], args[ 2], args[ 3],
- args[ 4], args[ 5], args[ 6], args[ 7],
- args[ 8], args[ 9], args[10], args[11],
- args[12], args[13], args[14], args[15]);
- } else {
- transform.set(args[0], args[1], args[2],
- args[3], args[4], args[5]);
- }
- break;
+ case TRANSLATE:
+ if (ncoords == 3) {
+ transform.translate(args[0], args[1], args[2]);
+ PGraphicsOpenGL.invTranslate((PMatrix3D)transformInv, args[0], args[1], args[2]);
+ } else {
+ transform.translate(args[0], args[1]);
+ PGraphicsOpenGL.invTranslate((PMatrix2D)transformInv, args[0], args[1]);
+ }
+ break;
+ case ROTATE:
+ if (ncoords == 3) {
+ transform.rotate(args[0], args[1], args[2], args[3]);
+ PGraphicsOpenGL.invRotate((PMatrix3D)transformInv, args[0], args[1], args[2], args[3]);
+ } else {
+ transform.rotate(args[0]);
+ PGraphicsOpenGL.invRotate((PMatrix2D)transformInv, -args[0]);
+ }
+ break;
+ case SCALE:
+ if (ncoords == 3) {
+ transform.scale(args[0], args[1], args[2]);
+ PGraphicsOpenGL.invScale((PMatrix3D)transformInv, args[0], args[1], args[2]);
+ } else {
+ transform.scale(args[0], args[1]);
+ PGraphicsOpenGL.invScale((PMatrix2D)transformInv, args[0], args[1]);
+ }
+ break;
+ case MATRIX:
+ if (ncoords == 3) {
+ transform.set(args[ 0], args[ 1], args[ 2], args[ 3],
+ args[ 4], args[ 5], args[ 6], args[ 7],
+ args[ 8], args[ 9], args[10], args[11],
+ args[12], args[13], args[14], args[15]);
+ } else {
+ transform.set(args[0], args[1], args[2],
+ args[3], args[4], args[5]);
+ }
+ transformInv.set(transform);
+ invertible = transformInv.invert();
+ break;
}
- matrix.apply(transform);
- pushTransform();
- if (tessellated) applyMatrixImpl(transform);
- }
-
-
- protected void pushTransform() {
- if (transformStack == null) transformStack = new Stack();
- PMatrix mat;
- if (transform instanceof PMatrix2D) {
- mat = new PMatrix2D();
+ matrix.preApply(transform);
+ if (invertible) {
+ matrixInv.apply(transformInv);
} else {
- mat = new PMatrix3D();
+ PGraphics.showWarning("Transformation applied on the shape cannot be inverted");
}
- mat.set(transform);
- transformStack.push(mat);
+ if (tessellated) applyMatrixImpl(transform);
}
- protected PMatrix popTransform() {
- if (transformStack == null || transformStack.size() == 0) return null;
- return transformStack.pop();
- }
-
protected void applyMatrixImpl(PMatrix matrix) {
if (hasPolys) {
tessGeo.applyMatrixOnPolyGeometry(matrix,
- firstPolyVertex, lastPolyVertex);
+ firstPolyVertex, lastPolyVertex);
root.setModifiedPolyVertices(firstPolyVertex, lastPolyVertex);
root.setModifiedPolyNormals(firstPolyVertex, lastPolyVertex);
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (attrib.isPosition() || attrib.isNormal()) {
+ root.setModifiedPolyAttrib(attrib, firstPolyVertex, lastPolyVertex);
+ }
+ }
}
if (is3D()) {
if (hasLines) {
tessGeo.applyMatrixOnLineGeometry(matrix,
- firstLineVertex, lastLineVertex);
+ firstLineVertex, lastLineVertex);
root.setModifiedLineVertices(firstLineVertex, lastLineVertex);
root.setModifiedLineAttributes(firstLineVertex, lastLineVertex);
}
if (hasPoints) {
tessGeo.applyMatrixOnPointGeometry(matrix,
- firstPointVertex, lastPointVertex);
+ firstPointVertex, lastPointVertex);
root.setModifiedPointVertices(firstPointVertex, lastPointVertex);
+ root.setModifiedPointAttributes(firstPointVertex, lastPointVertex);
}
}
}
+ @Override
+ protected void checkMatrix(int dimensions) {
+ if (matrix == null) {
+ if (dimensions == 2) {
+ matrix = new PMatrix2D();
+ matrixInv = new PMatrix2D();
+ } else {
+ matrix = new PMatrix3D();
+ matrixInv = new PMatrix3D();
+ }
+ } else if (dimensions == 3 && (matrix instanceof PMatrix2D)) {
+ matrix = new PMatrix3D(matrix);
+ matrixInv = new PMatrix3D(matrixInv);
+ }
+ }
+
+
///////////////////////////////////////////////////////////
//
@@ -1418,8 +1497,8 @@ public void bezierVertex(float x2, float y2,
float x3, float y3,
float x4, float y4) {
bezierVertexImpl(x2, y2, 0,
- x3, y3, 0,
- x4, y4, 0);
+ x3, y3, 0,
+ x4, y4, 0);
}
@@ -1428,8 +1507,8 @@ public void bezierVertex(float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4) {
bezierVertexImpl(x2, y2, z2,
- x3, y3, z3,
- x4, y4, z4);
+ x3, y3, z3,
+ x4, y4, z4);
}
@@ -1437,19 +1516,11 @@ protected void bezierVertexImpl(float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4) {
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addBezierVertex(x2, y2, z2,
- x3, y3, z3,
- x4, y4, z4, vertexBreak());
-
-// inGeo.addVertex(x2, y2, z2, BEZIER_VERTEX, vertexBreak());
-// inGeo.addVertex(x3, y3, z3, BEZIER_VERTEX, false);
-// inGeo.addVertex(x4, y4, z4, BEZIER_VERTEX, false);
-//// inGeo.addBezierVertex(x2, y2, z2,
-// x3, y3, z3,
-// x4, y4, z4,
-// fill, stroke, bezierDetail, vertexCode(), kind);
+ x3, y3, z3,
+ x4, y4, z4, vertexBreak());
}
@@ -1457,7 +1528,7 @@ protected void bezierVertexImpl(float x2, float y2, float z2,
public void quadraticVertex(float cx, float cy,
float x3, float y3) {
quadraticVertexImpl(cx, cy, 0,
- x3, y3, 0);
+ x3, y3, 0);
}
@@ -1465,22 +1536,17 @@ public void quadraticVertex(float cx, float cy,
public void quadraticVertex(float cx, float cy, float cz,
float x3, float y3, float z3) {
quadraticVertexImpl(cx, cy, cz,
- x3, y3, z3);
+ x3, y3, z3);
}
protected void quadraticVertexImpl(float cx, float cy, float cz,
float x3, float y3, float z3) {
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addQuadraticVertex(cx, cy, cz,
- x3, y3, z3, vertexBreak());
-// inGeo.addVertex(cx, cy, cz, QUADRATIC_VERTEX, vertexBreak());
-// inGeo.addVertex(x3, y3, z3, QUADRATIC_VERTEX, false);
-// inGeo.addQuadraticVertex(cx, cy, cz,
-// x3, y3, z3,
-// fill, stroke, bezierDetail, vertexCode(), kind);
+ x3, y3, z3, vertexBreak());
}
@@ -1525,12 +1591,9 @@ public void curveVertex(float x, float y, float z) {
protected void curveVertexImpl(float x, float y, float z) {
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addCurveVertex(x, y, z, vertexBreak());
-// inGeo.addVertex(x, y, z, CURVE_VERTEX, vertexBreak());
-// inGeo.addCurveVertex(x, y, z,
-// fill, stroke, curveDetail, vertexCode(), kind);
}
@@ -1600,10 +1663,26 @@ public void setVertex(int index, float x, float y, float z) {
// TODO: in certain cases (kind = TRIANGLE, etc) the correspondence between
// input and tessellated vertices is 1-1, so in those cases re-tessellation
- // wouldnt' be neccessary.
- inGeo.vertices[3 * index + 0] = x;
- inGeo.vertices[3 * index + 1] = y;
- inGeo.vertices[3 * index + 2] = z;
+ // wouldn't be necessary. But in order to reasonable take care of that
+ // situation, we would need a complete rethinking of the rendering architecture
+ // in Processing :-)
+ if (family == PATH) {
+ if (vertexCodes != null && vertexCodeCount > 0 &&
+ vertexCodes[index] != VERTEX) {
+ PGraphics.showWarning(NOT_A_SIMPLE_VERTEX, "setVertex()");
+ return;
+ }
+ vertices[index][X] = x;
+ vertices[index][Y] = y;
+ if (is3D && vertices[index].length > 2) {
+ // P3D allows to modify 2D shapes, ignoring the Z coordinate.
+ vertices[index][Z] = z;
+ }
+ } else {
+ inGeo.vertices[3 * index + 0] = x;
+ inGeo.vertices[3 * index + 1] = y;
+ inGeo.vertices[3 * index + 2] = z;
+ }
markForTessellation();
}
@@ -1615,9 +1694,22 @@ public void setVertex(int index, PVector vec) {
return;
}
- inGeo.vertices[3 * index + 0] = vec.x;
- inGeo.vertices[3 * index + 1] = vec.y;
- inGeo.vertices[3 * index + 2] = vec.z;
+ if (family == PATH) {
+ if (vertexCodes != null && vertexCodeCount > 0 &&
+ vertexCodes[index] != VERTEX) {
+ PGraphics.showWarning(NOT_A_SIMPLE_VERTEX, "setVertex()");
+ return;
+ }
+ vertices[index][X] = vec.x;
+ vertices[index][Y] = vec.y;
+ if (is3D && vertices[index].length > 2) {
+ vertices[index][Z] = vec.z;
+ }
+ } else {
+ inGeo.vertices[3 * index + 0] = vec.x;
+ inGeo.vertices[3 * index + 1] = vec.y;
+ inGeo.vertices[3 * index + 2] = vec.z;
+ }
markForTessellation();
}
@@ -1666,6 +1758,57 @@ public void setNormal(int index, float nx, float ny, float nz) {
}
+ @Override
+ public void setAttrib(String name, int index, float... values) {
+ if (openShape) {
+ PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setNormal()");
+ return;
+ }
+
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.FLOAT,
+ values.length);
+ float[] array = inGeo.fattribs.get(name);
+ for (int i = 0; i < values.length; i++) {
+ array[attrib.size * index + i] = values[i];
+ }
+ markForTessellation();
+ }
+
+
+ @Override
+ public void setAttrib(String name, int index, int... values) {
+ if (openShape) {
+ PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setNormal()");
+ return;
+ }
+
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.INT,
+ values.length);
+ int[] array = inGeo.iattribs.get(name);
+ for (int i = 0; i < values.length; i++) {
+ array[attrib.size * index + i] = values[i];
+ }
+ markForTessellation();
+ }
+
+
+ @Override
+ public void setAttrib(String name, int index, boolean... values) {
+ if (openShape) {
+ PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setNormal()");
+ return;
+ }
+
+ VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.BOOL,
+ values.length);
+ byte[] array = inGeo.battribs.get(name);
+ for (int i = 0; i < values.length; i++) {
+ array[attrib.size * index + i] = (byte)(values[i]?1:0);
+ }
+ markForTessellation();
+ }
+
+
@Override
public float getTextureU(int index) {
return inGeo.texcoords[2 * index + 0];
@@ -1718,8 +1861,8 @@ public void setFill(boolean fill) {
PShapeOpenGL child = (PShapeOpenGL) children[i];
child.setFill(fill);
}
- } else if (this.fill && !fill) {
- setFillImpl(0x0);
+ } else if (this.fill != fill) {
+ markForTessellation();
}
this.fill = fill;
}
@@ -1749,18 +1892,18 @@ protected void setFillImpl(int fill) {
if (image == null) {
Arrays.fill(inGeo.colors, 0, inGeo.vertexCount,
- PGL.javaToNativeARGB(fillColor));
+ PGL.javaToNativeARGB(fillColor));
if (shapeCreated && tessellated && hasPolys) {
if (is3D()) {
Arrays.fill(tessGeo.polyColors, firstPolyVertex, lastPolyVertex + 1,
- PGL.javaToNativeARGB(fillColor));
+ PGL.javaToNativeARGB(fillColor));
root.setModifiedPolyColors(firstPolyVertex, lastPolyVertex);
} else if (is2D()) {
int last1 = lastPolyVertex + 1;
if (-1 < firstLineVertex) last1 = firstLineVertex;
if (-1 < firstPointVertex) last1 = firstPointVertex;
Arrays.fill(tessGeo.polyColors, firstPolyVertex, last1,
- PGL.javaToNativeARGB(fillColor));
+ PGL.javaToNativeARGB(fillColor));
root.setModifiedPolyColors(firstPolyVertex, last1 - 1);
}
}
@@ -1844,18 +1987,18 @@ protected void setTintImpl(int tint) {
if (image != null) {
Arrays.fill(inGeo.colors, 0, inGeo.vertexCount,
- PGL.javaToNativeARGB(tintColor));
+ PGL.javaToNativeARGB(tintColor));
if (shapeCreated && tessellated && hasPolys) {
if (is3D()) {
Arrays.fill(tessGeo.polyColors, firstPolyVertex, lastPolyVertex + 1,
- PGL.javaToNativeARGB(tintColor));
+ PGL.javaToNativeARGB(tintColor));
root.setModifiedPolyColors(firstPolyVertex, lastPolyVertex);
} else if (is2D()) {
int last1 = lastPolyVertex + 1;
if (-1 < firstLineVertex) last1 = firstLineVertex;
if (-1 < firstPointVertex) last1 = firstPointVertex;
Arrays.fill(tessGeo.polyColors, firstPolyVertex, last1,
- PGL.javaToNativeARGB(tintColor));
+ PGL.javaToNativeARGB(tintColor));
root.setModifiedPolyColors(firstPolyVertex, last1 - 1);
}
}
@@ -1899,20 +2042,31 @@ public void setStroke(boolean stroke) {
PShapeOpenGL child = (PShapeOpenGL) children[i];
child.setStroke(stroke);
}
- } else if (this.stroke != stroke) {
- if (this.stroke) {
- // Disabling stroke on a shape previously with
- // stroke needs a re-tessellation in order to remove
- // the additional geometry of lines and/or points.
- markForTessellation();
- stroke = false;
+ this.stroke = stroke;
+ } else {
+ setStrokeImpl(stroke);
+ }
+ }
+
+
+ protected void setStrokeImpl(boolean stroke) {
+ if (this.stroke != stroke) {
+ if (stroke) {
+ // Before there was no stroke, now there is stroke, so current stroke
+ // color should be copied to the input geometry, and geometry should
+ // be marked as modified in case it needs to be re-tessellated.
+ int color = strokeColor;
+ strokeColor += 1; // Forces a color change
+ setStrokeImpl(color);
}
- setStrokeImpl(0x0);
+
+ markForTessellation();
if (is2D() && parent != null) {
- ((PShapeOpenGL)parent).strokedTexture(false);
+ ((PShapeOpenGL)parent).strokedTexture(stroke && image != null);
}
+
+ this.stroke = stroke;
}
- this.stroke = stroke;
}
@@ -1939,27 +2093,27 @@ protected void setStrokeImpl(int stroke) {
strokeColor = stroke;
Arrays.fill(inGeo.strokeColors, 0, inGeo.vertexCount,
- PGL.javaToNativeARGB(strokeColor));
+ PGL.javaToNativeARGB(strokeColor));
if (shapeCreated && tessellated && (hasLines || hasPoints)) {
if (hasLines) {
if (is3D()) {
Arrays.fill(tessGeo.lineColors, firstLineVertex, lastLineVertex + 1,
- PGL.javaToNativeARGB(strokeColor));
+ PGL.javaToNativeARGB(strokeColor));
root.setModifiedLineColors(firstLineVertex, lastLineVertex);
} else if (is2D()) {
Arrays.fill(tessGeo.polyColors, firstLineVertex, lastLineVertex + 1,
- PGL.javaToNativeARGB(strokeColor));
+ PGL.javaToNativeARGB(strokeColor));
root.setModifiedPolyColors(firstLineVertex, lastLineVertex);
}
}
if (hasPoints) {
if (is3D()) {
Arrays.fill(tessGeo.pointColors, firstPointVertex, lastPointVertex + 1,
- PGL.javaToNativeARGB(strokeColor));
+ PGL.javaToNativeARGB(strokeColor));
root.setModifiedPointColors(firstPointVertex, lastPointVertex);
} else if (is2D()) {
Arrays.fill(tessGeo.polyColors, firstPointVertex, lastPointVertex + 1,
- PGL.javaToNativeARGB(strokeColor));
+ PGL.javaToNativeARGB(strokeColor));
root.setModifiedPolyColors(firstPointVertex, lastPointVertex);
}
}
@@ -2139,18 +2293,18 @@ protected void setAmbientImpl(int ambient) {
ambientColor = ambient;
Arrays.fill(inGeo.ambient, 0, inGeo.vertexCount,
- PGL.javaToNativeARGB(ambientColor));
+ PGL.javaToNativeARGB(ambientColor));
if (shapeCreated && tessellated && hasPolys) {
if (is3D()) {
Arrays.fill(tessGeo.polyAmbient, firstPolyVertex, lastPolyVertex + 1,
- PGL.javaToNativeARGB(ambientColor));
+ PGL.javaToNativeARGB(ambientColor));
root.setModifiedPolyAmbient(firstPolyVertex, lastPolyVertex);
} else if (is2D()) {
int last1 = lastPolyVertex + 1;
if (-1 < firstLineVertex) last1 = firstLineVertex;
if (-1 < firstPointVertex) last1 = firstPointVertex;
Arrays.fill(tessGeo.polyAmbient, firstPolyVertex, last1,
- PGL.javaToNativeARGB(ambientColor));
+ PGL.javaToNativeARGB(ambientColor));
root.setModifiedPolyColors(firstPolyVertex, last1 - 1);
}
}
@@ -2204,18 +2358,18 @@ protected void setSpecularImpl(int specular) {
specularColor = specular;
Arrays.fill(inGeo.specular, 0, inGeo.vertexCount,
- PGL.javaToNativeARGB(specularColor));
+ PGL.javaToNativeARGB(specularColor));
if (shapeCreated && tessellated && hasPolys) {
if (is3D()) {
Arrays.fill(tessGeo.polySpecular, firstPolyVertex, lastPolyVertex + 1,
- PGL.javaToNativeARGB(specularColor));
+ PGL.javaToNativeARGB(specularColor));
root.setModifiedPolySpecular(firstPolyVertex, lastPolyVertex);
} else if (is2D()) {
int last1 = lastPolyVertex + 1;
if (-1 < firstLineVertex) last1 = firstLineVertex;
if (-1 < firstPointVertex) last1 = firstPointVertex;
Arrays.fill(tessGeo.polySpecular, firstPolyVertex, last1,
- PGL.javaToNativeARGB(specularColor));
+ PGL.javaToNativeARGB(specularColor));
root.setModifiedPolyColors(firstPolyVertex, last1 - 1);
}
}
@@ -2267,18 +2421,18 @@ protected void setEmissiveImpl(int emissive) {
emissiveColor = emissive;
Arrays.fill(inGeo.emissive, 0, inGeo.vertexCount,
- PGL.javaToNativeARGB(emissiveColor));
+ PGL.javaToNativeARGB(emissiveColor));
if (shapeCreated && tessellated && 0 < tessGeo.polyVertexCount) {
if (is3D()) {
Arrays.fill(tessGeo.polyEmissive, firstPolyVertex, lastPolyVertex + 1,
- PGL.javaToNativeARGB(emissiveColor));
+ PGL.javaToNativeARGB(emissiveColor));
root.setModifiedPolyEmissive(firstPolyVertex, lastPolyVertex);
} else if (is2D()) {
int last1 = lastPolyVertex + 1;
if (-1 < firstLineVertex) last1 = firstLineVertex;
if (-1 < firstPointVertex) last1 = firstPointVertex;
Arrays.fill(tessGeo.polyEmissive, firstPolyVertex, last1,
- PGL.javaToNativeARGB(emissiveColor));
+ PGL.javaToNativeARGB(emissiveColor));
root.setModifiedPolyColors(firstPolyVertex, last1 - 1);
}
}
@@ -2333,7 +2487,7 @@ protected void setShininessImpl(float shininess) {
if (shapeCreated && tessellated && hasPolys) {
if (is3D()) {
Arrays.fill(tessGeo.polyShininess, firstPolyVertex, lastPolyVertex + 1,
- shininess);
+ shininess);
root.setModifiedPolyShininess(firstPolyVertex, lastPolyVertex);
} else if (is2D()) {
int last1 = lastPolyVertex + 1;
@@ -2420,14 +2574,18 @@ public PShape getTessellation() {
short[] indices = tessGeo.polyIndices;
PShape tess;
- if (is3D()) {
- tess = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY);
- } else if (is2D()) {
- tess = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY);
- } else {
- PGraphics.showWarning("This shape is not either 2D or 3D!");
- return null;
- }
+// if (is3D()) {
+// //tess = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY);
+// tess = pg.createShapeFamily(PShape.GEOMETRY);
+// } else if (is2D()) {
+// //tess = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY);
+// tess = pg.createShapeFamily(PShape.GEOMETRY);
+// } else {
+// PGraphics.showWarning("This shape is not either 2D or 3D!");
+// return null;
+// }
+ tess = pg.createShapeFamily(PShape.GEOMETRY);
+ tess.set3D(is3D); // if this is a 3D shape, make the new shape 3D as well
tess.beginShape(TRIANGLES);
tess.noStroke();
@@ -2586,9 +2744,9 @@ public boolean contains(float x, float y) {
if (((inGeo.vertices[3 * i + 1] > y) != (inGeo.vertices[3 * j + 1] > y)) &&
(x <
(inGeo.vertices[3 * j]-inGeo.vertices[3 * i]) *
- (y-inGeo.vertices[3 * i + 1]) /
- (inGeo.vertices[3 * j + 1]-inGeo.vertices[3 * i + 1]) +
- inGeo.vertices[3 * i])) {
+ (y-inGeo.vertices[3 * i + 1]) /
+ (inGeo.vertices[3 * j + 1]-inGeo.vertices[3 * i + 1]) +
+ inGeo.vertices[3 * i])) {
c = !c;
}
}
@@ -2676,12 +2834,27 @@ protected void initModified() {
protected void tessellate() {
- if (root == this && parent == null) {
+ if (root == this && parent == null) { // Root shape
+ boolean initAttr = false;
+ if (polyAttribs == null) {
+ polyAttribs = PGraphicsOpenGL.newAttributeMap();
+ initAttr = true;
+ }
+
if (tessGeo == null) {
- tessGeo = PGraphicsOpenGL.newTessGeometry(pg, PGraphicsOpenGL.RETAINED);
+ tessGeo = PGraphicsOpenGL.newTessGeometry(pg, polyAttribs, PGraphicsOpenGL.RETAINED);
}
tessGeo.clear();
+ if (initAttr) {
+ collectPolyAttribs();
+ }
+
+ for (int i = 0; i < polyAttribs.size(); i++) {
+ VertexAttribute attrib = polyAttribs.get(i);
+ tessGeo.initAttrib(attrib);
+ }
+
tessellateImpl();
// Tessellated arrays are trimmed since they are expanded
@@ -2692,6 +2865,31 @@ protected void tessellate() {
}
+ protected void collectPolyAttribs() {
+ AttributeMap rootAttribs = root.polyAttribs;
+ tessGeo = root.tessGeo;
+
+ if (family == GROUP) {
+ for (int i = 0; i < childCount; i++) {
+ PShapeOpenGL child = (PShapeOpenGL) children[i];
+ child.collectPolyAttribs();
+ }
+ } else {
+ for (int i = 0; i < polyAttribs.size(); i++) {
+ VertexAttribute attrib = polyAttribs.get(i);
+ tessGeo.initAttrib(attrib);
+ if (rootAttribs.containsKey(attrib.name)) {
+ VertexAttribute rattrib = rootAttribs.get(attrib.name);
+ if (rattrib.diff(attrib)) {
+ throw new RuntimeException("Children shapes cannot have different attributes with same name");
+ }
+ } else {
+ rootAttribs.put(attrib.name, attrib);
+ }
+ }
+ }
+ }
+
protected void tessellateImpl() {
tessGeo = root.tessGeo;
@@ -2703,6 +2901,11 @@ protected void tessellateImpl() {
lastPointIndexCache = -1;
if (family == GROUP) {
+ if (polyAttribs == null) {
+ polyAttribs = PGraphicsOpenGL.newAttributeMap();
+ collectPolyAttribs();
+ }
+
for (int i = 0; i < childCount; i++) {
PShapeOpenGL child = (PShapeOpenGL) children[i];
child.tessellateImpl();
@@ -2767,7 +2970,7 @@ protected void tessellateImpl() {
tessellator.resetCurveVertexCount();
}
tessellator.tessellatePolygon(solid, close,
- normalMode == NORMAL_MODE_AUTO);
+ normalMode == NORMAL_MODE_AUTO);
if (bez ||quad) restoreBezierVertexSettings();
if (curv) restoreCurveVertexSettings();
}
@@ -2835,7 +3038,7 @@ protected void tessellatePoint() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addPoint(x, y, z, fill, stroke);
tessellator.tessellatePoints();
@@ -2860,11 +3063,11 @@ protected void tessellateLine() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addLine(x1, y1, z1,
- x2, y2, z2,
- fill, stroke);
+ x2, y2, z2,
+ fill, stroke);
tessellator.tessellateLines();
}
@@ -2883,12 +3086,12 @@ protected void tessellateTriangle() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addTriangle(x1, y1, 0,
- x2, y2, 0,
- x3, y3, 0,
- fill, stroke);
+ x2, y2, 0,
+ x3, y3, 0,
+ fill, stroke);
tessellator.tessellateTriangles();
}
@@ -2910,13 +3113,13 @@ protected void tessellateQuad() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addQuad(x1, y1, 0,
- x2, y2, 0,
- x3, y3, 0,
- x4, y4, 0,
- stroke);
+ x2, y2, 0,
+ x3, y3, 0,
+ x4, y4, 0,
+ stroke);
tessellator.tessellateQuads();
}
@@ -2932,11 +3135,15 @@ protected void tessellateRect() {
b = params[1];
c = params[2];
d = params[3];
+ rounded = false;
if (params.length == 5) {
- mode = (int)(params[4]);
+ tl = params[4];
+ tr = params[4];
+ br = params[4];
+ bl = params[4];
+ rounded = true;
}
- rounded = false;
- } else if (params.length == 8 || params.length == 9) {
+ } else if (params.length == 8) {
a = params[0];
b = params[1];
c = params[2];
@@ -2945,34 +3152,31 @@ protected void tessellateRect() {
tr = params[5];
br = params[6];
bl = params[7];
- if (params.length == 9) {
- mode = (int)(params[8]);
- }
rounded = true;
}
float hradius, vradius;
switch (mode) {
- case CORNERS:
- break;
- case CORNER:
- c += a; d += b;
- break;
- case RADIUS:
- hradius = c;
- vradius = d;
- c = a + hradius;
- d = b + vradius;
- a -= hradius;
- b -= vradius;
- break;
- case CENTER:
- hradius = c / 2.0f;
- vradius = d / 2.0f;
- c = a + hradius;
- d = b + vradius;
- a -= hradius;
- b -= vradius;
+ case CORNERS:
+ break;
+ case CORNER:
+ c += a; d += b;
+ break;
+ case RADIUS:
+ hradius = c;
+ vradius = d;
+ c = a + hradius;
+ d = b + vradius;
+ a -= hradius;
+ b -= vradius;
+ break;
+ case CENTER:
+ hradius = c / 2.0f;
+ vradius = d / 2.0f;
+ c = a + hradius;
+ d = b + vradius;
+ a -= hradius;
+ b -= vradius;
}
if (a > c) {
@@ -2990,12 +3194,12 @@ protected void tessellateRect() {
if (bl > maxRounding) bl = maxRounding;
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
if (rounded) {
saveBezierVertexSettings();
inGeo.addRect(a, b, c, d, tl, tr, br, bl, stroke);
- tessellator.tessellatePolygon(false, true, true);
+ tessellator.tessellatePolygon(true, true, true);
restoreBezierVertexSettings();
} else {
inGeo.addRect(a, b, c, d, stroke);
@@ -3013,9 +3217,6 @@ protected void tessellateEllipse() {
b = params[1];
c = params[2];
d = params[3];
- if (params.length == 5) {
- mode = (int)(params[4]);
- }
}
float x = a;
@@ -3049,7 +3250,7 @@ protected void tessellateEllipse() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
inGeo.addEllipse(x, y, w, h, fill, stroke);
tessellator.tessellateTriangleFan();
@@ -3060,6 +3261,7 @@ protected void tessellateArc() {
float a = 0, b = 0, c = 0, d = 0;
float start = 0, stop = 0;
int mode = ellipseMode;
+ int arcMode = 0;
if (6 <= params.length) {
a = params[0];
@@ -3069,7 +3271,7 @@ protected void tessellateArc() {
start = params[4];
stop = params[5];
if (params.length == 7) {
- mode = (int)(params[6]);
+ arcMode = (int)(params[6]);
}
}
@@ -3104,13 +3306,13 @@ protected void tessellateArc() {
}
if (stop - start > TWO_PI) {
- start = 0;
- stop = TWO_PI;
+ // don't change start, it is visible in PIE mode
+ stop = start + TWO_PI;
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.setNormal(normalX, normalY, normalZ);
- inGeo.addArc(x, y, w, h, start, stop, fill, stroke, mode);
+ inGeo.addArc(x, y, w, h, start, stop, fill, stroke, arcMode);
tessellator.tessellateTriangleFan();
}
}
@@ -3128,7 +3330,7 @@ protected void tessellateBox() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
inGeo.addBox(w, h, d, fill, stroke);
tessellator.tessellateQuads();
}
@@ -3158,11 +3360,12 @@ protected void tessellateSphere() {
}
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
int[] indices = inGeo.addSphere(r, nu, nv, fill, stroke);
tessellator.tessellateTriangles(indices);
- if (savedDetailU != nu || savedDetailV != nv) {
+ if ((0 < savedDetailU && savedDetailU != nu) ||
+ (0 < savedDetailV && savedDetailV != nv)) {
pg.sphereDetail(savedDetailU, savedDetailV);
}
}
@@ -3172,7 +3375,7 @@ protected void tessellatePath() {
if (vertices == null) return;
inGeo.setMaterial(fillColor, strokeColor, strokeWeight,
- ambientColor, specularColor, emissiveColor, shininess);
+ ambientColor, specularColor, emissiveColor, shininess);
if (vertexCodeCount == 0) { // each point is a simple vertex
if (vertices[0].length == 2) { // tessellating 2D vertices
@@ -3182,7 +3385,7 @@ protected void tessellatePath() {
} else { // drawing 3D vertices
for (int i = 0; i < vertexCount; i++) {
inGeo.addVertex(vertices[i][X], vertices[i][Y], vertices[i][Z],
- VERTEX, false);
+ VERTEX, false);
}
}
} else { // coded set of vertices
@@ -3194,88 +3397,88 @@ protected void tessellatePath() {
for (int j = 0; j < vertexCodeCount; j++) {
switch (vertexCodes[j]) {
- case VERTEX:
- inGeo.addVertex(vertices[idx][X], vertices[idx][Y], VERTEX, brk);
- brk = false;
- idx++;
- break;
-
- case QUADRATIC_VERTEX:
- inGeo.addQuadraticVertex(vertices[idx+0][X], vertices[idx+0][Y], 0,
- vertices[idx+1][X], vertices[idx+1][Y], 0,
- brk);
- brk = false;
- idx += 2;
- break;
-
- case BEZIER_VERTEX:
- inGeo.addBezierVertex(vertices[idx+0][X], vertices[idx+0][Y], 0,
- vertices[idx+1][X], vertices[idx+1][Y], 0,
- vertices[idx+2][X], vertices[idx+2][Y], 0,
- brk);
- brk = false;
- idx += 3;
- break;
-
- case CURVE_VERTEX:
- inGeo.addCurveVertex(vertices[idx][X], vertices[idx][Y], 0, brk);
- brk = false;
- idx++;
- break;
-
- case BREAK:
- brk = true;
+ case VERTEX:
+ inGeo.addVertex(vertices[idx][X], vertices[idx][Y], VERTEX, brk);
+ brk = false;
+ idx++;
+ break;
+
+ case QUADRATIC_VERTEX:
+ inGeo.addQuadraticVertex(vertices[idx+0][X], vertices[idx+0][Y], 0,
+ vertices[idx+1][X], vertices[idx+1][Y], 0,
+ brk);
+ brk = false;
+ idx += 2;
+ break;
+
+ case BEZIER_VERTEX:
+ inGeo.addBezierVertex(vertices[idx+0][X], vertices[idx+0][Y], 0,
+ vertices[idx+1][X], vertices[idx+1][Y], 0,
+ vertices[idx+2][X], vertices[idx+2][Y], 0,
+ brk);
+ brk = false;
+ idx += 3;
+ break;
+
+ case CURVE_VERTEX:
+ inGeo.addCurveVertex(vertices[idx][X], vertices[idx][Y], 0, brk);
+ brk = false;
+ idx++;
+ break;
+
+ case BREAK:
+ brk = true;
}
}
} else { // tessellating a 3D path
for (int j = 0; j < vertexCodeCount; j++) {
switch (vertexCodes[j]) {
- case VERTEX:
- inGeo.addVertex(vertices[idx][X], vertices[idx][Y],
- vertices[idx][Z], brk);
- brk = false;
- idx++;
- break;
-
- case QUADRATIC_VERTEX:
- inGeo.addQuadraticVertex(vertices[idx+0][X],
- vertices[idx+0][Y],
- vertices[idx+0][Z],
- vertices[idx+1][X],
- vertices[idx+1][Y],
- vertices[idx+0][Z],
- brk);
- brk = false;
- idx += 2;
- break;
-
- case BEZIER_VERTEX:
- inGeo.addBezierVertex(vertices[idx+0][X],
- vertices[idx+0][Y],
- vertices[idx+0][Z],
- vertices[idx+1][X],
- vertices[idx+1][Y],
- vertices[idx+1][Z],
- vertices[idx+2][X],
- vertices[idx+2][Y],
- vertices[idx+2][Z],
- brk);
- brk = false;
- idx += 3;
- break;
-
- case CURVE_VERTEX:
- inGeo.addCurveVertex(vertices[idx][X],
- vertices[idx][Y],
- vertices[idx][Z],
- brk);
- brk = false;
- idx++;
- break;
-
- case BREAK:
- brk = true;
+ case VERTEX:
+ inGeo.addVertex(vertices[idx][X], vertices[idx][Y],
+ vertices[idx][Z], brk);
+ brk = false;
+ idx++;
+ break;
+
+ case QUADRATIC_VERTEX:
+ inGeo.addQuadraticVertex(vertices[idx+0][X],
+ vertices[idx+0][Y],
+ vertices[idx+0][Z],
+ vertices[idx+1][X],
+ vertices[idx+1][Y],
+ vertices[idx+0][Z],
+ brk);
+ brk = false;
+ idx += 2;
+ break;
+
+ case BEZIER_VERTEX:
+ inGeo.addBezierVertex(vertices[idx+0][X],
+ vertices[idx+0][Y],
+ vertices[idx+0][Z],
+ vertices[idx+1][X],
+ vertices[idx+1][Y],
+ vertices[idx+1][Z],
+ vertices[idx+2][X],
+ vertices[idx+2][Y],
+ vertices[idx+2][Z],
+ brk);
+ brk = false;
+ idx += 3;
+ break;
+
+ case CURVE_VERTEX:
+ inGeo.addCurveVertex(vertices[idx][X],
+ vertices[idx][Y],
+ vertices[idx][Z],
+ brk);
+ brk = false;
+ idx++;
+ break;
+
+ case BREAK:
+ brk = true;
}
}
}
@@ -3289,7 +3492,7 @@ protected void tessellatePath() {
saveCurveVertexSettings();
tessellator.resetCurveVertexCount();
}
- tessellator.tessellatePolygon(false, close, true);
+ tessellator.tessellatePolygon(true, close, true);
if (bez || quad) restoreBezierVertexSettings();
if (curv) restoreCurveVertexSettings();
}
@@ -3418,16 +3621,16 @@ protected void aggregateImpl() {
// this shape before tessellation, so they are applied now.
if (hasPolys) {
tessGeo.applyMatrixOnPolyGeometry(matrix,
- firstPolyVertex, lastPolyVertex);
+ firstPolyVertex, lastPolyVertex);
}
if (is3D()) {
if (hasLines) {
tessGeo.applyMatrixOnLineGeometry(matrix,
- firstLineVertex, lastLineVertex);
+ firstLineVertex, lastLineVertex);
}
if (hasPoints) {
tessGeo.applyMatrixOnPointGeometry(matrix,
- firstPointVertex, lastPointVertex);
+ firstPointVertex, lastPointVertex);
}
}
}
@@ -3470,7 +3673,7 @@ protected void updatePolyIndexCache() {
// This is a result of how the indices are updated for the
// leaf shapes.
cache.incCounts(gindex,
- cache.indexCount[n], cache.vertexCount[n]);
+ cache.indexCount[n], cache.vertexCount[n]);
} else {
gindex = cache.addNew(n);
}
@@ -3503,7 +3706,7 @@ protected void updatePolyIndexCache() {
// to be restarted as well to reflect the new index offset.
firstPolyVertex = lastPolyVertex =
- cache.vertexOffset[firstPolyIndexCache];
+ cache.vertexOffset[firstPolyIndexCache];
for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) {
int ioffset = cache.indexOffset[n];
int icount = cache.indexCount[n];
@@ -3516,7 +3719,7 @@ protected void updatePolyIndexCache() {
cache.indexOffset[n] = root.polyIndexOffset;
} else {
tessGeo.incPolyIndices(ioffset, ioffset + icount - 1,
- root.polyVertexRel);
+ root.polyVertexRel);
}
cache.vertexOffset[n] = root.polyVertexOffset;
if (is2D()) {
@@ -3538,7 +3741,7 @@ protected void updatePolyIndexCache() {
protected boolean startStrokedTex(int n) {
return image != null && (n == firstLineIndexCache ||
- n == firstPointIndexCache);
+ n == firstPointIndexCache);
}
@@ -3578,7 +3781,7 @@ protected void updateLineIndexCache() {
} else {
if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) {
cache.incCounts(gindex, cache.indexCount[n],
- cache.vertexCount[n]);
+ cache.vertexCount[n]);
} else {
gindex = cache.addNew(n);
}
@@ -3597,7 +3800,7 @@ protected void updateLineIndexCache() {
lastLineIndexCache = gindex;
} else {
firstLineVertex = lastLineVertex =
- cache.vertexOffset[firstLineIndexCache];
+ cache.vertexOffset[firstLineIndexCache];
for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) {
int ioffset = cache.indexOffset[n];
int icount = cache.indexCount[n];
@@ -3609,7 +3812,7 @@ protected void updateLineIndexCache() {
cache.indexOffset[n] = root.lineIndexOffset;
} else {
tessGeo.incLineIndices(ioffset, ioffset + icount - 1,
- root.lineVertexRel);
+ root.lineVertexRel);
}
cache.vertexOffset[n] = root.lineVertexOffset;
@@ -3646,7 +3849,7 @@ protected void updatePointIndexCache() {
// This is a result of how the indices are updated for the
// leaf shapes in aggregateImpl().
cache.incCounts(gindex, cache.indexCount[n],
- cache.vertexCount[n]);
+ cache.vertexCount[n]);
} else {
gindex = cache.addNew(n);
}
@@ -3657,7 +3860,7 @@ protected void updatePointIndexCache() {
if (-1 < child.firstPointVertex) {
if (firstPointVertex == -1) firstPointVertex = Integer.MAX_VALUE;
firstPointVertex = PApplet.min(firstPointVertex,
- child.firstPointVertex);
+ child.firstPointVertex);
}
if (-1 < child.lastPointVertex) {
lastPointVertex = PApplet.max(lastPointVertex, child.lastPointVertex);
@@ -3666,7 +3869,7 @@ protected void updatePointIndexCache() {
lastPointIndexCache = gindex;
} else {
firstPointVertex = lastPointVertex =
- cache.vertexOffset[firstPointIndexCache];
+ cache.vertexOffset[firstPointIndexCache];
for (int n = firstPointIndexCache; n <= lastPointIndexCache; n++) {
int ioffset = cache.indexOffset[n];
int icount = cache.indexCount[n];
@@ -3678,7 +3881,7 @@ protected void updatePointIndexCache() {
cache.indexOffset[n] = root.pointIndexOffset;
} else {
tessGeo.incPointIndices(ioffset, ioffset + icount - 1,
- root.pointVertexRel);
+ root.pointVertexRel);
}
cache.vertexOffset[n] = root.pointVertexOffset;
@@ -3725,70 +3928,79 @@ protected void initPolyBuffers() {
int sizei = size * PGL.SIZEOF_INT;
tessGeo.updatePolyVerticesBuffer();
- if (glPolyVertex == 0)
- glPolyVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex);
+ if (bufPolyVertex == null)
+ bufPolyVertex = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 4, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyVertex.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
- tessGeo.polyVerticesBuffer, glUsage);
+ tessGeo.polyVerticesBuffer, glUsage);
tessGeo.updatePolyColorsBuffer();
- if (glPolyColor == 0)
- glPolyColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor);
+ if (bufPolyColor == null)
+ bufPolyColor = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyColor.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
- tessGeo.polyColorsBuffer, glUsage);
+ tessGeo.polyColorsBuffer, glUsage);
tessGeo.updatePolyNormalsBuffer();
- if (glPolyNormal == 0)
- glPolyNormal = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal);
+ if (bufPolyNormal == null)
+ bufPolyNormal = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 3, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyNormal.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef,
- tessGeo.polyNormalsBuffer, glUsage);
+ tessGeo.polyNormalsBuffer, glUsage);
tessGeo.updatePolyTexCoordsBuffer();
- if (glPolyTexcoord == 0)
- glPolyTexcoord = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord);
+ if (bufPolyTexcoord == null)
+ bufPolyTexcoord = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 2, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyTexcoord.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef,
- tessGeo.polyTexCoordsBuffer, glUsage);
+ tessGeo.polyTexCoordsBuffer, glUsage);
tessGeo.updatePolyAmbientBuffer();
- if (glPolyAmbient == 0)
- glPolyAmbient = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient);
+ if (bufPolyAmbient == null)
+ bufPolyAmbient = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyAmbient.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
- tessGeo.polyAmbientBuffer, glUsage);
+ tessGeo.polyAmbientBuffer, glUsage);
tessGeo.updatePolySpecularBuffer();
- if (glPolySpecular == 0)
- glPolySpecular = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular);
+ if (bufPolySpecular == null)
+ bufPolySpecular = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolySpecular.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
- tessGeo.polySpecularBuffer, glUsage);
+ tessGeo.polySpecularBuffer, glUsage);
tessGeo.updatePolyEmissiveBuffer();
- if (glPolyEmissive == 0)
- glPolyEmissive = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive);
+ if (bufPolyEmissive == null)
+ bufPolyEmissive = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyEmissive.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
- tessGeo.polyEmissiveBuffer, glUsage);
+ tessGeo.polyEmissiveBuffer, glUsage);
tessGeo.updatePolyShininessBuffer();
- if (glPolyShininess == 0)
- glPolyShininess = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess);
+ if (bufPolyShininess == null)
+ bufPolyShininess = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyShininess.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizef,
- tessGeo.polyShininessBuffer, glUsage);
+ tessGeo.polyShininessBuffer, glUsage);
+
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ tessGeo.updateAttribBuffer(attrib.name);
+ if (!attrib.bufferCreated()) attrib.createBuffer(pgl);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, attrib.buf.glId);
+ pgl.bufferData(PGL.ARRAY_BUFFER, attrib.sizeInBytes(size),
+ tessGeo.polyAttribBuffers.get(name), glUsage);
+ }
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
tessGeo.updatePolyIndicesBuffer();
- if (glPolyIndex == 0)
- glPolyIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex);
+ if (bufPolyIndex == null)
+ bufPolyIndex = new VertexBuffer(pg, PGL.ELEMENT_ARRAY_BUFFER, 1, PGL.SIZEOF_INDEX, true);
+ pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, bufPolyIndex.glId);
pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER,
- tessGeo.polyIndexCount * PGL.SIZEOF_INDEX,
- tessGeo.polyIndicesBuffer, glUsage);
+ tessGeo.polyIndexCount * PGL.SIZEOF_INDEX,
+ tessGeo.polyIndicesBuffer, glUsage);
pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0);
}
@@ -3800,35 +4012,35 @@ protected void initLineBuffers() {
int sizei = size * PGL.SIZEOF_INT;
tessGeo.updateLineVerticesBuffer();
- if (glLineVertex == 0)
- glLineVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex);
+ if (bufLineVertex == null)
+ bufLineVertex = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 4, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineVertex.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
- tessGeo.lineVerticesBuffer, glUsage);
+ tessGeo.lineVerticesBuffer, glUsage);
tessGeo.updateLineColorsBuffer();
- if (glLineColor == 0)
- glLineColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor);
+ if (bufLineColor == null)
+ bufLineColor = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineColor.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
- tessGeo.lineColorsBuffer, glUsage);
+ tessGeo.lineColorsBuffer, glUsage);
tessGeo.updateLineDirectionsBuffer();
- if (glLineAttrib == 0)
- glLineAttrib = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib);
+ if (bufLineAttrib == null)
+ bufLineAttrib = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 4, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineAttrib.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
- tessGeo.lineDirectionsBuffer, glUsage);
+ tessGeo.lineDirectionsBuffer, glUsage);
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
tessGeo.updateLineIndicesBuffer();
- if (glLineIndex == 0)
- glLineIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex);
+ if (bufLineIndex == null)
+ bufLineIndex = new VertexBuffer(pg, PGL.ELEMENT_ARRAY_BUFFER, 1, PGL.SIZEOF_INDEX, true);
+ pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, bufLineIndex.glId);
pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER,
- tessGeo.lineIndexCount * PGL.SIZEOF_INDEX,
- tessGeo.lineIndicesBuffer, glUsage);
+ tessGeo.lineIndexCount * PGL.SIZEOF_INDEX,
+ tessGeo.lineIndicesBuffer, glUsage);
pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0);
}
@@ -3840,35 +4052,35 @@ protected void initPointBuffers() {
int sizei = size * PGL.SIZEOF_INT;
tessGeo.updatePointVerticesBuffer();
- if (glPointVertex == 0)
- glPointVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex);
+ if (bufPointVertex == null)
+ bufPointVertex = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 4, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointVertex.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef,
- tessGeo.pointVerticesBuffer, glUsage);
+ tessGeo.pointVerticesBuffer, glUsage);
tessGeo.updatePointColorsBuffer();
- if (glPointColor == 0)
- glPointColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor);
+ if (bufPointColor == null)
+ bufPointColor = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 1, PGL.SIZEOF_INT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointColor.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, sizei,
- tessGeo.pointColorsBuffer, glUsage);
+ tessGeo.pointColorsBuffer, glUsage);
tessGeo.updatePointOffsetsBuffer();
- if (glPointAttrib == 0)
- glPointAttrib = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib);
+ if (bufPointAttrib == null)
+ bufPointAttrib = new VertexBuffer(pg, PGL.ARRAY_BUFFER, 2, PGL.SIZEOF_FLOAT);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointAttrib.glId);
pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef,
- tessGeo.pointOffsetsBuffer, glUsage);
+ tessGeo.pointOffsetsBuffer, glUsage);
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
tessGeo.updatePointIndicesBuffer();
- if (glPointIndex == 0)
- glPointIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl);
- pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex);
+ if (bufPointIndex == null)
+ bufPointIndex = new VertexBuffer(pg, PGL.ELEMENT_ARRAY_BUFFER, 1, PGL.SIZEOF_INDEX, true);
+ pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, bufPointIndex.glId);
pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER,
- tessGeo.pointIndexCount * PGL.SIZEOF_INDEX,
- tessGeo.pointIndicesBuffer, glUsage);
+ tessGeo.pointIndexCount * PGL.SIZEOF_INDEX,
+ tessGeo.pointIndicesBuffer, glUsage);
pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, 0);
}
@@ -3877,163 +4089,30 @@ protected void initPointBuffers() {
protected boolean contextIsOutdated() {
boolean outdated = !pgl.contextIsCurrent(context);
if (outdated) {
- // Removing the VBOs from the renderer's list so they
- // doesn't get deleted by OpenGL. The VBOs were already
- // automatically disposed when the old context was
- // destroyed.
- PGraphicsOpenGL.removeVertexBufferObject(glPolyVertex, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyColor, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyNormal, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyTexcoord, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyAmbient, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolySpecular, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyEmissive, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyShininess, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPolyIndex, context);
-
- PGraphicsOpenGL.removeVertexBufferObject(glLineVertex, context);
- PGraphicsOpenGL.removeVertexBufferObject(glLineColor, context);
- PGraphicsOpenGL.removeVertexBufferObject(glLineAttrib, context);
- PGraphicsOpenGL.removeVertexBufferObject(glLineIndex, context);
-
- PGraphicsOpenGL.removeVertexBufferObject(glPointVertex, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPointColor, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPointAttrib, context);
- PGraphicsOpenGL.removeVertexBufferObject(glPointIndex, context);
-
- // The OpenGL resources have been already deleted
- // when the context changed. We only need to zero
- // them to avoid deleting them again when the GC
- // runs the finalizers of the disposed object.
- glPolyVertex = 0;
- glPolyColor = 0;
- glPolyNormal = 0;
- glPolyTexcoord = 0;
- glPolyAmbient = 0;
- glPolySpecular = 0;
- glPolyEmissive = 0;
- glPolyShininess = 0;
- glPolyIndex = 0;
-
- glLineVertex = 0;
- glLineColor = 0;
- glLineAttrib = 0;
- glLineIndex = 0;
-
- glPointVertex = 0;
- glPointColor = 0;
- glPointAttrib = 0;
- glPointIndex = 0;
- }
- return outdated;
- }
-
-
- ///////////////////////////////////////////////////////////
-
- //
-
- // Deletion methods
-
-
- protected void dispose() {
- deletePolyBuffers();
- deleteLineBuffers();
- deletePointBuffers();
- }
-
-
- protected void deletePolyBuffers() {
- if (glPolyVertex != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyVertex, context, pgl);
- glPolyVertex = 0;
- }
-
- if (glPolyColor != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyColor, context, pgl);
- glPolyColor = 0;
- }
-
- if (glPolyNormal != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyNormal, context, pgl);
- glPolyNormal = 0;
- }
-
- if (glPolyTexcoord != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyTexcoord, context, pgl);
- glPolyTexcoord = 0;
- }
-
- if (glPolyAmbient != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyAmbient, context, pgl);
- glPolyAmbient = 0;
- }
-
- if (glPolySpecular != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolySpecular, context, pgl);
- glPolySpecular = 0;
- }
-
- if (glPolyEmissive != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyEmissive, context, pgl);
- glPolyEmissive = 0;
- }
-
- if (glPolyShininess != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyShininess, context, pgl);
- glPolyShininess = 0;
- }
-
- if (glPolyIndex != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPolyIndex, context, pgl);
- glPolyIndex = 0;
- }
- }
-
-
- protected void deleteLineBuffers() {
- if (glLineVertex != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glLineVertex, context, pgl);
- glLineVertex = 0;
- }
-
- if (glLineColor != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glLineColor, context, pgl);
- glLineColor = 0;
- }
-
- if (glLineAttrib != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glLineAttrib, context, pgl);
- glLineAttrib = 0;
- }
-
- if (glLineIndex != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glLineIndex, context, pgl);
- glLineIndex = 0;
- }
- }
-
-
- protected void deletePointBuffers() {
- if (glPointVertex != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPointVertex, context, pgl);
- glPointVertex = 0;
- }
-
- if (glPointColor != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPointColor, context, pgl);
- glPointColor = 0;
- }
+ bufPolyVertex.dispose();
+ bufPolyColor.dispose();
+ bufPolyNormal.dispose();
+ bufPolyTexcoord.dispose();
+ bufPolyAmbient.dispose();
+ bufPolySpecular.dispose();
+ bufPolyEmissive.dispose();
+ bufPolyShininess.dispose();
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ attrib.buf.dispose();
+ }
+ bufPolyIndex.dispose();
- if (glPointAttrib != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPointAttrib, context, pgl);
- glPointAttrib = 0;
- }
+ bufLineVertex.dispose();
+ bufLineColor.dispose();
+ bufLineAttrib.dispose();
+ bufLineIndex.dispose();
- if (glPointIndex != 0) {
- PGraphicsOpenGL.deleteVertexBufferObject(glPointIndex, context, pgl);
- glPointIndex = 0;
+ bufPointVertex.dispose();
+ bufPointColor.dispose();
+ bufPointAttrib.dispose();
+ bufPointIndex.dispose();
}
+ return outdated;
}
@@ -4117,6 +4196,17 @@ protected void updateGeometryImpl() {
firstModifiedPolyShininess = PConstants.MAX_INT;
lastModifiedPolyShininess = PConstants.MIN_INT;
}
+ for (String name: polyAttribs.keySet()) {
+ VertexAttribute attrib = polyAttribs.get(name);
+ if (attrib.modified) {
+ int offset = firstModifiedPolyVertex;
+ int size = lastModifiedPolyVertex - offset + 1;
+ copyPolyAttrib(attrib, offset, size);
+ attrib.modified = false;
+ attrib.firstModified = PConstants.MAX_INT;
+ attrib.lastModified = PConstants.MIN_INT;
+ }
+ }
if (modifiedLineVertices) {
int offset = firstModifiedLineVertex;
@@ -4174,10 +4264,10 @@ protected void updateGeometryImpl() {
protected void copyPolyVertices(int offset, int size) {
tessGeo.updatePolyVerticesBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyVertex.glId);
tessGeo.polyVerticesBuffer.position(4 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT,
- 4 * size * PGL.SIZEOF_FLOAT, tessGeo.polyVerticesBuffer);
+ 4 * size * PGL.SIZEOF_FLOAT, tessGeo.polyVerticesBuffer);
tessGeo.polyVerticesBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4185,10 +4275,10 @@ protected void copyPolyVertices(int offset, int size) {
protected void copyPolyColors(int offset, int size) {
tessGeo.updatePolyColorsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyColor.glId);
tessGeo.polyColorsBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT,
- size * PGL.SIZEOF_INT, tessGeo.polyColorsBuffer);
+ size * PGL.SIZEOF_INT, tessGeo.polyColorsBuffer);
tessGeo.polyColorsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4196,10 +4286,10 @@ protected void copyPolyColors(int offset, int size) {
protected void copyPolyNormals(int offset, int size) {
tessGeo.updatePolyNormalsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyNormal.glId);
tessGeo.polyNormalsBuffer.position(3 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 3 * offset * PGL.SIZEOF_FLOAT,
- 3 * size * PGL.SIZEOF_FLOAT, tessGeo.polyNormalsBuffer);
+ 3 * size * PGL.SIZEOF_FLOAT, tessGeo.polyNormalsBuffer);
tessGeo.polyNormalsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4207,10 +4297,10 @@ protected void copyPolyNormals(int offset, int size) {
protected void copyPolyTexCoords(int offset, int size) {
tessGeo.updatePolyTexCoordsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyTexcoord.glId);
tessGeo.polyTexCoordsBuffer.position(2 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT,
- 2 * size * PGL.SIZEOF_FLOAT, tessGeo.polyTexCoordsBuffer);
+ 2 * size * PGL.SIZEOF_FLOAT, tessGeo.polyTexCoordsBuffer);
tessGeo.polyTexCoordsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4218,10 +4308,10 @@ protected void copyPolyTexCoords(int offset, int size) {
protected void copyPolyAmbient(int offset, int size) {
tessGeo.updatePolyAmbientBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyAmbient.glId);
tessGeo.polyAmbientBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT,
- size * PGL.SIZEOF_INT, tessGeo.polyAmbientBuffer);
+ size * PGL.SIZEOF_INT, tessGeo.polyAmbientBuffer);
tessGeo.polyAmbientBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4229,10 +4319,10 @@ protected void copyPolyAmbient(int offset, int size) {
protected void copyPolySpecular(int offset, int size) {
tessGeo.updatePolySpecularBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolySpecular.glId);
tessGeo.polySpecularBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT,
- size * PGL.SIZEOF_INT, tessGeo.polySpecularBuffer);
+ size * PGL.SIZEOF_INT, tessGeo.polySpecularBuffer);
tessGeo.polySpecularBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4240,10 +4330,10 @@ protected void copyPolySpecular(int offset, int size) {
protected void copyPolyEmissive(int offset, int size) {
tessGeo.updatePolyEmissiveBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyEmissive.glId);
tessGeo.polyEmissiveBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT,
- size * PGL.SIZEOF_INT, tessGeo.polyEmissiveBuffer);
+ size * PGL.SIZEOF_INT, tessGeo.polyEmissiveBuffer);
tessGeo.polyEmissiveBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4251,21 +4341,33 @@ protected void copyPolyEmissive(int offset, int size) {
protected void copyPolyShininess(int offset, int size) {
tessGeo.updatePolyShininessBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPolyShininess.glId);
tessGeo.polyShininessBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_FLOAT,
- size * PGL.SIZEOF_FLOAT, tessGeo.polyShininessBuffer);
+ size * PGL.SIZEOF_FLOAT, tessGeo.polyShininessBuffer);
tessGeo.polyShininessBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
+ protected void copyPolyAttrib(VertexAttribute attrib, int offset, int size) {
+ tessGeo.updateAttribBuffer(attrib.name, offset, size);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, attrib.buf.glId);
+ Buffer buf = tessGeo.polyAttribBuffers.get(attrib.name);
+ buf.position(attrib.size * offset);
+ pgl.bufferSubData(PGL.ARRAY_BUFFER, attrib.sizeInBytes(offset),
+ attrib.sizeInBytes(size), buf);
+ buf.rewind();
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
+ }
+
+
protected void copyLineVertices(int offset, int size) {
tessGeo.updateLineVerticesBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineVertex.glId);
tessGeo.lineVerticesBuffer.position(4 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT,
- 4 * size * PGL.SIZEOF_FLOAT, tessGeo.lineVerticesBuffer);
+ 4 * size * PGL.SIZEOF_FLOAT, tessGeo.lineVerticesBuffer);
tessGeo.lineVerticesBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4273,10 +4375,10 @@ protected void copyLineVertices(int offset, int size) {
protected void copyLineColors(int offset, int size) {
tessGeo.updateLineColorsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineColor.glId);
tessGeo.lineColorsBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT,
- size * PGL.SIZEOF_INT, tessGeo.lineColorsBuffer);
+ size * PGL.SIZEOF_INT, tessGeo.lineColorsBuffer);
tessGeo.lineColorsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4284,10 +4386,10 @@ protected void copyLineColors(int offset, int size) {
protected void copyLineAttributes(int offset, int size) {
tessGeo.updateLineDirectionsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufLineAttrib.glId);
tessGeo.lineDirectionsBuffer.position(4 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT,
- 4 * size * PGL.SIZEOF_FLOAT, tessGeo.lineDirectionsBuffer);
+ 4 * size * PGL.SIZEOF_FLOAT, tessGeo.lineDirectionsBuffer);
tessGeo.lineDirectionsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4295,10 +4397,10 @@ protected void copyLineAttributes(int offset, int size) {
protected void copyPointVertices(int offset, int size) {
tessGeo.updatePointVerticesBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointVertex.glId);
tessGeo.pointVerticesBuffer.position(4 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT,
- 4 * size * PGL.SIZEOF_FLOAT, tessGeo.pointVerticesBuffer);
+ 4 * size * PGL.SIZEOF_FLOAT, tessGeo.pointVerticesBuffer);
tessGeo.pointVerticesBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4306,10 +4408,10 @@ protected void copyPointVertices(int offset, int size) {
protected void copyPointColors(int offset, int size) {
tessGeo.updatePointColorsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointColor.glId);
tessGeo.pointColorsBuffer.position(offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, offset * PGL.SIZEOF_INT,
- size * PGL.SIZEOF_INT,tessGeo.pointColorsBuffer);
+ size * PGL.SIZEOF_INT,tessGeo.pointColorsBuffer);
tessGeo.pointColorsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4317,10 +4419,10 @@ protected void copyPointColors(int offset, int size) {
protected void copyPointAttributes(int offset, int size) {
tessGeo.updatePointOffsetsBuffer(offset, size);
- pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib);
+ pgl.bindBuffer(PGL.ARRAY_BUFFER, bufPointAttrib.glId);
tessGeo.pointOffsetsBuffer.position(2 * offset);
pgl.bufferSubData(PGL.ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT,
- 2 * size * PGL.SIZEOF_FLOAT, tessGeo.pointOffsetsBuffer);
+ 2 * size * PGL.SIZEOF_FLOAT, tessGeo.pointOffsetsBuffer);
tessGeo.pointOffsetsBuffer.rewind();
pgl.bindBuffer(PGL.ARRAY_BUFFER, 0);
}
@@ -4390,6 +4492,14 @@ protected void setModifiedPolyShininess(int first, int last) {
}
+ protected void setModifiedPolyAttrib(VertexAttribute attrib, int first, int last) {
+ if (first < attrib.firstModified) attrib.firstModified = first;
+ if (last > attrib.lastModified) attrib.lastModified = last;
+ attrib.modified = true;
+ modified = true;
+ }
+
+
protected void setModifiedLineVertices(int first, int last) {
if (first < firstModifiedLineVertex) firstModifiedLineVertex = first;
if (last > lastModifiedLineVertex) lastModifiedLineVertex = last;
@@ -4509,7 +4619,6 @@ public void enableStyle() {
}
- // Applies the styles of g.
@Override
protected void styles(PGraphics g) {
if (g instanceof PGraphicsOpenGL) {
@@ -4556,9 +4665,11 @@ protected void styles(PGraphics g) {
// Rendering methods
+ /*
public void draw() {
draw(pg);
}
+ */
@Override
@@ -4583,32 +4694,119 @@ public void draw(PGraphics g) {
}
render(gl, tex);
}
-
} else {
render(gl, image);
}
-
post(gl);
}
} else {
- // The renderer is not PGraphicsOpenGL, which probably
- // means that the draw() method is being called by the
- // recorder. We just use the default drawing from the
- // parent class.
- super.draw(g);
+ if (family == GEOMETRY) {
+ inGeoToVertices();
+ }
+ pre(g);
+ drawImpl(g);
+ post(g);
+ }
+ }
+
+
+ private void inGeoToVertices() {
+ vertexCount = 0;
+ vertexCodeCount = 0;
+ if (inGeo.codeCount == 0) {
+ for (int i = 0; i < inGeo.vertexCount; i++) {
+ int index = 3 * i;
+ float x = inGeo.vertices[index++];
+ float y = inGeo.vertices[index ];
+ super.vertex(x, y);
+ }
+ } else {
+ int v;
+ float x, y;
+ float cx, cy;
+ float x2, y2, x3, y3, x4, y4;
+ int idx = 0;
+ boolean insideContour = false;
+
+ for (int j = 0; j < inGeo.codeCount; j++) {
+ switch (inGeo.codes[j]) {
+
+ case VERTEX:
+ v = 3 * idx;
+ x = inGeo.vertices[v++];
+ y = inGeo.vertices[v ];
+ super.vertex(x, y);
+
+ idx++;
+ break;
+
+ case QUADRATIC_VERTEX:
+ v = 3 * idx;
+ cx = inGeo.vertices[v++];
+ cy = inGeo.vertices[v];
+
+ v = 3 * (idx + 1);
+ x3 = inGeo.vertices[v++];
+ y3 = inGeo.vertices[v];
+
+ super.quadraticVertex(cx, cy, x3, y3);
+
+ idx += 2;
+ break;
+
+ case BEZIER_VERTEX:
+ v = 3 * idx;
+ x2 = inGeo.vertices[v++];
+ y2 = inGeo.vertices[v ];
+
+ v = 3 * (idx + 1);
+ x3 = inGeo.vertices[v++];
+ y3 = inGeo.vertices[v ];
+
+ v = 3 * (idx + 2);
+ x4 = inGeo.vertices[v++];
+ y4 = inGeo.vertices[v ];
+
+ super.bezierVertex(x2, y2, x3, y3, x4, y4);
+
+ idx += 3;
+ break;
+
+ case CURVE_VERTEX:
+ v = 3 * idx;
+ x = inGeo.vertices[v++];
+ y = inGeo.vertices[v ];
+
+ super.curveVertex(x, y);
+
+ idx++;
+ break;
+
+ case BREAK:
+ if (insideContour) {
+ super.endContourImpl();
+ }
+ super.beginContourImpl();
+ insideContour = true;
+ }
+ }
+ if (insideContour) {
+ super.endContourImpl();
+ }
}
}
// Returns true if some child shapes below this one either
- // use different texture maps or have stroked textures,
+ // use different texture maps (or only one texture is used by some while
+ // others are untextured), or have stroked textures,
// so they cannot rendered in a single call.
// Or accurate 2D mode is enabled, which forces each
// shape to be rendered separately.
protected boolean fragmentedGroup(PGraphicsOpenGL g) {
return g.getHint(DISABLE_OPTIMIZED_STROKE) ||
- (textures != null && 1 < textures.size()) ||
- strokedTexture;
+ (textures != null && (1 < textures.size() || untexChild)) ||
+ strokedTexture;
}
@@ -4651,7 +4849,7 @@ protected void render(PGraphicsOpenGL g, PImage texture) {
if (root == null) {
// Some error. Root should never be null. At least it should be 'this'.
throw new RuntimeException("Error rendering PShapeOpenGL, root shape is " +
- "null");
+ "null");
}
if (hasPolys) {
@@ -4693,9 +4891,9 @@ protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) {
IndexCache cache = tessGeo.polyIndexCache;
for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) {
if (is3D() || (tex != null && (firstLineIndexCache == -1 ||
- n < firstLineIndexCache) &&
- (firstPointIndexCache == -1 ||
- n < firstPointIndexCache))) {
+ n < firstLineIndexCache) &&
+ (firstPointIndexCache == -1 ||
+ n < firstPointIndexCache))) {
// Rendering fill triangles, which can be lit and textured.
if (!renderingFill) {
shader = g.getPolyShader(g.lights, tex != null);
@@ -4728,37 +4926,48 @@ protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) {
int icount = cache.indexCount[n];
int voffset = cache.vertexOffset[n];
- shader.setVertexAttribute(root.glPolyVertex, 4, PGL.FLOAT,
- 0, 4 * voffset * PGL.SIZEOF_FLOAT);
- shader.setColorAttribute(root.glPolyColor, 4, PGL.UNSIGNED_BYTE,
- 0, 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setVertexAttribute(root.bufPolyVertex.glId, 4, PGL.FLOAT,
+ 0, 4 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setColorAttribute(root.bufPolyColor.glId, 4, PGL.UNSIGNED_BYTE,
+ 0, 4 * voffset * PGL.SIZEOF_BYTE);
if (g.lights) {
- shader.setNormalAttribute(root.glPolyNormal, 3, PGL.FLOAT,
- 0, 3 * voffset * PGL.SIZEOF_FLOAT);
- shader.setAmbientAttribute(root.glPolyAmbient, 4, PGL.UNSIGNED_BYTE,
- 0, 4 * voffset * PGL.SIZEOF_BYTE);
- shader.setSpecularAttribute(root.glPolySpecular, 4, PGL.UNSIGNED_BYTE,
- 0, 4 * voffset * PGL.SIZEOF_BYTE);
- shader.setEmissiveAttribute(root.glPolyEmissive, 4, PGL.UNSIGNED_BYTE,
- 0, 4 * voffset * PGL.SIZEOF_BYTE);
- shader.setShininessAttribute(root.glPolyShininess, 1, PGL.FLOAT,
- 0, voffset * PGL.SIZEOF_FLOAT);
+ shader.setNormalAttribute(root.bufPolyNormal.glId, 3, PGL.FLOAT,
+ 0, 3 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setAmbientAttribute(root.bufPolyAmbient.glId, 4, PGL.UNSIGNED_BYTE,
+ 0, 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setSpecularAttribute(root.bufPolySpecular.glId, 4, PGL.UNSIGNED_BYTE,
+ 0, 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setEmissiveAttribute(root.bufPolyEmissive.glId, 4, PGL.UNSIGNED_BYTE,
+ 0, 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setShininessAttribute(root.bufPolyShininess.glId, 1, PGL.FLOAT,
+ 0, voffset * PGL.SIZEOF_FLOAT);
}
if (g.lights || needNormals) {
- shader.setNormalAttribute(root.glPolyNormal, 3, PGL.FLOAT,
- 0, 3 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setNormalAttribute(root.bufPolyNormal.glId, 3, PGL.FLOAT,
+ 0, 3 * voffset * PGL.SIZEOF_FLOAT);
}
if (tex != null || needTexCoords) {
- shader.setTexcoordAttribute(root.glPolyTexcoord, 2, PGL.FLOAT,
- 0, 2 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setTexcoordAttribute(root.bufPolyTexcoord.glId, 2, PGL.FLOAT,
+ 0, 2 * voffset * PGL.SIZEOF_FLOAT);
shader.setTexture(tex);
}
- shader.draw(root.glPolyIndex, icount, ioffset);
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (!attrib.active(shader)) continue;
+ attrib.bind(pgl);
+ shader.setAttributeVBO(attrib.glLoc, attrib.buf.glId,
+ attrib.tessSize, attrib.type,
+ attrib.isColor(), 0, attrib.sizeInBytes(voffset));
+ }
+
+ shader.draw(root.bufPolyIndex.glId, icount, ioffset);
}
+ for (VertexAttribute attrib: polyAttribs.values()) {
+ if (attrib.active(shader)) attrib.unbind(pgl);
+ }
if (shader != null && shader.bound()) {
shader.unbind();
}
@@ -4870,14 +5079,14 @@ protected void renderLines(PGraphicsOpenGL g) {
int icount = cache.indexCount[n];
int voffset = cache.vertexOffset[n];
- shader.setVertexAttribute(root.glLineVertex, 4, PGL.FLOAT,
- 0, 4 * voffset * PGL.SIZEOF_FLOAT);
- shader.setColorAttribute(root.glLineColor, 4, PGL.UNSIGNED_BYTE,
- 0, 4 * voffset * PGL.SIZEOF_BYTE);
- shader.setLineAttribute(root.glLineAttrib, 4, PGL.FLOAT,
- 0, 4 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setVertexAttribute(root.bufLineVertex.glId, 4, PGL.FLOAT,
+ 0, 4 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setColorAttribute(root.bufLineColor.glId, 4, PGL.UNSIGNED_BYTE,
+ 0, 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setLineAttribute(root.bufLineAttrib.glId, 4, PGL.FLOAT,
+ 0, 4 * voffset * PGL.SIZEOF_FLOAT);
- shader.draw(root.glLineIndex, icount, ioffset);
+ shader.draw(root.bufLineIndex.glId, icount, ioffset);
}
shader.unbind();
@@ -4967,14 +5176,14 @@ protected void renderPoints(PGraphicsOpenGL g) {
int icount = cache.indexCount[n];
int voffset = cache.vertexOffset[n];
- shader.setVertexAttribute(root.glPointVertex, 4, PGL.FLOAT,
- 0, 4 * voffset * PGL.SIZEOF_FLOAT);
- shader.setColorAttribute(root.glPointColor, 4, PGL.UNSIGNED_BYTE,
- 0, 4 * voffset * PGL.SIZEOF_BYTE);
- shader.setPointAttribute(root.glPointAttrib, 2, PGL.FLOAT,
- 0, 2 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setVertexAttribute(root.bufPointVertex.glId, 4, PGL.FLOAT,
+ 0, 4 * voffset * PGL.SIZEOF_FLOAT);
+ shader.setColorAttribute(root.bufPointColor.glId, 4, PGL.UNSIGNED_BYTE,
+ 0, 4 * voffset * PGL.SIZEOF_BYTE);
+ shader.setPointAttribute(root.bufPointAttrib.glId, 2, PGL.FLOAT,
+ 0, 2 * voffset * PGL.SIZEOF_FLOAT);
- shader.draw(root.glPointIndex, icount, ioffset);
+ shader.draw(root.bufPointIndex.glId, icount, ioffset);
}
shader.unbind();
@@ -5008,9 +5217,9 @@ protected void rawPoints(PGraphicsOpenGL g) {
if (0 < size) { // round point
weight = +size / 0.5f;
perim = PApplet.min(PGraphicsOpenGL.MAX_POINT_ACCURACY,
- PApplet.max(PGraphicsOpenGL.MIN_POINT_ACCURACY,
- (int) (TWO_PI * weight /
- PGraphicsOpenGL.POINT_ACCURACY_FACTOR))) + 1;
+ PApplet.max(PGraphicsOpenGL.MIN_POINT_ACCURACY,
+ (int) (TWO_PI * weight /
+ PGraphicsOpenGL.POINT_ACCURACY_FACTOR))) + 1;
} else { // Square point
weight = -size / 0.5f;
perim = 5;
diff --git a/libs/processing-core/src/main/java/processing/opengl/PSurfaceGLES.java b/libs/processing-core/src/main/java/processing/opengl/PSurfaceGLES.java
new file mode 100644
index 000000000..b9a86ae87
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/opengl/PSurfaceGLES.java
@@ -0,0 +1,604 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-21 The Processing Foundation
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+*/
+
+package processing.opengl;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLSurfaceView.EGLConfigChooser;
+import android.opengl.GLSurfaceView.Renderer;
+import android.service.wallpaper.WallpaperService;
+import android.support.wearable.watchface.Gles2WatchFaceService;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import processing.android.AppComponent;
+import processing.android.PFragment;
+import processing.core.PApplet;
+import processing.core.PGraphics;
+import processing.core.PSurfaceNone;
+
+public class PSurfaceGLES extends PSurfaceNone {
+ public PGLES pgl;
+ private SurfaceViewGLES glsurf;
+
+ public PSurfaceGLES() { }
+
+ public PSurfaceGLES(PGraphics graphics, AppComponent component, SurfaceHolder holder) {
+ this.sketch = graphics.parent;
+ this.graphics = graphics;
+ this.component = component;
+ this.pgl = (PGLES)((PGraphicsOpenGL)graphics).pgl;
+ if (component.getKind() == AppComponent.FRAGMENT) {
+ PFragment frag = (PFragment)component;
+ activity = frag.getActivity();
+ surfaceView = new SurfaceViewGLES(activity, null);
+ } else if (component.getKind() == AppComponent.WALLPAPER) {
+ wallpaper = (WallpaperService)component;
+ surfaceView = new SurfaceViewGLES(wallpaper, holder);
+ } else if (component.getKind() == AppComponent.WATCHFACE) {
+ watchface = (Gles2WatchFaceService)component;
+ // Set as ready here, as watch faces don't have a surface view with a
+ // surfaceCreate() event to do it.
+ surfaceReady = true;
+ }
+ glsurf = (SurfaceViewGLES)surfaceView;
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ if (glsurf != null) {
+ glsurf.dispose();
+ glsurf = null;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ // Thread handling
+
+ @Override
+ protected void callDraw() {
+ component.requestDraw();
+ if (component.canDraw() && glsurf != null) {
+ glsurf.requestRender();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ // GL SurfaceView
+
+ public class SurfaceViewGLES extends GLSurfaceView {
+ SurfaceHolder holder;
+
+ public SurfaceViewGLES(Context context, SurfaceHolder holder) {
+ super(context);
+ this.holder = holder;
+
+ // Check if the system supports OpenGL ES 2.0.
+ final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
+ final boolean supportsGLES2 = configurationInfo.reqGlEsVersion >= 0x20000;
+
+ if (!supportsGLES2) {
+ throw new RuntimeException("OpenGL ES 2.0 is not supported by this device.");
+ }
+
+ SurfaceHolder h = getHolder();
+ h.addCallback(this);
+
+ // Tells the default EGLContextFactory and EGLConfigChooser to create an GLES2 context.
+ setEGLContextClientVersion(PGLES.version);
+ setPreserveEGLContextOnPause(true);
+
+ int samples = sketch.sketchSmooth();
+ if (1 < samples) {
+ setEGLConfigChooser(getConfigChooser(samples));
+ } else {
+ // use default EGL config chooser for now...
+// setEGLConfigChooser(getConfigChooser(5, 6, 5, 4, 16, 1, samples));
+
+ // Some notes on how to choose an EGL configuration:
+ // https://github.com/mapbox/mapbox-gl-native/issues/574
+ // http://malideveloper.arm.com/sample-code/selecting-the-correct-eglconfig/
+ }
+
+
+ // The renderer can be set only once.
+ setRenderer(getRenderer());
+ setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+
+ surfaceReady = false; // Will be ready when the surfaceCreated() event is called
+ }
+
+ @Override
+ public SurfaceHolder getHolder() {
+ if (holder == null) {
+ return super.getHolder();
+ } else {
+ return holder;
+ }
+ }
+
+ public void dispose() {
+ super.destroyDrawingCache();
+ super.onDetachedFromWindow();
+ }
+
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+
+// if (PApplet.DEBUG) {
+// System.out.println("SketchSurfaceView3D.surfaceChanged() " + w + " " + h);
+// }
+// System.out.println("SketchSurfaceView3D.surfaceChanged() " + w + " " + h + " " + sketch);
+// sketch.surfaceChanged();
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ super.surfaceCreated(holder);
+ surfaceReady = true;
+ if (requestedThreadStart) {
+ startThread();
+ }
+ if (PApplet.DEBUG) {
+ System.out.println("surfaceCreated()");
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ super.surfaceDestroyed(holder);
+ if (PApplet.DEBUG) {
+ System.out.println("surfaceDestroyed()");
+ }
+ }
+
+
+ // Inform the view that the window focus has changed.
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ sketch.surfaceWindowFocusChanged(hasFocus);
+ }
+
+ // Do we need these to capture events...?
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean fullscreen = sketch.width == sketch.displayWidth &&
+ sketch.height == sketch.displayHeight;
+ if (fullscreen && PApplet.SDK < 19) {
+ // The best we can do pre-KitKat to keep the navigation bar hidden
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ }
+ return sketch.surfaceTouchEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int code, android.view.KeyEvent event) {
+ sketch.surfaceKeyDown(code, event);
+ return super.onKeyDown(code, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int code, android.view.KeyEvent event) {
+ sketch.surfaceKeyUp(code, event);
+ return super.onKeyUp(code, event);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ // Android specific classes (Renderer, ConfigChooser)
+
+
+ public RendererGLES getRenderer() {
+// renderer = new AndroidRenderer();
+// return renderer;
+ return new RendererGLES();
+ }
+
+
+ public ContextFactoryGLES getContextFactory() {
+ return new ContextFactoryGLES();
+ }
+
+
+ public ConfigChooserGLES getConfigChooser(int samples) {
+ return new ConfigChooserGLES(5, 6, 5, 4, 16, 1, samples);
+// return new AndroidConfigChooser(8, 8, 8, 8, 16, 8, samples);
+ }
+
+
+ public ConfigChooserGLES getConfigChooser(int r, int g, int b, int a,
+ int d, int s, int samples) {
+ return new ConfigChooserGLES(r, g, b, a, d, s, samples);
+ }
+
+
+ protected class RendererGLES implements Renderer {
+
+ public RendererGLES() {
+ }
+
+ @Override
+ public void onDrawFrame(GL10 igl) {
+ pgl.getGL(igl);
+ sketch.handleDraw();
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 igl, int iwidth, int iheight) {
+ if (PApplet.DEBUG) {
+ System.out.println("AndroidRenderer.onSurfaceChanged() " + iwidth + " " + iheight);
+ }
+
+ pgl.getGL(igl);
+
+ // Here is where we should initialize native libs...
+ // lib.init(iwidth, iheight);
+
+// sketch.surfaceChanged();
+// graphics.surfaceChanged();
+//
+// sketch.setSize(iwidth, iheight);
+// graphics.setSize(sketch.sketchWidth(), sketch.sketchHeight());
+ sketch.surfaceChanged();
+ sketch.setSize(iwidth, iheight);
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 igl, EGLConfig config) {
+ pgl.init(igl);
+ }
+ }
+
+
+ protected class ContextFactoryGLES implements
+ GLSurfaceView.EGLContextFactory {
+ public EGLContext createContext(EGL10 egl, EGLDisplay display,
+ EGLConfig eglConfig) {
+ int[] attrib_list = { PGLES.EGL_CONTEXT_CLIENT_VERSION, PGLES.version,
+ EGL10.EGL_NONE };
+ EGLContext context = egl.eglCreateContext(display, eglConfig,
+ EGL10.EGL_NO_CONTEXT,
+ attrib_list);
+ return context;
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display,
+ EGLContext context) {
+ egl.eglDestroyContext(display, context);
+ }
+ }
+
+
+ protected class ConfigChooserGLES implements EGLConfigChooser {
+ // Desired size (in bits) for the rgba color, depth and stencil buffers.
+ public int redTarget;
+ public int greenTarget;
+ public int blueTarget;
+ public int alphaTarget;
+ public int depthTarget;
+ public int stencilTarget;
+
+ // Actual rgba color, depth and stencil sizes (in bits) supported by the
+ // device.
+ public int redBits;
+ public int greenBits;
+ public int blueBits;
+ public int alphaBits;
+ public int depthBits;
+ public int stencilBits;
+ public int[] tempValue = new int[1];
+
+ public int numSamples;
+
+ /*
+ The GLES2 extensions supported are:
+ GL_OES_rgb8_rgba8 GL_OES_depth24 GL_OES_vertex_half_float
+ GL_OES_texture_float GL_OES_texture_half_float
+ GL_OES_element_index_uint GL_OES_mapbuffer
+ GL_OES_fragment_precision_high GL_OES_compressed_ETC1_RGB8_texture
+ GL_OES_EGL_image GL_OES_required_internalformat GL_OES_depth_texture
+ GL_OES_get_program_binary GL_OES_packed_depth_stencil
+ GL_OES_standard_derivatives GL_OES_vertex_array_object GL_OES_egl_sync
+ GL_EXT_multi_draw_arrays GL_EXT_texture_format_BGRA8888
+ GL_EXT_discard_framebuffer GL_EXT_shader_texture_lod
+ GL_IMG_shader_binary GL_IMG_texture_compression_pvrtc
+ GL_IMG_texture_stream2 GL_IMG_texture_npot
+ GL_IMG_texture_format_BGRA8888 GL_IMG_read_format
+ GL_IMG_program_binary GL_IMG_multisampled_render_to_texture
+ */
+
+ /*
+ // The attributes we want in the frame buffer configuration for Processing.
+ // For more details on other attributes, see:
+ // http://www.khronos.org/opengles/documentation/opengles1_0/html/eglChooseConfig.html
+ protected int[] configAttribsGL_MSAA = {
+ EGL10.EGL_RED_SIZE, 5,
+ EGL10.EGL_GREEN_SIZE, 6,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_ALPHA_SIZE, 4,
+ EGL10.EGL_DEPTH_SIZE, 16,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_SAMPLE_BUFFERS, 1,
+ EGL10.EGL_SAMPLES, 2,
+ EGL10.EGL_NONE };
+
+ protected int[] configAttribsGL_CovMSAA = {
+ EGL10.EGL_RED_SIZE, 5,
+ EGL10.EGL_GREEN_SIZE, 6,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_ALPHA_SIZE, 4,
+ EGL10.EGL_DEPTH_SIZE, 16,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_COVERAGE_BUFFERS_NV, 1,
+ EGL_COVERAGE_SAMPLES_NV, 2,
+ EGL10.EGL_NONE };
+
+ protected int[] configAttribsGL_NoMSAA = {
+ EGL10.EGL_RED_SIZE, 5,
+ EGL10.EGL_GREEN_SIZE, 6,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_ALPHA_SIZE, 4,
+ EGL10.EGL_DEPTH_SIZE, 16,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE };
+
+ protected int[] configAttribsGL_Good = {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 8,
+ EGL10.EGL_DEPTH_SIZE, 16,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE };
+
+ protected int[] configAttribsGL_TestMSAA = {
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_SAMPLE_BUFFERS, 1,
+ EGL10.EGL_SAMPLES, 2,
+ EGL10.EGL_NONE };
+ */
+
+ protected int[] attribsNoMSAA = {
+ EGL10.EGL_RENDERABLE_TYPE, PGLES.EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_SAMPLE_BUFFERS, 0,
+ EGL10.EGL_NONE };
+
+ public ConfigChooserGLES(int rbits, int gbits, int bbits, int abits,
+ int dbits, int sbits, int samples) {
+ redTarget = rbits;
+ greenTarget = gbits;
+ blueTarget = bbits;
+ alphaTarget = abits;
+ depthTarget = dbits;
+ stencilTarget = sbits;
+ numSamples = samples;
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ EGLConfig[] configs = null;
+ if (1 < numSamples) {
+ int[] attribs = new int[] {
+ EGL10.EGL_RENDERABLE_TYPE, PGLES.EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_SAMPLE_BUFFERS, 1,
+ EGL10.EGL_SAMPLES, numSamples,
+ EGL10.EGL_NONE };
+ configs = chooseConfigWithAttribs(egl, display, attribs);
+ if (configs == null) {
+ // No normal multisampling config was found. Try to create a
+ // coverage multisampling configuration, for the nVidia Tegra2.
+ // See the EGL_NV_coverage_sample documentation.
+ int[] attribsCov = {
+ EGL10.EGL_RENDERABLE_TYPE, PGLES.EGL_OPENGL_ES2_BIT,
+ PGLES.EGL_COVERAGE_BUFFERS_NV, 1,
+ PGLES.EGL_COVERAGE_SAMPLES_NV, numSamples,
+ EGL10.EGL_NONE };
+ configs = chooseConfigWithAttribs(egl, display, attribsCov);
+ if (configs == null) {
+ configs = chooseConfigWithAttribs(egl, display, attribsNoMSAA);
+ } else {
+ PGLES.usingMultisampling = true;
+ PGLES.usingCoverageMultisampling = true;
+ PGLES.multisampleCount = numSamples;
+ }
+ } else {
+ PGLES.usingMultisampling = true;
+ PGLES.usingCoverageMultisampling = false;
+ PGLES.multisampleCount = numSamples;
+ }
+ } else {
+ configs = chooseConfigWithAttribs(egl, display, attribsNoMSAA);
+ }
+
+ if (configs == null) {
+ throw new IllegalArgumentException("No EGL configs match configSpec");
+ }
+
+ if (PApplet.DEBUG) {
+ for (EGLConfig config : configs) {
+ String configStr = "P3D - selected EGL config : "
+ + printConfig(egl, display, config);
+ System.out.println(configStr);
+ }
+ }
+
+ // Now return the configuration that best matches the target one.
+ return chooseBestConfig(egl, display, configs);
+ }
+
+ public EGLConfig chooseBestConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig bestConfig = null;
+ float bestScore = Float.MAX_VALUE;
+
+ for (EGLConfig config : configs) {
+ int gl = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RENDERABLE_TYPE, 0);
+ boolean isGLES2 = (gl & PGLES.EGL_OPENGL_ES2_BIT) != 0;
+ if (isGLES2) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+
+ float score = 0.20f * PApplet.abs(r - redTarget) +
+ 0.20f * PApplet.abs(g - greenTarget) +
+ 0.20f * PApplet.abs(b - blueTarget) +
+ 0.15f * PApplet.abs(a - alphaTarget) +
+ 0.15f * PApplet.abs(d - depthTarget) +
+ 0.10f * PApplet.abs(s - stencilTarget);
+
+ if (score < bestScore) {
+ // We look for the config closest to the target config.
+ // Closeness is measured by the score function defined above:
+ // we give more weight to the RGB components, followed by the
+ // alpha, depth and finally stencil bits.
+ bestConfig = config;
+ bestScore = score;
+
+ redBits = r;
+ greenBits = g;
+ blueBits = b;
+ alphaBits = a;
+ depthBits = d;
+ stencilBits = s;
+ }
+ }
+ }
+
+ if (PApplet.DEBUG) {
+ String configStr = "P3D - selected EGL config : "
+ + printConfig(egl, display, bestConfig);
+ System.out.println(configStr);
+ }
+ return bestConfig;
+ }
+
+ protected String printConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig config) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ int type = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RENDERABLE_TYPE, 0);
+ int nat = findConfigAttrib(egl, display, config,
+ EGL10.EGL_NATIVE_RENDERABLE, 0);
+ int bufSize = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BUFFER_SIZE, 0);
+ int bufSurf = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RENDER_BUFFER, 0);
+
+ return String.format("EGLConfig rgba=%d%d%d%d depth=%d stencil=%d",
+ r,g,b,a,d,s)
+ + " type=" + type
+ + " native=" + nat
+ + " buffer size=" + bufSize
+ + " buffer surface=" + bufSurf +
+ String.format(" caveat=0x%04x",
+ findConfigAttrib(egl, display, config,
+ EGL10.EGL_CONFIG_CAVEAT, 0));
+ }
+
+ protected int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+ if (egl.eglGetConfigAttrib(display, config, attribute, tempValue)) {
+ return tempValue[0];
+ }
+ return defaultValue;
+ }
+
+ protected EGLConfig[] chooseConfigWithAttribs(EGL10 egl,
+ EGLDisplay display,
+ int[] configAttribs) {
+ // Get the number of minimally matching EGL configurations
+ int[] configCounts = new int[1];
+ egl.eglChooseConfig(display, configAttribs, null, 0, configCounts);
+
+ int count = configCounts[0];
+
+ if (count <= 0) {
+ //throw new IllegalArgumentException("No EGL configs match configSpec");
+ return null;
+ }
+
+ // Allocate then read the array of minimally matching EGL configs
+ EGLConfig[] configs = new EGLConfig[count];
+ egl.eglChooseConfig(display, configAttribs, configs, count, configCounts);
+ return configs;
+
+ // Get the number of minimally matching EGL configurations
+// int[] num_config = new int[1];
+// egl.eglChooseConfig(display, configAttribsGL, null, 0, num_config);
+//
+// int numConfigs = num_config[0];
+//
+// if (numConfigs <= 0) {
+// throw new IllegalArgumentException("No EGL configs match configSpec");
+// }
+//
+// // Allocate then read the array of minimally matching EGL configs
+// EGLConfig[] configs = new EGLConfig[numConfigs];
+// egl.eglChooseConfig(display, configAttribsGL, configs, numConfigs,
+// num_config);
+
+ }
+ }
+}
diff --git a/core/src/processing/opengl/Texture.java b/libs/processing-core/src/main/java/processing/opengl/Texture.java
similarity index 86%
rename from core/src/processing/opengl/Texture.java
rename to libs/processing-core/src/main/java/processing/opengl/Texture.java
index 5ffd60b0b..29d8ba4c5 100644
--- a/core/src/processing/opengl/Texture.java
+++ b/libs/processing-core/src/main/java/processing/opengl/Texture.java
@@ -1,12 +1,15 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
/*
Part of the Processing project - http://processing.org
- Copyright (c) 2011-12 Ben Fry and Casey Reas
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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.
+ License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -24,9 +27,12 @@
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;
+import processing.opengl.PGraphicsOpenGL.GLResourceTexture;
+
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.NoSuchElementException;
@@ -82,6 +88,7 @@ public class Texture implements PConstants {
public int glWrapT;
public int glWidth;
public int glHeight;
+ private GLResourceTexture glres;
protected PGraphicsOpenGL pg;
protected PGL pgl; // The interface between Processing and OpenGL.
@@ -100,6 +107,10 @@ public class Texture implements PConstants {
protected int[] rgbaPixels = null;
protected IntBuffer pixelBuffer = null;
+
+ protected int[] edgePixels = null;
+ protected IntBuffer edgeBuffer = null;
+
protected FrameBuffer tempFbo = null;
protected int pixBufUpdateCount = 0;
protected int rgbaPixUpdateCount = 0;
@@ -161,18 +172,6 @@ public Texture(PGraphicsOpenGL pg, int width, int height, Object params) {
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (glName != 0) {
- PGraphicsOpenGL.finalizeTextureObject(glName, context);
- }
- } finally {
- super.finalize();
- }
- }
-
-
////////////////////////////////////////////////////////////
// Init, resize methods
@@ -247,8 +246,7 @@ public void init(int width, int height,
public void resize(int wide, int high) {
- // Marking the texture object as finalized so it is deleted
- // when creating the new texture.
+ // Disposing current resources.
dispose();
// Creating new texture with the appropriate size.
@@ -340,82 +338,21 @@ public void set(int[] pixels, int x, int y, int w, int h, int format) {
}
pgl.bindTexture(glTarget, glName);
+ loadPixels(w * h);
+ convertToRGBA(pixels, format, w, h);
+ if (invertedX) flipArrayOnX(rgbaPixels, 1);
+ if (invertedY) flipArrayOnY(rgbaPixels, 1);
+ updatePixelBuffer(rgbaPixels);
+ pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
+ pixelBuffer);
+ fillEdges(x, y, w, h);
+
if (usingMipmaps) {
if (PGraphicsOpenGL.autoMipmapGenSupported) {
- // Automatic mipmap generation.
- loadPixels(w * h);
- convertToRGBA(pixels, format, w, h);
- updatePixelBuffer(rgbaPixels);
- pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
- pixelBuffer);
pgl.generateMipmap(glTarget);
} else {
- // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage,
- // making it work in npot textures (embed npot tex into larger pot tex?), subregions,
- // and moving GLUtils.texImage2D (originally from Android SDK) into PGL.
- // Actually, this whole code should go into PGL, so the Android implementation can
- // use Bitmap, and desktop use BufferedImage.
-
- /*
- if (w != width || h != height) {
- System.err.println("Sorry but I don't know how to generate mipmaps for a subregion.");
- return;
- }
-
- // Code by Mike Miller obtained from here:
- // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/
- int w0 = glWidth;
- int h0 = glHeight;
- int[] argbPixels = new int[w0 * h0];
- convertToARGB(pixels, argbPixels, format);
- int level = 0;
- int denom = 1;
-
- // We create a Bitmap because then we use its built-in filtered downsampling
- // functionality.
- Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888);
- bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0);
-
- while (w0 >= 1 || h0 >= 1) {
- //First of all, generate the texture from our bitmap and set it to the according level
- GLUtils.texImage2D(glTarget, level, bitmap, 0);
-
- // We are done.
- if (w0 == 1 && h0 == 1) {
- break;
- }
-
- // Increase the mipmap level
- level++;
- denom *= 2;
-
- // Downsampling bitmap. We must eventually arrive to the 1x1 level,
- // and if the width and height are different, there will be a few 1D
- // texture levels just before.
- // This update formula also allows for NPOT resolutions.
- w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom));
- h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom));
- // (see getScaledInstance in AWT Image)
- Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true);
-
- // Clean up
- bitmap.recycle();
- bitmap = bitmap2;
- }
- */
-
- loadPixels(w * h);
- convertToRGBA(pixels, format, w, h);
- updatePixelBuffer(rgbaPixels);
- pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
- pixelBuffer);
+ manualMipmap();
}
- } else {
- loadPixels(w * h);
- convertToRGBA(pixels, format, w, h);
- updatePixelBuffer(rgbaPixels);
- pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
- pixelBuffer);
}
pgl.bindTexture(glTarget, 0);
@@ -472,20 +409,17 @@ public void setNative(IntBuffer pixBuf, int x, int y, int w, int h) {
}
pgl.bindTexture(glTarget, glName);
+ pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
+ pixBuf);
+ fillEdges(x, y, w, h);
+
if (usingMipmaps) {
if (PGraphicsOpenGL.autoMipmapGenSupported) {
- pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
- pixBuf);
pgl.generateMipmap(glTarget);
} else {
- pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
- pixBuf);
+ manualMipmap();
}
- } else {
- pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE,
- pixBuf);
}
-
pgl.bindTexture(glTarget, 0);
if (enabledTex) {
pgl.disableTexturing(glTarget);
@@ -576,49 +510,60 @@ public boolean usingMipmaps() {
public void usingMipmaps(boolean mipmaps, int sampling) {
+ int glMagFilter0 = glMagFilter;
+ int glMinFilter0 = glMinFilter;
if (mipmaps) {
- if (glMinFilter != PGL.LINEAR_MIPMAP_NEAREST &&
- glMinFilter != PGL.LINEAR_MIPMAP_LINEAR) {
- if (sampling == POINT) {
- glMagFilter = PGL.NEAREST;
- glMinFilter = PGL.NEAREST;
- } else if (sampling == LINEAR) {
- glMagFilter = PGL.NEAREST;
- glMinFilter =
- PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR;
- } else if (sampling == BILINEAR) {
- glMagFilter = PGL.LINEAR;
- glMinFilter =
- PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR;
- } else if (sampling == TRILINEAR) {
- glMagFilter = PGL.LINEAR;
- glMinFilter =
- PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR;
- } else {
- throw new RuntimeException("Unknown texture filtering mode");
- }
+ if (sampling == POINT) {
+ glMagFilter = PGL.NEAREST;
+ glMinFilter = PGL.NEAREST;
+ usingMipmaps = false;
+ } else if (sampling == LINEAR) {
+ glMagFilter = PGL.NEAREST;
+ glMinFilter =
+ PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR;
+ usingMipmaps = true;
+ } else if (sampling == BILINEAR) {
+ glMagFilter = PGL.LINEAR;
+ glMinFilter =
+ PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR;
+ usingMipmaps = true;
+ } else if (sampling == TRILINEAR) {
+ glMagFilter = PGL.LINEAR;
+ glMinFilter =
+ PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR;
+ usingMipmaps = true;
+ } else {
+ throw new RuntimeException("Unknown texture filtering mode");
}
-
- usingMipmaps = true;
} else {
- if (glMinFilter == PGL.LINEAR_MIPMAP_NEAREST ||
- glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) {
+ usingMipmaps = false;
+ if (sampling == POINT) {
+ glMagFilter = PGL.NEAREST;
+ glMinFilter = PGL.NEAREST;
+ } else if (sampling == LINEAR) {
+ glMagFilter = PGL.NEAREST;
glMinFilter = PGL.LINEAR;
+ } else if (sampling == BILINEAR || sampling == TRILINEAR) {
+ glMagFilter = PGL.LINEAR;
+ glMinFilter = PGL.LINEAR;
+ } else {
+ throw new RuntimeException("Unknown texture filtering mode");
}
- usingMipmaps = false;
}
- bind();
- pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter);
- pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter);
- if (usingMipmaps) {
- if (PGraphicsOpenGL.autoMipmapGenSupported) {
- pgl.generateMipmap(glTarget);
- } else {
- // TODO: need manual generation here..
+ if (glMagFilter0 != glMagFilter || glMinFilter0 != glMinFilter) {
+ bind();
+ pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter);
+ pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter);
+ if (usingMipmaps) {
+ if (PGraphicsOpenGL.autoMipmapGenSupported) {
+ pgl.generateMipmap(glTarget);
+ } else {
+ manualMipmap();
+ }
}
+ unbind();
}
- unbind();
}
@@ -705,6 +650,23 @@ public void invertedY(boolean v) {
}
+ public int currentSampling() {
+ if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.NEAREST) {
+ return POINT;
+ } else if (glMagFilter == PGL.NEAREST &&
+ glMinFilter == (PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR)) {
+ return LINEAR;
+ } else if (glMagFilter == PGL.LINEAR &&
+ glMinFilter == (PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR)) {
+ return BILINEAR;
+ } else if (glMagFilter == PGL.LINEAR &&
+ glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) {
+ return TRILINEAR;
+ } else {
+ return -1;
+ }
+ }
+
////////////////////////////////////////////////////////////
// Bind/unbind
@@ -833,6 +795,12 @@ protected void updatePixelBuffer(int[] pixels) {
}
+ protected void manualMipmap() {
+ // TODO: finish manual mipmap generation,
+ // https://github.com/processing/processing/issues/3335
+ }
+
+
////////////////////////////////////////////////////////////
// Buffer sink interface.
@@ -1174,7 +1142,7 @@ protected void allocate() {
}
context = pgl.getCurrentContext();
- glName = PGraphicsOpenGL.createTextureObject(context, pgl);
+ glres = new GLResourceTexture(this);
pgl.bindTexture(glTarget, glName);
pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter);
@@ -1208,8 +1176,9 @@ protected void allocate() {
* Marks the texture object for deletion.
*/
protected void dispose() {
- if (glName != 0) {
- PGraphicsOpenGL.finalizeTextureObject(glName, context);
+ if (glres != null) {
+ glres.dispose();
+ glres = null;
glName = 0;
}
}
@@ -1218,14 +1187,7 @@ protected void dispose() {
protected boolean contextIsOutdated() {
boolean outdated = !pgl.contextIsCurrent(context);
if (outdated) {
- // Removing the texture object from the renderer's list so it
- // doesn't get deleted by OpenGL. The texture object was
- // automatically disposed when the old context was destroyed.
- PGraphicsOpenGL.removeTextureObject(glName, context);
-
- // And then set the id to zero, so it doesn't try to be
- // deleted when the object's finalizer is invoked by the GC.
- glName = 0;
+ dispose();
}
return outdated;
}
@@ -1264,27 +1226,29 @@ protected void copyTexture(Texture tex, int x, int y, int w, int h,
// FBO copy:
pg.pushFramebuffer();
pg.setFramebuffer(tempFbo);
- // Clear the color buffer to make sure that the alpha channel is set to
- // full transparency
- pgl.clearColor(0, 0, 0, 0);
- pgl.clear(PGL.COLOR_BUFFER_BIT);
+ // Replaces anything that this texture might contain in the area being
+ // replaced by the new one.
+ pg.pushStyle();
+ pg.blendMode(REPLACE);
if (scale) {
// Rendering tex into "this", and scaling the source rectangle
// to cover the entire destination region.
- pgl.drawTexture(tex.glTarget, tex.glName,
- tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height,
+ pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight,
+ 0, 0, tempFbo.width, tempFbo.height, 1,
x, y, x + w, y + h, 0, 0, width, height);
} else {
// Rendering tex into "this" but without scaling so the contents
// of the source texture fall in the corresponding texels of the
// destination.
- pgl.drawTexture(tex.glTarget, tex.glName,
- tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height,
+ pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight,
+ 0, 0, tempFbo.width, tempFbo.height, 1,
x, y, x + w, y + h, x, y, x + w, y + h);
}
+ pgl.flush(); // Needed to make sure that the change in this texture is
+ // available immediately.
+ pg.popStyle();
pg.popFramebuffer();
-
updateTexels(x, y, w, h);
}
@@ -1304,21 +1268,28 @@ protected void copyTexture(int texTarget, int texName,
// FBO copy:
pg.pushFramebuffer();
pg.setFramebuffer(tempFbo);
+ // Replaces anything that this texture might contain in the area being
+ // replaced by the new one.
+ pg.pushStyle();
+ pg.blendMode(REPLACE);
if (scale) {
// Rendering tex into "this", and scaling the source rectangle
// to cover the entire destination region.
- pgl.drawTexture(texTarget, texName,
- texWidth, texHeight, tempFbo.width, tempFbo.height,
+ pgl.drawTexture(texTarget, texName, texWidth, texHeight,
+ 0, 0, tempFbo.width, tempFbo.height,
x, y, w, h, 0, 0, width, height);
} else {
// Rendering tex into "this" but without scaling so the contents
// of the source texture fall in the corresponding texels of the
// destination.
- pgl.drawTexture(texTarget, texName,
- texWidth, texHeight, tempFbo.width, tempFbo.height,
+ pgl.drawTexture(texTarget, texName, texWidth, texHeight,
+ 0, 0, tempFbo.width, tempFbo.height,
x, y, w, h, x, y, w, h);
}
+ pgl.flush(); // Needed to make sure that the change in this texture is
+ // available immediately.
+ pg.popStyle();
pg.popFramebuffer();
updateTexels(x, y, w, h);
}
@@ -1504,6 +1475,44 @@ protected void setParameters(Parameters params) {
}
+ protected void fillEdges(int x, int y, int w, int h) {
+ if ((width < glWidth || height < glHeight) && (x + w == width || y + h == height)) {
+ if (x + w == width) {
+ int ew = glWidth - width;
+ edgePixels = new int[h * ew];
+ for (int i = 0; i < h; i++) {
+ int c = rgbaPixels[i * w + (w - 1)];
+ Arrays.fill(edgePixels, i * ew, (i + 1) * ew, c);
+ }
+ edgeBuffer = PGL.updateIntBuffer(edgeBuffer, edgePixels, true);
+ pgl.texSubImage2D(glTarget, 0, width, y, ew, h, PGL.RGBA,
+ PGL.UNSIGNED_BYTE, edgeBuffer);
+ }
+
+ if (y + h == height) {
+ int eh = glHeight - height;
+ edgePixels = new int[eh * w];
+ for (int i = 0; i < eh; i++) {
+ System.arraycopy(rgbaPixels, (h - 1) * w, edgePixels, i * w, w);
+ }
+ edgeBuffer = PGL.updateIntBuffer(edgeBuffer, edgePixels, true);
+ pgl.texSubImage2D(glTarget, 0, x, height, w, eh, PGL.RGBA,
+ PGL.UNSIGNED_BYTE, edgeBuffer);
+ }
+
+ if (x + w == width && y + h == height) {
+ int ew = glWidth - width;
+ int eh = glHeight - height;
+ int c = rgbaPixels[w * h - 1];
+ edgePixels = new int[eh * ew];
+ Arrays.fill(edgePixels, 0, eh * ew, c);
+ edgeBuffer = PGL.updateIntBuffer(edgeBuffer, edgePixels, true);
+ pgl.texSubImage2D(glTarget, 0, width, height, ew, eh, PGL.RGBA,
+ PGL.UNSIGNED_BYTE, edgeBuffer);
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Parameters object
diff --git a/libs/processing-core/src/main/java/processing/opengl/VertexBuffer.java b/libs/processing-core/src/main/java/processing/opengl/VertexBuffer.java
new file mode 100644
index 000000000..114428b03
--- /dev/null
+++ b/libs/processing-core/src/main/java/processing/opengl/VertexBuffer.java
@@ -0,0 +1,88 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-21 The Processing Foundation
+ Copyright (c) 2004-12 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, version 2.1.
+
+ 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.opengl;
+
+import processing.opengl.PGraphicsOpenGL.GLResourceVertexBuffer;
+
+// TODO: need to combine with PGraphicsOpenGL.VertexAttribute
+public class VertexBuffer {
+ static protected final int INIT_VERTEX_BUFFER_SIZE = 256;
+ static protected final int INIT_INDEX_BUFFER_SIZE = 512;
+
+ public int glId;
+ int target;
+ int elementSize;
+ int ncoords;
+ boolean index;
+
+ protected PGL pgl; // The interface between Processing and OpenGL.
+ protected int context; // The context that created this texture.
+ private GLResourceVertexBuffer glres;
+
+ VertexBuffer(PGraphicsOpenGL pg, int target, int ncoords, int esize) {
+ this(pg, target, ncoords, esize, false);
+ }
+
+ VertexBuffer(PGraphicsOpenGL pg, int target, int ncoords, int esize, boolean index) {
+ pgl = pg.pgl;
+ context = pgl.createEmptyContext();
+
+ this.target = target;
+ this.ncoords = ncoords;
+ this.elementSize = esize;
+ this.index = index;
+ create();
+ init();
+ }
+
+ protected void create() {
+ context = pgl.getCurrentContext();
+ glres = new GLResourceVertexBuffer(this);
+ }
+
+ protected void init() {
+ int size = index ? ncoords * INIT_INDEX_BUFFER_SIZE * elementSize :
+ ncoords * INIT_VERTEX_BUFFER_SIZE * elementSize;
+ pgl.bindBuffer(target, glId);
+ pgl.bufferData(target, size, null, PGL.STATIC_DRAW);
+ }
+
+ protected void dispose() {
+ if (glres != null) {
+ glres.dispose();
+ glId = 0;
+ glres = null;
+ }
+ }
+
+ protected boolean contextIsOutdated() {
+ boolean outdated = !pgl.contextIsCurrent(context);
+ if (outdated) {
+ dispose();
+ }
+ return outdated;
+ }
+
+}
diff --git a/core/src/processing/opengl/tess/ActiveRegion.java b/libs/processing-core/src/main/java/processing/opengl/tess/ActiveRegion.java
similarity index 100%
rename from core/src/processing/opengl/tess/ActiveRegion.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/ActiveRegion.java
diff --git a/core/src/processing/opengl/tess/CachedVertex.java b/libs/processing-core/src/main/java/processing/opengl/tess/CachedVertex.java
similarity index 100%
rename from core/src/processing/opengl/tess/CachedVertex.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/CachedVertex.java
diff --git a/core/src/processing/opengl/tess/Dict.java b/libs/processing-core/src/main/java/processing/opengl/tess/Dict.java
similarity index 100%
rename from core/src/processing/opengl/tess/Dict.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/Dict.java
diff --git a/core/src/processing/opengl/tess/DictNode.java b/libs/processing-core/src/main/java/processing/opengl/tess/DictNode.java
similarity index 100%
rename from core/src/processing/opengl/tess/DictNode.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/DictNode.java
diff --git a/core/src/processing/opengl/tess/GLUface.java b/libs/processing-core/src/main/java/processing/opengl/tess/GLUface.java
similarity index 100%
rename from core/src/processing/opengl/tess/GLUface.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/GLUface.java
diff --git a/core/src/processing/opengl/tess/GLUhalfEdge.java b/libs/processing-core/src/main/java/processing/opengl/tess/GLUhalfEdge.java
similarity index 100%
rename from core/src/processing/opengl/tess/GLUhalfEdge.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/GLUhalfEdge.java
diff --git a/core/src/processing/opengl/tess/GLUmesh.java b/libs/processing-core/src/main/java/processing/opengl/tess/GLUmesh.java
similarity index 100%
rename from core/src/processing/opengl/tess/GLUmesh.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/GLUmesh.java
diff --git a/core/src/processing/opengl/tess/GLUtessellatorImpl.java b/libs/processing-core/src/main/java/processing/opengl/tess/GLUtessellatorImpl.java
similarity index 100%
rename from core/src/processing/opengl/tess/GLUtessellatorImpl.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/GLUtessellatorImpl.java
diff --git a/core/src/processing/opengl/tess/GLUvertex.java b/libs/processing-core/src/main/java/processing/opengl/tess/GLUvertex.java
similarity index 100%
rename from core/src/processing/opengl/tess/GLUvertex.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/GLUvertex.java
diff --git a/core/src/processing/opengl/tess/Geom.java b/libs/processing-core/src/main/java/processing/opengl/tess/Geom.java
similarity index 100%
rename from core/src/processing/opengl/tess/Geom.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/Geom.java
diff --git a/core/src/processing/opengl/tess/Mesh.java b/libs/processing-core/src/main/java/processing/opengl/tess/Mesh.java
similarity index 100%
rename from core/src/processing/opengl/tess/Mesh.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/Mesh.java
diff --git a/core/src/processing/opengl/tess/Normal.java b/libs/processing-core/src/main/java/processing/opengl/tess/Normal.java
similarity index 100%
rename from core/src/processing/opengl/tess/Normal.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/Normal.java
diff --git a/core/src/processing/opengl/tess/PGLU.java b/libs/processing-core/src/main/java/processing/opengl/tess/PGLU.java
similarity index 100%
rename from core/src/processing/opengl/tess/PGLU.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PGLU.java
diff --git a/core/src/processing/opengl/tess/PGLUtessellator.java b/libs/processing-core/src/main/java/processing/opengl/tess/PGLUtessellator.java
similarity index 100%
rename from core/src/processing/opengl/tess/PGLUtessellator.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PGLUtessellator.java
diff --git a/core/src/processing/opengl/tess/PGLUtessellatorCallback.java b/libs/processing-core/src/main/java/processing/opengl/tess/PGLUtessellatorCallback.java
similarity index 100%
rename from core/src/processing/opengl/tess/PGLUtessellatorCallback.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PGLUtessellatorCallback.java
diff --git a/core/src/processing/opengl/tess/PGLUtessellatorCallbackAdapter.java b/libs/processing-core/src/main/java/processing/opengl/tess/PGLUtessellatorCallbackAdapter.java
similarity index 100%
rename from core/src/processing/opengl/tess/PGLUtessellatorCallbackAdapter.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PGLUtessellatorCallbackAdapter.java
diff --git a/core/src/processing/opengl/tess/PriorityQ.java b/libs/processing-core/src/main/java/processing/opengl/tess/PriorityQ.java
similarity index 100%
rename from core/src/processing/opengl/tess/PriorityQ.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PriorityQ.java
diff --git a/core/src/processing/opengl/tess/PriorityQHeap.java b/libs/processing-core/src/main/java/processing/opengl/tess/PriorityQHeap.java
similarity index 100%
rename from core/src/processing/opengl/tess/PriorityQHeap.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PriorityQHeap.java
diff --git a/core/src/processing/opengl/tess/PriorityQSort.java b/libs/processing-core/src/main/java/processing/opengl/tess/PriorityQSort.java
similarity index 100%
rename from core/src/processing/opengl/tess/PriorityQSort.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/PriorityQSort.java
diff --git a/core/src/processing/opengl/tess/Render.java b/libs/processing-core/src/main/java/processing/opengl/tess/Render.java
similarity index 100%
rename from core/src/processing/opengl/tess/Render.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/Render.java
diff --git a/core/src/processing/opengl/tess/Sweep.java b/libs/processing-core/src/main/java/processing/opengl/tess/Sweep.java
similarity index 100%
rename from core/src/processing/opengl/tess/Sweep.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/Sweep.java
diff --git a/core/src/processing/opengl/tess/TessMono.java b/libs/processing-core/src/main/java/processing/opengl/tess/TessMono.java
similarity index 100%
rename from core/src/processing/opengl/tess/TessMono.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/TessMono.java
diff --git a/core/src/processing/opengl/tess/TessState.java b/libs/processing-core/src/main/java/processing/opengl/tess/TessState.java
similarity index 100%
rename from core/src/processing/opengl/tess/TessState.java
rename to libs/processing-core/src/main/java/processing/opengl/tess/TessState.java
diff --git a/libs/processing-vr/build.gradle b/libs/processing-vr/build.gradle
new file mode 100644
index 000000000..06328948e
--- /dev/null
+++ b/libs/processing-vr/build.gradle
@@ -0,0 +1,24 @@
+plugins {
+ id 'com.android.library'
+}
+
+android {
+ namespace "processing.vr"
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 33
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ productFlavors {
+ }
+}
+
+dependencies {
+ implementation project(':libs:processing-core')
+ implementation project(':libs:google-vr')
+}
\ No newline at end of file
diff --git a/libs/processing-vr/proguard-rules.pro b/libs/processing-vr/proguard-rules.pro
new file mode 100644
index 000000000..f1b424510
--- /dev/null
+++ b/libs/processing-vr/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/libs/processing-vr/src/main/AndroidManifest.xml b/libs/processing-vr/src/main/AndroidManifest.xml
new file mode 100755
index 000000000..97330b776
--- /dev/null
+++ b/libs/processing-vr/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/libs/processing-vr/src/main/java/processing/vr/VRActivity.java b/libs/processing-vr/src/main/java/processing/vr/VRActivity.java
new file mode 100644
index 000000000..28e199585
--- /dev/null
+++ b/libs/processing-vr/src/main/java/processing/vr/VRActivity.java
@@ -0,0 +1,198 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-19 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.vr;
+
+import android.content.Intent;
+import android.util.DisplayMetrics;
+
+// This will give a "Cannot resolve symbol 'base'" error in Android Studio because it cannot get
+// the classes from inside the local .aar files for google-vr. But any VR app runs and can also be debugged.
+import com.google.vr.sdk.base.GvrActivity;
+
+import processing.android.AppComponent;
+import processing.android.ServiceEngine;
+import processing.core.PApplet;
+
+public class VRActivity extends GvrActivity implements AppComponent {
+ static public final int GVR = 3;
+
+ private DisplayMetrics metrics;
+ private PApplet sketch;
+
+
+ public VRActivity() {
+
+ }
+
+
+ static public VRGraphics getRenderer(PApplet p) {
+ return (VRGraphics) p.g;
+ }
+
+
+ public VRActivity(PApplet sketch) {
+ this.sketch = sketch;
+ }
+
+
+ public void initDimensions() {
+ metrics = getResources().getDisplayMetrics();
+ }
+
+
+ public int getDisplayWidth() {
+ return metrics.widthPixels;
+ }
+
+
+ public int getDisplayHeight() {
+ return metrics.heightPixels;
+ }
+
+
+ public float getDisplayDensity() {
+ return metrics.density;
+ }
+
+
+ public int getKind() {
+ return GVR;
+ }
+
+
+ public void dispose() {
+ }
+
+
+ public void setSketch(PApplet sketch) {
+ this.sketch = sketch;
+ if (sketch != null) {
+ sketch.initSurface(VRActivity.this, null);
+ // Required to read the paired viewer's distortion parameters.
+ sketch.requestPermission("android.permission.READ_EXTERNAL_STORAGE");
+ }
+ }
+
+
+ public PApplet getSketch() {
+ return sketch;
+ }
+
+
+ public boolean isService() {
+ return false;
+ }
+
+
+ public ServiceEngine getEngine() {
+ return null;
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (sketch != null) {
+ sketch.onResume();
+ }
+ }
+
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (sketch != null) {
+ sketch.onPause();
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (sketch != null) {
+ sketch.onDestroy();
+ }
+ }
+
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (sketch != null) {
+ sketch.onStart();
+ }
+ }
+
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (sketch != null) {
+ sketch.onStop();
+ }
+ }
+
+
+ public void requestDraw() {
+ }
+
+
+ public boolean canDraw() {
+ return true;
+ }
+
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[],
+ int[] grantResults) {
+ if (sketch != null) {
+ sketch.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ if (sketch != null) {
+ sketch.onNewIntent(intent);
+ }
+ }
+
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (sketch != null) {
+ sketch.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ if (sketch != null) {
+ sketch.onBackPressed();
+ }
+ }
+}
diff --git a/libs/processing-vr/src/main/java/processing/vr/VRCamera.java b/libs/processing-vr/src/main/java/processing/vr/VRCamera.java
new file mode 100644
index 000000000..c7e150297
--- /dev/null
+++ b/libs/processing-vr/src/main/java/processing/vr/VRCamera.java
@@ -0,0 +1,67 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2019 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.vr;
+
+import processing.core.PApplet;
+import processing.core.PMatrix3D;
+import processing.core.PVector;
+
+public class VRCamera {
+ protected PApplet parent;
+ protected VRGraphics graphics;
+ protected PMatrix3D eyeMat;
+
+ public VRCamera(PApplet parent) {
+ if (parent.g instanceof VRGraphics) {
+ this.parent = parent;
+ this.graphics = (VRGraphics)(parent.g);
+ } else {
+ System.err.println("The VR camera can only be created when the VR renderer is in use");
+ }
+ }
+
+ public void sticky() {
+ parent.pushMatrix();
+ parent.eye();
+ }
+
+ public void noSticky() {
+ parent.popMatrix();
+ }
+
+ public void setPosition(float x, float y, float z) {
+ eyeMat = graphics.getEyeMatrix(eyeMat);
+ float x0 = eyeMat.m03;
+ float y0 = eyeMat.m13;
+ float z0 = eyeMat.m23;
+ graphics.translate(x0 - x, y0 - y, z0 - z);
+ }
+
+ public void setNear(float near) {
+ graphics.defCameraNear = near;
+ }
+
+ public void setFar(float far) {
+ graphics.defCameraFar = far;
+ }
+}
diff --git a/libs/processing-vr/src/main/java/processing/vr/VRGraphics.java b/libs/processing-vr/src/main/java/processing/vr/VRGraphics.java
new file mode 100644
index 000000000..f39560457
--- /dev/null
+++ b/libs/processing-vr/src/main/java/processing/vr/VRGraphics.java
@@ -0,0 +1,264 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-19 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.vr;
+
+import com.google.vr.sdk.base.Eye;
+import com.google.vr.sdk.base.FieldOfView;
+import com.google.vr.sdk.base.HeadTransform;
+import com.google.vr.sdk.base.Viewport;
+
+import processing.core.PApplet;
+import processing.core.PGraphics;
+import processing.core.PMatrix3D;
+import processing.core.PVector;
+import processing.opengl.PGL;
+import processing.opengl.PGLES;
+import processing.opengl.PGraphics3D;
+import processing.opengl.PGraphicsOpenGL;
+
+public class VRGraphics extends PGraphics3D {
+ static public final int LEFT = Eye.Type.LEFT;
+ static public final int RIGHT = Eye.Type.RIGHT;
+ static public final int MONOCULAR = Eye.Type.MONOCULAR;
+
+ private boolean initialized = false;
+
+ public HeadTransform headTransform;
+ public Eye eye;
+ public int eyeType;
+
+ protected float[] forwardVector;
+ protected float[] rightVector;
+ protected float[] upVector;
+
+ private Viewport eyeViewport;
+ private float[] eyeView;
+ private float[] eyePerspective;
+
+
+ @Override
+ protected PGL createPGL(PGraphicsOpenGL pg) {
+ return new PGLES(pg);
+ }
+
+
+ @Override
+ public void beginDraw() {
+ super.beginDraw();
+ updateView();
+ }
+
+
+ @Override
+ public void camera(float eyeX, float eyeY, float eyeZ,
+ float centerX, float centerY, float centerZ,
+ float upX, float upY, float upZ) {
+ PGraphics.showWarning("The camera cannot be set in VR");
+ }
+
+
+ @Override
+ public void perspective(float fov, float aspect, float zNear, float zFar) {
+ PGraphics.showWarning("Perspective cannot be set in VR");
+ }
+
+
+ @Override
+ protected void defaultCamera() {
+ // do nothing
+ }
+
+
+ @Override
+ protected void defaultPerspective() {
+ // do nothing
+ }
+
+
+ @Override
+ protected void saveState() {
+ }
+
+
+ @Override
+ protected void restoreState() {
+ }
+
+
+ @Override
+ protected void restoreSurface() {
+ }
+
+
+ protected void updateView() {
+ setVRViewport();
+ setVRCamera();
+ setVRProjection();
+ }
+
+
+ protected void eyeTransform(Eye e) {
+ eye = e;
+ eyeType = eye.getType();
+ eyeViewport = eye.getViewport();
+ eyePerspective = eye.getPerspective(defCameraNear, defCameraFar);
+ eyeView = eye.getEyeView();
+
+ // Adjust the camera Z position to it fits the (width,height) rect at Z = 0, given
+ // the fov settings.
+ FieldOfView fov = eye.getFov();
+ defCameraFOV = fov.getTop()* DEG_TO_RAD;
+ defCameraZ = (float) (height / (2 * Math.tan(defCameraFOV)));
+ cameraAspect = (float)width / height;
+ if (cameraUp) {
+ defCameraX = 0;
+ defCameraY = 0;
+ } else {
+ defCameraX = +width / 2.0f;
+ defCameraY = +height / 2.0f;
+ }
+ }
+
+
+ protected void headTransform(HeadTransform ht) {
+ initVR();
+
+ headTransform = ht;
+
+ // Forward, right, and up vectors are given in the original system with Y
+ // pointing up. Need to invert y coords in the non-gl case:
+ float yf = cameraUp ? +1 : -1;
+
+ headTransform.getForwardVector(forwardVector, 0);
+ headTransform.getRightVector(rightVector, 0);
+ headTransform.getUpVector(upVector, 0);
+
+ forwardX = forwardVector[0];
+ forwardY = yf * forwardVector[1];
+ forwardZ = forwardVector[2];
+
+ rightX = rightVector[0];
+ rightY = yf * rightVector[1];
+ rightZ = rightVector[2];
+
+ upX = upVector[0];
+ upY = yf * upVector[1];
+ upZ = upVector[2];
+ }
+
+
+ protected void initVR() {
+ if (!initialized) {
+ forwardVector = new float[3];
+ rightVector = new float[3];
+ upVector = new float[3];
+ initialized = true;
+ }
+ }
+
+
+ protected void setVRViewport() {
+ pgl.viewport(eyeViewport.x, eyeViewport.y, eyeViewport.width, eyeViewport.height);
+ }
+
+
+ protected void setVRCamera() {
+ cameraX = defCameraX;
+ cameraY = defCameraY;
+ cameraZ = defCameraZ;
+
+ // Calculating Z vector
+ float z0 = 0;
+ float z1 = 0;
+ float z2 = defCameraZ;
+ eyeDist = PApplet.abs(z2);
+ if (nonZero(eyeDist)) {
+ z0 /= eyeDist;
+ z1 /= eyeDist;
+ z2 /= eyeDist;
+ }
+
+ // Calculating Y vector
+ float y0 = 0;
+ float y1 = cameraUp ? + 1: -1;
+ float y2 = 0;
+
+ // Computing X vector as Y cross Z
+ float x0 = y1 * z2 - y2 * z1;
+ float x1 = -y0 * z2 + y2 * z0;
+ float x2 = y0 * z1 - y1 * z0;
+ if (!cameraUp) {
+ // Inverting X axis
+ x0 *= -1;
+ x1 *= -1;
+ x2 *= -1;
+ }
+
+ // Cross product gives area of parallelogram, which is < 1.0 for
+ // non-perpendicular unit-length vectors; so normalize x, y here:
+ float xmag = PApplet.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
+ if (nonZero(xmag)) {
+ x0 /= xmag;
+ x1 /= xmag;
+ x2 /= xmag;
+ }
+
+ float ymag = PApplet.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
+ if (nonZero(ymag)) {
+ y0 /= ymag;
+ y1 /= ymag;
+ y2 /= ymag;
+ }
+
+ // Pre-apply the eye view matrix:
+ // https://developers.google.com/vr/android/reference/com/google/vr/sdk/base/Eye.html#getEyeView()
+ modelview.set(eyeView[0], eyeView[4], eyeView[8], eyeView[12],
+ eyeView[1], eyeView[5], eyeView[9], eyeView[13],
+ eyeView[2], eyeView[6], eyeView[10], eyeView[14],
+ eyeView[3], eyeView[7], eyeView[11], eyeView[15]);
+ modelview.apply(x0, x1, x2, 0,
+ y0, y1, y2, 0,
+ z0, z1, z2, 0,
+ 0, 0, 0, 1);
+ float tx = -defCameraX;
+ float ty = -defCameraY;
+ float tz = -defCameraZ;
+ modelview.translate(tx, ty, tz);
+
+ modelviewInv.set(modelview);
+ modelviewInv.invert();
+
+ camera.set(modelview);
+ cameraInv.set(modelviewInv);
+ }
+
+
+ protected void setVRProjection() {
+ // Matrices in Processing are row-major, and GVR API is column-major
+ projection.set(eyePerspective[0], eyePerspective[4], eyePerspective[8], eyePerspective[12],
+ eyePerspective[1], eyePerspective[5], eyePerspective[9], eyePerspective[13],
+ eyePerspective[2], eyePerspective[6], eyePerspective[10], eyePerspective[14],
+ eyePerspective[3], eyePerspective[7], eyePerspective[11], eyePerspective[15]);
+ updateProjmodelview();
+ }
+}
diff --git a/libs/processing-vr/src/main/java/processing/vr/VRGraphicsMono.java b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsMono.java
new file mode 100644
index 000000000..6a31a2b57
--- /dev/null
+++ b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsMono.java
@@ -0,0 +1,35 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-19 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.vr;
+
+import android.view.SurfaceHolder;
+import processing.android.AppComponent;
+import processing.core.PSurface;
+
+public class VRGraphicsMono extends VRGraphics {
+ @Override
+ public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore
+ if (reset) pgl.resetFBOLayer();
+ return new VRSurface(this, component, holder, false);
+ }
+}
\ No newline at end of file
diff --git a/libs/processing-vr/src/main/java/processing/vr/VRGraphicsStereo.java b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsStereo.java
new file mode 100644
index 000000000..681de9e1b
--- /dev/null
+++ b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsStereo.java
@@ -0,0 +1,35 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-19 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.vr;
+
+import android.view.SurfaceHolder;
+import processing.android.AppComponent;
+import processing.core.PSurface;
+
+public class VRGraphicsStereo extends VRGraphics {
+ @Override
+ public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore
+ if (reset) pgl.resetFBOLayer();
+ return new VRSurface(this, component, holder, true);
+ }
+}
diff --git a/libs/processing-vr/src/main/java/processing/vr/VRSurface.java b/libs/processing-vr/src/main/java/processing/vr/VRSurface.java
new file mode 100644
index 000000000..dac07ee27
--- /dev/null
+++ b/libs/processing-vr/src/main/java/processing/vr/VRSurface.java
@@ -0,0 +1,338 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2016-19 The Processing Foundation
+
+ 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, version 2.1.
+
+ 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.vr;
+
+import java.io.File;
+import java.io.InputStream;
+
+import javax.microedition.khronos.egl.EGLConfig;
+
+import com.google.vr.sdk.base.GvrActivity;
+import com.google.vr.sdk.base.GvrView;
+import com.google.vr.sdk.base.AndroidCompat;
+import com.google.vr.sdk.base.Eye;
+import com.google.vr.sdk.base.HeadTransform;
+import com.google.vr.sdk.base.Viewport;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.res.AssetManager;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import processing.android.AppComponent;
+import processing.core.PGraphics;
+import processing.opengl.PGLES;
+import processing.opengl.PGraphicsOpenGL;
+import processing.opengl.PSurfaceGLES;
+import android.view.Window;
+import android.view.WindowManager;
+
+public class VRSurface extends PSurfaceGLES {
+ protected SurfaceViewVR vrView;
+ protected VRGraphics pvr;
+
+ protected GvrActivity vrActivity;
+ protected AndroidVRStereoRenderer renderer;
+
+ private boolean needCalculate;
+
+ public VRSurface(PGraphics graphics, AppComponent component, SurfaceHolder holder, boolean vr) {
+ this.sketch = graphics.parent;
+ this.graphics = graphics;
+ this.component = component;
+ this.pgl = (PGLES)((PGraphicsOpenGL)graphics).pgl;
+
+ vrActivity = (GvrActivity)component;
+ this.activity = vrActivity;
+ pvr = (VRGraphics)graphics;
+
+ vrView = new SurfaceViewVR(vrActivity);
+
+ // Enables/disables the transition view used to prompt the user to place
+ // their phone into a GVR viewer.
+ vrView.setTransitionViewEnabled(true);
+
+ // Enables Cardboard-trigger feedback with Daydream headsets. This is a simple way of supporting
+ // Daydream controller input for basic interactions using the existing Cardboard trigger API.
+ vrView.enableCardboardTriggerEmulation();
+
+ vrView.setStereoModeEnabled(vr);
+ if (vr) {
+ vrView.setDistortionCorrectionEnabled(true);
+ vrView.setNeckModelEnabled(true);
+ }
+
+ if (vrView.setAsyncReprojectionEnabled(true)) {
+ // Async reprojection decouples the app framerate from the display framerate,
+ // allowing immersive interaction even at the throttled clockrates set by
+ // sustained performance mode.
+ AndroidCompat.setSustainedPerformanceMode(vrActivity, true);
+ }
+ vrActivity.setGvrView(vrView);
+
+ surfaceView = null;
+
+ // The glview is ready right after creation, does not need to wait for a
+ // surfaceCreate() event.
+ surfaceReady = true;
+ }
+
+ @Override
+ public Context getContext() {
+ return vrActivity;
+ }
+
+ @Override
+ public Activity getActivity() {
+ return vrActivity;
+ }
+
+ @Override
+ public void finish() {
+ vrActivity.finish();
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ return vrActivity.getAssets();
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ vrActivity.startActivity(intent);
+ }
+
+ @Override
+ public void initView(int sketchWidth, int sketchHeight) {
+ Window window = vrActivity.getWindow();
+
+ // Take up as much area as possible
+ //requestWindowFeature(Window.FEATURE_NO_TITLE); // may need to set in theme properties
+ // the above line does not seem to be needed when using VR
+ // android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
+ window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
+
+ // This does the actual full screen work
+ window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ window.setContentView(vrView);
+ }
+
+ @Override
+ public String getName() {
+ return vrActivity.getComponentName().getPackageName();
+ }
+
+ @Override
+ public void setOrientation(int which) {
+ PGraphics.showWarning("Orientation in VR apps cannot be changed");
+ }
+
+ @Override
+ public File getFilesDir() {
+ return vrActivity.getFilesDir();
+ }
+
+ @Override
+ public InputStream openFileInput(String filename) {
+ return null;
+ }
+
+ @Override
+ public File getFileStreamPath(String path) {
+ return vrActivity.getFileStreamPath(path);
+ }
+
+ @Override
+ public void dispose() {
+// surface.onDestroy();
+ }
+
+
+ ///////////////////////////////////////////////////////////
+
+ // Thread handling
+
+ private boolean running = false;
+
+ @Override
+ public void startThread() {
+ vrView.onResume();
+ running = true;
+ }
+
+ @Override
+ public void pauseThread() {
+ vrView.onPause();
+ running = false;
+ }
+
+ @Override
+ public void resumeThread() {
+ vrView.onResume();
+ running = true;
+ }
+
+ @Override
+ public boolean stopThread() {
+ running = false;
+ return true;
+ }
+
+ @Override
+ public boolean isStopped() {
+ return !running;
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ public class SurfaceViewVR extends GvrView {
+ public SurfaceViewVR(Context context) {
+ super(context);
+
+ // Check if the system supports OpenGL ES 2.0.
+ final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
+ final boolean supportsGLES2 = configurationInfo.reqGlEsVersion >= 0x20000;
+
+ if (!supportsGLES2) {
+ throw new RuntimeException("OpenGL ES 2.0 is not supported by this device.");
+ }
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+
+
+ int samples = sketch.sketchSmooth();
+ if (1 < samples) {
+ setMultisampling(samples);
+ } else {
+ // use default EGL config chooser for now
+// setEGLConfigChooser(8, 8, 8, 8, 16, 8);
+ }
+
+ // The renderer can be set only once.
+ setRenderer(getVRStereoRenderer());
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return sketch.surfaceTouchEvent(event);
+ }
+
+
+ @Override
+ public boolean onKeyDown(int code, android.view.KeyEvent event) {
+ sketch.surfaceKeyDown(code, event);
+ return super.onKeyDown(code, event);
+ }
+
+
+ @Override
+ public boolean onKeyUp(int code, android.view.KeyEvent event) {
+ sketch.surfaceKeyUp(code, event);
+ return super.onKeyUp(code, event);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////
+
+ // Android specific classes (Renderer, ConfigChooser)
+
+
+ public AndroidVRStereoRenderer getVRStereoRenderer() {
+ renderer = new AndroidVRStereoRenderer();
+ return renderer;
+ }
+
+
+ protected class AndroidVRStereoRenderer implements GvrView.StereoRenderer {
+ public AndroidVRStereoRenderer() {
+
+ }
+
+ @Override
+ public void onNewFrame(HeadTransform transform) {
+ hadnleGVREnumError();
+ pgl.getGL(null);
+ pvr.headTransform(transform);
+ needCalculate = true;
+ }
+
+ @Override
+ public void onDrawEye(Eye eye) {
+ pvr.eyeTransform(eye);
+ if (needCalculate) {
+ // Call calculate() right after we have the first eye transform.
+ // This allows to update the modelview and projection matrices, so
+ // geometry-related calculations can also be conducted in calculate().
+ pvr.updateView();
+ sketch.calculate();
+ needCalculate = false;
+ }
+ sketch.handleDraw();
+ }
+
+ @Override
+ public void onFinishFrame(Viewport arg0) {
+ }
+
+ @Override
+ public void onRendererShutdown() {
+ }
+
+ @Override
+ public void onSurfaceChanged(int iwidth, int iheight) {
+ sketch.surfaceChanged();
+ graphics.surfaceChanged();
+
+ sketch.setSize(iwidth, iheight);
+ graphics.setSize(sketch.sketchWidth(), sketch.sketchHeight());
+ }
+
+ @Override
+ public void onSurfaceCreated(EGLConfig arg0) {
+ }
+
+ // Don't print the invalid enum error:
+ // https://github.com/processing/processing-android/issues/281
+ // seems harmless as it happens in the first frame only
+ // TODO: need to find the reason for the error (gl config?)
+ private void hadnleGVREnumError() {
+ int err = pgl.getError();
+ if (err != 0 && err != 1280) {
+ String where = "top onNewFrame";
+ String errString = pgl.errorString(err);
+ String msg = "OpenGL error " + err + " at " + where + ": " + errString;
+ PGraphics.showWarning(msg);
+ }
+ }
+ }
+}
diff --git a/mode.properties b/mode.properties
deleted file mode 100644
index 98b6b8f0a..000000000
--- a/mode.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-name = Android Mode
-authorList = [The Processing Foundation](http://android.processing.org/)
-url = https://github.com/processing/processing-android
-sentence = Create projects with Processing for Android devices
-paragraph = This version of the Android Mode is for Processing 3.0+
-version = 232
-prettyVersion = 3.0.1
-minRevision = 228
-maxRevision = 0
diff --git a/mode/.gitignore b/mode/.gitignore
deleted file mode 100644
index 7e0d4cfbd..000000000
--- a/mode/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-AndroidMode.jar
diff --git a/mode/org.eclipse.core.runtime-3.1.0.jar b/mode/org.eclipse.core.runtime-3.1.0.jar
deleted file mode 100644
index 746421bbc..000000000
Binary files a/mode/org.eclipse.core.runtime-3.1.0.jar and /dev/null differ
diff --git a/processing-android b/processing-android
deleted file mode 100644
index 5bf102cad..000000000
--- a/processing-android
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/sh
-
-# This script runs Processing, using the JDK in the Processing
-# installation directory if possible.
-
-# If no JDK was installed with Processing then the script tries to use
-# the preferred Java version of the machine, i.e. what is executed
-# by the "java" console command. This must be a Sun JDK (for details, see
-# http://processing.org/reference/environment/platforms.html#java).
-
-# In order to run Processing with an already installed JDK that is *not*
-# the preferred Java version of the machine, create a symlink named "java"
-# in the Processing installation directory that points to the JDK home
-# directory.
-
-# Thanks to Ferdinand Kasper for this build script. [fry]
-
-
-# JARs required from JDK (anywhere in/below the JDK home directory)
-JDKLIBS="rt.jar tools.jar"
-
-# Set this to non-zero for logging
-LOGGING=0
-
-# Logs name and value of a variable to stdout if LOGGING is non-zero.
-# Expects the variable name as parameter $1.
-log() {
- if [ $LOGGING -ne 0 ]; then
- eval echo $1=\$$1
- fi
-}
-
-
-# Locates JDKLIBS in a directory and its subdirectories and saves their
-# absolute paths as list to JDKCP. Expects the directory as parameter $1.
-# Sets SUCCESS to 1 if all libraries were found, to 0 otherwise.
-make_jdkcp() {
- # Back out of JRE directory if apparently located inside a JDK
- if [ -f "$1/../bin/java" ]; then
- DIR="$1/.."
- else
- DIR="$1"
- fi
- log DIR
-
- JDKCP=
- SUCCESS=1
-
- # Locate JDKLIBS
- for L in $JDKLIBS; do
- # Locate only the first library with a matching name
- LIB=`find "$DIR" -name $L 2>/dev/null | head -n 1`
- log L
- log LIB
-
- # Library found?
- if [ -n "$LIB" ]; then
- JDKCP="$JDKCP"${JDKCP:+:}"$LIB"
- else
- SUCCESS=0
- fi
- done
-
- log JDKCP
-}
-
-
-# Get absolute path of directory where this script is located
-APPDIR=`readlink -f "$0"`
-APPDIR=`dirname "$APPDIR"`
-log APPDIR
-
-# Try using a local JDK from the same directory as this script
-JDKDIR=`readlink -f "$APPDIR/java"`
-make_jdkcp "$JDKDIR"
-log SUCCESS
-
-# Local JDK found?
-if [ $SUCCESS -ne 1 ]; then
- # No, try using the preferred system JRE/JDK (if any)
- JDKDIR=`which java` && JDKDIR=`readlink -e "$JDKDIR"` && JDKDIR=`dirname "$JDKDIR"`/..
- make_jdkcp "$JDKDIR"
- log SUCCESS
-fi
-
-# Add all required JARs to CLASSPATH
-CLASSPATH="$CLASSPATH"${CLASSPATH:+:}"$JDKCP"
-for LIB in "$APPDIR"/lib/*.jar; do
- CLASSPATH="$CLASSPATH"${CLASSPATH:+:}"$LIB"
-done
-for LIB in "$APPDIR"/core/library/*.jar; do
- CLASSPATH="$CLASSPATH"${CLASSPATH:+:}"$LIB"
-done
-export CLASSPATH
-log CLASSPATH
-
-# Make all JDK binaries available in PATH
-export PATH="$JDKDIR/bin":"$PATH"
-log PATH
-
-current_name=`basename $0`
-cmd_name='processing-android'
-
-if [ $current_name = $cmd_name ]
-then
- java processing.mode.android.Commander "$@"
-else
- # Start Processing in the same directory as this script
- if [ "$1" ]; then
- SKETCH=`readlink -f $1`
- else
- SKETCH=
- fi
- cd "$APPDIR"
-
- java processing.app.Base "$SKETCH" &
-fi
diff --git a/processing/.gitignore b/processing/.gitignore
new file mode 100644
index 000000000..9c6eb9d40
--- /dev/null
+++ b/processing/.gitignore
@@ -0,0 +1,14 @@
+mode/processing-core.zip
+mode/mode/AndroidMode.jar
+mode/mode/gradle-tooling-api*
+mode/mode/slf4j*
+
+mode/mode/percent.jar
+mode/mode/recyclerview-v7.jar
+mode/mode/support-*
+mode/mode/wearable.jar
+
+mode/libraries/vr/library
+mode/libraries/ar/library
+mode/tools/SDKUpdater/tool
+mode/tools/SDKUpdater/lib
diff --git a/processing/README.md b/processing/README.md
new file mode 100644
index 000000000..743c8422c
--- /dev/null
+++ b/processing/README.md
@@ -0,0 +1,6 @@
+Processing for Android
+======================
+
+This is the main repository for Processing for Android. It includes the core library inside the core folder, and the mode itself in the root. See the [wiki](https://github.com/processing/processing-android/wiki) for build instructions.
+
+
diff --git a/processing/build.gradle b/processing/build.gradle
new file mode 100644
index 000000000..9823664a5
--- /dev/null
+++ b/processing/build.gradle
@@ -0,0 +1,175 @@
+import java.nio.file.Files
+import org.zeroturnaround.zip.ZipUtil
+import org.apache.commons.io.FileUtils
+import java.util.regex.Pattern
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.3.1'
+ classpath group: 'commons-io', name: 'commons-io', version: '2.12.0'
+ classpath group: 'org.zeroturnaround', name: 'zt-zip', version: '1.15'
+ }
+}
+
+plugins {
+ id 'java'
+ id('io.github.gradle-nexus.publish-plugin') version '1.1.0'
+}
+
+apply from: "${rootDir}/scripts/publish-root.gradle"
+
+allprojects {
+ apply plugin: 'java'
+ apply plugin: 'java-library'
+
+ Properties versions = new Properties()
+ versions.load(project.rootProject.file("mode/version.properties").newDataInputStream())
+ ext.targetSdkVersion = versions.getProperty("android-platform")
+ ext.appcompatVersion = versions.getProperty("androidx.appcompat%appcompat")
+ ext.v4legacyVersion = versions.getProperty("androidx.legacy%legacy-support-v4")
+ ext.wearVersion = versions.getProperty("com.google.android.support%wearable")
+ ext.gvrVersion = versions.getProperty("com.google.vr")
+ ext.garVersion = versions.getProperty("com.google.ar")
+ ext.processingVersion = versions.getProperty("org.processing")
+ ext.toolingVersion = versions.getProperty("org.gradle%gradle-tooling-api")
+ ext.slf4jVersion = versions.getProperty("org.slf4j")
+ ext.gradlewVersion = versions.getProperty("gradle-wrapper")
+ ext.toolsLibVersion = versions.getProperty("android-toolslib")
+ ext.jdtVersion = versions.getProperty("org.eclipse.jdt")
+
+ Properties modeProperties = new Properties()
+ modeProperties.load(project.rootProject.file("mode/mode.properties").newDataInputStream())
+ ext.modeVersion = modeProperties.getProperty("prettyVersion")
+
+ Properties vrProperties = new Properties()
+ vrProperties.load(project.rootProject.file("mode/libraries/vr/library.properties").newDataInputStream())
+ ext.vrLibVersion = vrProperties.getProperty("prettyVersion")
+
+ Properties arProperties = new Properties()
+ arProperties.load(project.rootProject.file("mode/libraries/ar/library.properties").newDataInputStream())
+ ext.arLibVersion = arProperties.getProperty("prettyVersion")
+
+
+ def fn = project.rootProject.file("local.properties")
+ if (!fn.exists()) {
+ if (System.env["ANDROID_SDK"] != null) {
+ def syspath = System.env["ANDROID_SDK"]
+ def parts = syspath.split(Pattern.quote(File.separator))
+ def path = String.join("/", parts)
+ fn.withWriterAppend { w ->
+ w << "sdk.dir=${path}\n"
+ }
+ } else {
+ throw new GradleException(
+ "The file local.properties does not exist, and there is no ANDROID_SDK environmental variable defined in the system.\n" +
+ "Define ANDROID_SDK so it points to the location of the Android SDK, or create the local.properties file manually\n" +
+ "and add the following line to it:\n" +
+ "sdk.dir=")
+ }
+ }
+
+
+ Properties localProperties = new Properties()
+ localProperties.load(project.rootProject.file("local.properties").newDataInputStream())
+ def sdkDir = localProperties.getProperty("sdk.dir")
+ ext.androidPlatformPath = "${sdkDir}/platforms/android-${targetSdkVersion}"
+ ext.coreZipPath = "${rootDir}/mode/processing-core.zip"
+
+ repositories {
+ google()
+ mavenCentral()
+ maven { url "https://maven.google.com" }
+ maven { url "https://jitpack.io" }
+ maven { url 'https://repo.gradle.org/gradle/libs-releases' }
+ flatDir dirs: androidPlatformPath
+ flatDir dirs: "${rootDir}/core/build/libs"
+ }
+
+ compileJava {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+
+ // Uncomment this option when building with Java 11+
+ // https://github.com/processing/processing-android/issues/625
+ // options.release = 8
+ }
+}
+
+clean.doFirst {
+ delete "dist"
+}
+
+task dist {
+ dependsOn subprojects.build
+ doLast {
+ def root = "${buildDir}/zip/AndroidMode"
+
+ // Copy assets to build dir
+ FileUtils.copyDirectory(file("mode/templates"), file("${root}/templates"))
+ FileUtils.copyDirectory(file("mode/examples"), file("${root}/examples"))
+ FileUtils.copyDirectory(file("mode/icons"), file("${root}/icons"))
+ FileUtils.copyDirectory(file("mode/theme"), file("${root}/theme"))
+ FileUtils.copyDirectory(file("mode/mode"), file("${root}/mode"))
+ delete "${root}/mode/core.jar"
+ delete "${root}/mode/pde.jar"
+ delete "${root}/mode/JavaMode.jar"
+ // delete "${root}/mode/jdi.jar"
+ // delete "${root}/mode/jdimodel.jar"
+
+ Files.copy(file("mode/processing-core.zip").toPath(),
+ file("${root}/processing-core.zip").toPath(), REPLACE_EXISTING)
+
+ Files.copy(file("mode/keywords.txt").toPath(),
+ file("${root}/keywords.txt").toPath(), REPLACE_EXISTING)
+
+ Files.copy(file("mode/version.properties").toPath(),
+ file("${root}/version.properties").toPath(), REPLACE_EXISTING)
+
+ Files.copy(file("mode/mode.properties").toPath(),
+ file("${root}/mode.properties").toPath(), REPLACE_EXISTING)
+
+ FileUtils.copyDirectory(file("mode/languages"),
+ file("${root}/languages"))
+
+ FileUtils.copyDirectory(file("mode/resources"),
+ file("${root}/resources"))
+
+ FileUtils.copyDirectory(file("mode/tools/SDKUpdater/tool"),
+ file("${root}/tools/SDKUpdater/tool"))
+ FileUtils.copyDirectory(file("mode/tools/SDKUpdater/lib"),
+ file("${root}/tools/SDKUpdater/lib"))
+ FileUtils.copyDirectory(file("mode/tools/SDKUpdater/src"),
+ file("${root}/tools/SDKUpdater/src"))
+
+ FileUtils.copyDirectory(file("mode/libraries/vr/examples"),
+ file("${root}/libraries/vr/examples"))
+ FileUtils.copyDirectory(file("mode/libraries/vr/library"),
+ file("${root}/libraries/vr/library"))
+ FileUtils.copyDirectory(file("mode/libraries/vr/libs"),
+ file("${root}/libraries/vr/libs"))
+ FileUtils.copyDirectory(file("../libs/processing-vr/src/main/java/"),
+ file("${root}/libraries/vr/src"))
+ Files.copy(file("mode/libraries/vr/library.properties").toPath(),
+ file("${root}/libraries/vr/library.properties").toPath(), REPLACE_EXISTING)
+
+ FileUtils.copyDirectory(file("mode/libraries/ar/examples"),
+ file("${root}/libraries/ar/examples"))
+ FileUtils.copyDirectory(file("mode/libraries/ar/library"),
+ file("${root}/libraries/ar/library"))
+ FileUtils.copyDirectory(file("../libs/processing-ar/src/main/java/"),
+ file("${root}/libraries/ar/src"))
+ Files.copy(file("mode/libraries/ar/library.properties").toPath(),
+ file("${root}/libraries/ar/library.properties").toPath(), REPLACE_EXISTING)
+
+ File distFolder = file("dist")
+ distFolder.mkdirs()
+ ZipUtil.pack(file("${buildDir}/zip"), new File("dist/AndroidMode.zip"))
+ Files.copy(file("mode/mode.properties").toPath(),
+ file("dist/AndroidMode.txt").toPath(), REPLACE_EXISTING)
+ }
+}
diff --git a/processing/buildSrc/build.gradle b/processing/buildSrc/build.gradle
new file mode 100644
index 000000000..e6ffdbeb9
--- /dev/null
+++ b/processing/buildSrc/build.gradle
@@ -0,0 +1,17 @@
+// apply plugin: 'groovy'
+plugins {
+ id 'groovy'
+}
+
+repositories {
+ google()
+ mavenCentral()
+}
+
+dependencies {
+ implementation gradleApi()
+ implementation localGroovy()
+ implementation 'com.android.tools.build:gradle:7.3.1'
+ implementation 'com.google.guava:guava:32.0.0-jre'
+ implementation 'com.android.tools:common:25.3.0'
+}
\ No newline at end of file
diff --git a/processing/buildSrc/src/main/groovy/ImportAar.groovy b/processing/buildSrc/src/main/groovy/ImportAar.groovy
new file mode 100644
index 000000000..fc20588f1
--- /dev/null
+++ b/processing/buildSrc/src/main/groovy/ImportAar.groovy
@@ -0,0 +1,237 @@
+// import org.gradle.api.Plugin
+// import org.gradle.api.Project
+// import org.gradle.api.artifacts.Configuration
+// import org.gradle.api.artifacts.transform.TransformAction
+// import org.gradle.api.artifacts.transform.TransformParameters
+// import org.gradle.api.artifacts.transform.InputArtifact
+// import org.gradle.api.artifacts.transform.TransformOutputs
+// import org.gradle.api.file.FileSystemLocation
+// import org.gradle.api.provider.Provider
+// import org.gradle.api.tasks.PathSensitive
+// import org.gradle.api.tasks.PathSensitivity
+
+// import org.gradle.api.attributes.LibraryElements
+// import org.gradle.api.attributes.Usage
+// import org.gradle.api.attributes.Category
+
+// import com.android.build.gradle.internal.dependency.AarTransform
+// import com.android.build.gradle.internal.dependency.ExtractAarTransform
+// import com.android.build.gradle.internal.publishing.AndroidArtifacts
+// import com.android.builder.aar.AarExtractor
+// import com.google.common.collect.ImmutableList
+
+// import java.nio.file.Files
+// import static java.nio.file.StandardCopyOption.REPLACE_EXISTING
+
+
+// import org.gradle.api.Plugin
+// import org.gradle.api.Project
+// import org.gradle.api.artifacts.Configuration
+// import org.gradle.api.artifacts.ResolvedArtifact
+// import java.util.zip.ZipFile
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.tasks.TaskAction
+import java.util.zip.ZipFile
+
+/**
+ * Build Gradle plgin needed to use aar files as dependencies in a pure java library project.
+ * Adapted from the following plugin by nekocode
+ * https://github.com/nekocode/Gradle-Import-Aar
+ * Ported to Groovy, and made specific to the needs of the Android mode build process (i.e.: this plugin
+ * is not meant to be used with other projects).
+ * Ported to Gradle 8 replacing the deprecated ArtifactTransform with the new TransformAction API.
+ */
+class ImportAar implements Plugin {
+ final String CONFIG_NAME_POSTFIX = "Aar"
+
+ @Override
+ void apply(Project project) {
+ // def aar = AndroidArtifacts.TYPE_AAR
+ // def jar = AndroidArtifacts.TYPE_JAR
+
+ println ">>> Calling ImportAar"
+
+ // Create a custom resolvable configuration
+ project.configurations.create('aarExtractorResolvable') {
+ canBeResolved = true
+ canBeConsumed = false
+ extendsFrom project.configurations.implementation
+ }
+
+ project.tasks.register('extractAarJars', ExtractAarJarsTask) {
+ group = 'build'
+ description = 'Extracts JAR files from AAR dependencies and places them in build/libs.'
+ }
+
+
+ // project.task('extractAarJars') {
+ // doLast {
+ // println "=======> Calling extractAarJars task"
+
+ // project.configurations.each { Configuration config ->
+ // config.resolvedConfiguration.resolvedArtifacts.each { ResolvedArtifact artifact ->
+ // println "Resolved artifact: ${artifact}"
+ // if (artifact.type == 'aar') {
+ // extractJarFromAar(artifact, project)
+ // }
+ // }
+ // }
+ // }
+ // }
+
+
+
+/*
+ // Create AAR configurations
+ Collection allConfigs = project.getConfigurations().toList()
+ for (Configuration config: allConfigs) {
+ println config
+ Configuration aarConfig = project.configurations.maybeCreate(config.name + CONFIG_NAME_POSTFIX)
+ println aarConfig
+
+ // Add extracted jars to original configuration after project evaluating
+ aarConfig.attributes {
+ attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR))
+ attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
+ attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category, Category.LIBRARY))
+ }
+
+ project.afterEvaluate {
+ println "-> In afterEvaluate"
+ aarConfig.resolvedConfiguration.resolvedArtifacts.each { artifact ->
+ File jarFile = artifact.file
+ print "================================================> FILE "
+ println jarFile
+ println jarFile.getName()
+
+ // Add jar file to classpath
+ project.sourceSets.main.compileClasspath += project.files(jarFile)
+
+ File libraryFolder = new File(project.buildDir, "libs")
+ libraryFolder.mkdirs()
+
+ // Strip version number when copying
+ String name = jarFile.name
+ int p = name.lastIndexOf("-")
+ String libName = name.substring(0, p) + ".jar"
+ File libraryJar = new File(libraryFolder, libName)
+ Files.copy(jarFile.toPath(), libraryJar.toPath(), REPLACE_EXISTING)
+ }
+ }
+ }
+
+ // Register aar transform
+ project.dependencies {
+ registerTransform(AarToJarTransform) {
+ from.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, aar))
+ to.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, jar))
+ }
+ }
+ */
+ }
+
+ // void extractJarFromAar(ResolvedArtifact artifact, Project project) {
+ // println "Input AAR: ${aarFile}"
+ // def aarFile = artifact.file
+ // def zipFile = new ZipFile(aarFile)
+ // def entry = zipFile.getEntry('classes.jar')
+ // if (entry) {
+ // project.copy {
+ // from project.zipTree(aarFile)
+ // include 'classes.jar'
+ // into "${project.buildDir}/libs"
+ // rename { "${artifact.name}-${artifact.moduleVersion.id.version}.jar" }
+ // }
+ // }
+ // zipFile.close()
+ // }
+
+
+ // abstract static class AarToJarTransform implements TransformAction {
+ // AarToJarTransform() {
+ // println "AarToJarTransform instantiated"
+ // }
+
+ // @InputArtifact
+ // @PathSensitive(PathSensitivity.NAME_ONLY)
+ // abstract Provider getInputArtifact()
+
+ // @Override
+ // void transform(TransformOutputs outputs) {
+ // File inputFile = inputArtifact.get().asFile
+ // println "Input AAR: ${inputFile}"
+ // File explodedDir = new File(outputs.getOutputDirectory(), "exploded")
+ // println "Exploded Directory: ${explodedDir}"
+
+ // AarExtractor aarExtractor = new AarExtractor()
+ // aarExtractor.extract(inputFile, explodedDir)
+ // File classesJar = new File(new File(explodedDir, "jars"), "classes.jar")
+ // if (classesJar.exists()) {
+ // println "Classes JAR found: ${classesJar}"
+ // String aarName = inputFile.name.replace(".aar", "")
+ // File renamedJar = outputs.file("${aarName}.jar")
+ // Files.copy(classesJar.toPath(), renamedJar.toPath(), REPLACE_EXISTING)
+ // println "Transformed JAR: ${renamedJar}"
+ // } else {
+ // println "Error: classes.jar not found in ${explodedDir}"
+ // }
+ // }
+ // }
+}
+
+
+class ExtractAarJarsTask extends org.gradle.api.DefaultTask {
+ @TaskAction
+ void extractJars() {
+ //File outputDir = new File(project.buildDir, 'libs')
+ File outputDir = new File(System.getProperty("user.dir"), "build/libs")
+ outputDir.mkdirs()
+
+ // Configuration compileClasspath = project.configurations.getByName('implementation')
+ Configuration aarExtractorResolvable = project.configurations.getByName('aarExtractorResolvable')
+
+ // compileClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact ->
+ aarExtractorResolvable.resolvedConfiguration.resolvedArtifacts.each { artifact ->
+ if (artifact.type == 'aar') {
+ File aarFile = artifact.file
+ println "Processing AAR: ${aarFile.name}"
+
+ // Extract the AAR file
+ ZipFile zipFile = new ZipFile(aarFile)
+ zipFile.entries().each { entry ->
+ if (entry.name.endsWith('.jar')) {
+ println "Classes JAR found: ${entry}"
+ String aarName = aarFile.name.replace(".aar", "")
+ String jarName = "${aarName}.jar".replaceFirst(/-\d+(\.\d+)*(?=\.jar$)/, '')
+
+ File jarOutput = new File(outputDir, jarName)
+ jarOutput.parentFile.mkdirs()
+
+ // Write the JAR file to the output directory
+ zipFile.getInputStream(entry).withCloseable { inputStream ->
+ jarOutput.withOutputStream { outputStream ->
+ copyStream(inputStream, outputStream)
+ }
+ }
+ println "Extracted JAR: ${jarOutput.absolutePath}"
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Copies data from an InputStream to an OutputStream.
+ */
+ void copyStream(InputStream input, OutputStream output) {
+ byte[] buffer = new byte[1024]
+ int bytesRead
+ while ((bytesRead = input.read(buffer)) != -1) {
+ output.write(buffer, 0, bytesRead)
+ }
+ }
+}
+
diff --git a/processing/buildSrc/src/main/resources/META-INF/gradle-plugins/ImportAar.properties b/processing/buildSrc/src/main/resources/META-INF/gradle-plugins/ImportAar.properties
new file mode 100644
index 000000000..f41e15936
--- /dev/null
+++ b/processing/buildSrc/src/main/resources/META-INF/gradle-plugins/ImportAar.properties
@@ -0,0 +1 @@
+implementation-class=ImportAar
\ No newline at end of file
diff --git a/processing/core/build.gradle b/processing/core/build.gradle
new file mode 100644
index 000000000..07a8b8b73
--- /dev/null
+++ b/processing/core/build.gradle
@@ -0,0 +1,110 @@
+import org.apache.tools.ant.Project
+import java.nio.file.Files
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+plugins {
+ id 'ImportAar'
+ id 'java-library'
+ id 'maven-publish'
+}
+
+dependencies {
+ implementation name: "android"
+ implementation "androidx.legacy:legacy-support-v4:${v4legacyVersion}"
+ implementation "com.google.android.support:wearable:${wearVersion}"
+}
+
+sourceSets.main {
+ java.srcDir("../../libs/processing-core/src/main/java/")
+ resources.srcDir("../../libs/processing-core/src/main/")
+ resources.exclude("AndroidManifest.xml", "**/java/**")
+}
+
+tasks.register('sourceJar', Jar) {
+ dependsOn classes
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
+ archiveClassifier.set("sources")
+ from sourceSets.main.allSource
+}
+
+// Does not work because of Processing-specific tags in source code, such as @webref
+tasks.register('javadocJar', Jar) {
+ dependsOn javadoc
+ archiveClassifier.set("javadoc")
+ from javadoc.destinationDir
+}
+
+// project.afterEvaluate {
+// tasks.named('extractAarJars').configure {
+// dependsOn configurations.runtimeClasspath
+// }
+// }
+
+// project.tasks.named('build').configure {
+// finalizedBy('extractAarJars')
+// }
+
+artifacts {
+// archives javadocJar
+ archives sourceJar
+}
+
+jar.doLast { task ->
+ ant.checksum file: task.archiveFile.get().asFile
+}
+
+tasks.named('clean').configure {
+ doFirst {
+ delete "dist"
+ delete "${coreZipPath}"
+ }
+}
+
+tasks.named('compileJava').configure {
+ doFirst {
+ String[] deps = ["wearable.jar"]
+ deps.each { fn ->
+ Files.copy(file("${rootDir}/build/libs/${fn}").toPath(),
+ file("${rootDir}/mode/mode/${fn}").toPath(), REPLACE_EXISTING)
+ }
+ }
+}
+
+tasks.named('build').configure {
+ doLast {
+ // Need to check the existance of the files before using as the files
+ // will get generated only if Task :core:jar is not being skipped
+ // Task :core:jar will be skipped if source files are unchanged or jar task is UP-TO-DATE
+ if (file("${buildDir}/libs/core.jar").exists()) {
+ // Copying core jar as zip inside the mode folder
+ Files.copy(file("${buildDir}/libs/core.jar").toPath(),
+ file("${coreZipPath}").toPath(), REPLACE_EXISTING)
+ }
+ // Renaming artifacts for maven publishing
+ if (file("${buildDir}/libs/core.jar").exists()) {
+ Files.move(file("${buildDir}/libs/core.jar").toPath(),
+ file("$buildDir/libs/processing-core-${modeVersion}.jar").toPath(), REPLACE_EXISTING)
+ }
+ if (file("${buildDir}/libs/core-sources.jar").exists()) {
+ Files.move(file("${buildDir}/libs/core-sources.jar").toPath(),
+ file("$buildDir/libs/processing-core-${modeVersion}-sources.jar").toPath(), REPLACE_EXISTING)
+ }
+ if (file("${buildDir}/libs/core.jar.MD5").exists()) {
+ Files.move(file("${buildDir}/libs/core.jar.MD5").toPath(),
+ file("$buildDir/libs/processing-core-${modeVersion}.jar.md5").toPath(), REPLACE_EXISTING)
+ }
+ }
+}
+
+ext {
+ libName = 'processing-core'
+ libVersion = modeVersion
+ libJar = "${buildDir}/libs/${libName}-${libVersion}.jar"
+ libSrc = "${buildDir}/libs/${libName}-${libVersion}-sources.jar"
+ libMd5 = "${buildDir}/libs/${libName}-${libVersion}-sources.jar.md5"
+ libDependencies = [[name: 'legacy-support-v4', group: 'androidx.legacy', version: v4legacyVersion],
+ [name: 'wearable', group: 'com.google.android.support', version: wearVersion],
+ [name: 'android']]
+}
+
+apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
diff --git a/processing/gradle.properties b/processing/gradle.properties
new file mode 100644
index 000000000..5465fec0e
--- /dev/null
+++ b/processing/gradle.properties
@@ -0,0 +1,2 @@
+android.enableJetifier=true
+android.useAndroidX=true
\ No newline at end of file
diff --git a/processing/gradle/wrapper/gradle-wrapper.jar b/processing/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..41d9927a4
Binary files /dev/null and b/processing/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/processing/gradle/wrapper/gradle-wrapper.properties b/processing/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..d6e308a63
--- /dev/null
+++ b/processing/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/processing/gradlew b/processing/gradlew
new file mode 100755
index 000000000..1b6c78733
--- /dev/null
+++ b/processing/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/processing/gradlew.bat b/processing/gradlew.bat
new file mode 100644
index 000000000..ac1b06f93
--- /dev/null
+++ b/processing/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/processing/mode/.classpath b/processing/mode/.classpath
new file mode 100644
index 000000000..3242dff64
--- /dev/null
+++ b/processing/mode/.classpath
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/processing/mode/.project b/processing/mode/.project
new file mode 100644
index 000000000..e89bdfe07
--- /dev/null
+++ b/processing/mode/.project
@@ -0,0 +1,34 @@
+
+
+ android-mode
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.buildship.core.gradleprojectnature
+
+
+
+ 1675640664215
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
+
diff --git a/processing/mode/.settings/org.eclipse.buildship.core.prefs b/processing/mode/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 000000000..e8895216f
--- /dev/null
+++ b/processing/mode/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/processing/mode/.settings/org.eclipse.jdt.core.prefs b/processing/mode/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0fee6a9c4
--- /dev/null
+++ b/processing/mode/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/processing/mode/build.gradle b/processing/mode/build.gradle
new file mode 100644
index 000000000..0f34a95d9
--- /dev/null
+++ b/processing/mode/build.gradle
@@ -0,0 +1,102 @@
+import java.nio.file.Files
+import org.zeroturnaround.zip.ZipUtil
+import org.apache.commons.io.FileUtils
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+plugins {
+ id 'java'
+}
+
+// Extend compile to copy the jars from gradle-tooling and slf4j:
+// https://stackoverflow.com/a/43602463
+configurations {
+ implementationCopy
+ implementationExtract
+}
+
+dependencies {
+ // implementation group: "org.processing", name: "core", version: "${processingVersion}"
+ // implementation group: "org.processing", name: "pde", version: "${processingVersion}"
+ // implementation group: "org.processing", name: "java-mode", version: "${processingVersion}"
+
+ implementationExtract "org.eclipse.jdt:org.eclipse.jdt.debug:${jdtVersion}"
+
+ implementationCopy "org.gradle:gradle-tooling-api:${toolingVersion}"
+ implementationCopy "org.slf4j:slf4j-api:${slf4jVersion}"
+ implementationCopy "org.slf4j:slf4j-simple:${slf4jVersion}"
+
+ implementation fileTree(include: ["jdi.jar", "jdimodel.jar", "core.jar", "pde.jar", "JavaMode.jar"], dir: 'mode')
+}
+
+// This task copies the gradle tooling jar into the mode folder
+tasks.register("copyToLib", Copy) {
+ from(configurations.implementationCopy)
+ into("mode")
+}
+
+tasks.named('build') {
+ dependsOn 'copyToLib'
+}
+
+tasks.named('compileJava') {
+ dependsOn 'copyToLib'
+}
+
+sourceSets.main.java.srcDir("src/")
+
+tasks.register('getjdi', Copy) {
+ // This task extracts the jar files inside org.eclipse.jdt.debug, which are
+ // jdi.jar and jdimodel.jar and needed to build the debugger.
+ from(zipTree(configurations.implementationExtract.singleFile)) {
+ include '**/*.jar'
+ exclude 'META-INF'
+ }
+ into "mode"
+}
+
+tasks.register('permissions', Exec) {
+ // This task retrieves the latest list of Android permissions and adds them
+ // to the Permissions.java file. The python scripts requries BeautifulSoup
+ workingDir "scripts"
+ commandLine "python", "permissions.py"
+}
+
+
+tasks.register("wrapper", Wrapper) {
+ gradleVersion = "${gradlewVersion}" // version required for gradle wrapper
+}
+
+tasks.named("wrapper").configure {
+ doLast {
+ def wrapperFolder = file("mode/gradlew")
+ wrapperFolder.mkdirs()
+ file("gradle").renameTo(file("mode/gradlew/gradle"))
+ file("gradlew").renameTo(file("mode/gradlew/gradlew"))
+ file("gradlew.bat").renameTo(file("mode/gradlew/gradlew.bat"))
+ FileUtils.copyDirectory(file("gradle"), file("../debug/gradle"))
+ delete("gradle")
+ ZipUtil.pack(file("mode/gradlew"), new File("mode/mode/gradlew.zip"))
+ delete("mode/gradlew")
+ }
+}
+
+tasks.named('clean') {
+ doFirst {
+ delete fileTree("mode") {
+ include "**/*.jar"
+ exclude "jdi.jar", "jdimodel.jar", "istack-commons-runtime.jar", "javax.activation-api.jar",
+ "jaxb-api.jar", "jaxb-jxc.jar", "jaxb-runtime.jar", "jaxb-xjc.jar", "core.jar",
+ "pde.jar", "JavaMode.jar", "org.eclipse.core.contenttype.jar", "org.eclipse.core.jobs.jar",
+ "org.eclipse.core.resources.jar", "org.eclipse.core.runtime.jar", "org.eclipse.equinox.common.jar",
+ "org.eclipse.equinox.preferences.jar", "org.eclipse.jdt.core.jar", "org.eclipse.osgi.jar",
+ "org.eclipse.text.jar"
+ }
+ }
+}
+
+tasks.named('build') {
+ doLast {
+ Files.copy(file("$buildDir/libs/mode.jar").toPath(),
+ file("mode/AndroidMode.jar").toPath(), REPLACE_EXISTING)
+ }
+}
\ No newline at end of file
diff --git a/examples/Basics/Arrays/Array/Array.pde b/processing/mode/examples/Basics/Arrays/Array/Array.pde
similarity index 100%
rename from examples/Basics/Arrays/Array/Array.pde
rename to processing/mode/examples/Basics/Arrays/Array/Array.pde
diff --git a/examples/Basics/Arrays/Array2D/Array2D.pde b/processing/mode/examples/Basics/Arrays/Array2D/Array2D.pde
similarity index 100%
rename from examples/Basics/Arrays/Array2D/Array2D.pde
rename to processing/mode/examples/Basics/Arrays/Array2D/Array2D.pde
diff --git a/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde b/processing/mode/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde
similarity index 100%
rename from examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde
rename to processing/mode/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde
diff --git a/examples/Basics/Arrays/ArrayObjects/Module.pde b/processing/mode/examples/Basics/Arrays/ArrayObjects/Module.pde
similarity index 100%
rename from examples/Basics/Arrays/ArrayObjects/Module.pde
rename to processing/mode/examples/Basics/Arrays/ArrayObjects/Module.pde
diff --git a/examples/Basics/Camera/MoveEye/MoveEye.pde b/processing/mode/examples/Basics/Camera/MoveEye/MoveEye.pde
similarity index 100%
rename from examples/Basics/Camera/MoveEye/MoveEye.pde
rename to processing/mode/examples/Basics/Camera/MoveEye/MoveEye.pde
diff --git a/examples/Basics/Camera/Perspective/Perspective.pde b/processing/mode/examples/Basics/Camera/Perspective/Perspective.pde
similarity index 100%
rename from examples/Basics/Camera/Perspective/Perspective.pde
rename to processing/mode/examples/Basics/Camera/Perspective/Perspective.pde
diff --git a/examples/Basics/Color/Brightness/Brightness.pde b/processing/mode/examples/Basics/Color/Brightness/Brightness.pde
similarity index 100%
rename from examples/Basics/Color/Brightness/Brightness.pde
rename to processing/mode/examples/Basics/Color/Brightness/Brightness.pde
diff --git a/examples/Basics/Color/ColorWheel/ColorWheel.pde b/processing/mode/examples/Basics/Color/ColorWheel/ColorWheel.pde
similarity index 100%
rename from examples/Basics/Color/ColorWheel/ColorWheel.pde
rename to processing/mode/examples/Basics/Color/ColorWheel/ColorWheel.pde
diff --git a/examples/Basics/Color/Creating/Creating.pde b/processing/mode/examples/Basics/Color/Creating/Creating.pde
similarity index 100%
rename from examples/Basics/Color/Creating/Creating.pde
rename to processing/mode/examples/Basics/Color/Creating/Creating.pde
diff --git a/examples/Basics/Color/Hue/Hue.pde b/processing/mode/examples/Basics/Color/Hue/Hue.pde
similarity index 100%
rename from examples/Basics/Color/Hue/Hue.pde
rename to processing/mode/examples/Basics/Color/Hue/Hue.pde
diff --git a/examples/Basics/Color/LinearGradient/LinearGradient.pde b/processing/mode/examples/Basics/Color/LinearGradient/LinearGradient.pde
similarity index 100%
rename from examples/Basics/Color/LinearGradient/LinearGradient.pde
rename to processing/mode/examples/Basics/Color/LinearGradient/LinearGradient.pde
diff --git a/examples/Basics/Color/RadialGradient/RadialGradient.pde b/processing/mode/examples/Basics/Color/RadialGradient/RadialGradient.pde
similarity index 100%
rename from examples/Basics/Color/RadialGradient/RadialGradient.pde
rename to processing/mode/examples/Basics/Color/RadialGradient/RadialGradient.pde
diff --git a/examples/Basics/Color/RadialGradient2/RadialGradient2.pde b/processing/mode/examples/Basics/Color/RadialGradient2/RadialGradient2.pde
similarity index 100%
rename from examples/Basics/Color/RadialGradient2/RadialGradient2.pde
rename to processing/mode/examples/Basics/Color/RadialGradient2/RadialGradient2.pde
diff --git a/examples/Basics/Color/Reading/Reading.pde b/processing/mode/examples/Basics/Color/Reading/Reading.pde
similarity index 100%
rename from examples/Basics/Color/Reading/Reading.pde
rename to processing/mode/examples/Basics/Color/Reading/Reading.pde
diff --git a/examples/Basics/Color/Reading/data/cait.jpg b/processing/mode/examples/Basics/Color/Reading/data/cait.jpg
similarity index 100%
rename from examples/Basics/Color/Reading/data/cait.jpg
rename to processing/mode/examples/Basics/Color/Reading/data/cait.jpg
diff --git a/examples/Basics/Color/Relativity/Relativity.pde b/processing/mode/examples/Basics/Color/Relativity/Relativity.pde
similarity index 100%
rename from examples/Basics/Color/Relativity/Relativity.pde
rename to processing/mode/examples/Basics/Color/Relativity/Relativity.pde
diff --git a/examples/Basics/Color/Saturation/Saturation.pde b/processing/mode/examples/Basics/Color/Saturation/Saturation.pde
similarity index 100%
rename from examples/Basics/Color/Saturation/Saturation.pde
rename to processing/mode/examples/Basics/Color/Saturation/Saturation.pde
diff --git a/examples/Basics/Color/WaveGradient/WaveGradient.pde b/processing/mode/examples/Basics/Color/WaveGradient/WaveGradient.pde
similarity index 100%
rename from examples/Basics/Color/WaveGradient/WaveGradient.pde
rename to processing/mode/examples/Basics/Color/WaveGradient/WaveGradient.pde
diff --git a/examples/Basics/Control/Conditionals1/Conditionals1.pde b/processing/mode/examples/Basics/Control/Conditionals1/Conditionals1.pde
similarity index 100%
rename from examples/Basics/Control/Conditionals1/Conditionals1.pde
rename to processing/mode/examples/Basics/Control/Conditionals1/Conditionals1.pde
diff --git a/examples/Basics/Control/Conditionals2/Conditionals2.pde b/processing/mode/examples/Basics/Control/Conditionals2/Conditionals2.pde
similarity index 100%
rename from examples/Basics/Control/Conditionals2/Conditionals2.pde
rename to processing/mode/examples/Basics/Control/Conditionals2/Conditionals2.pde
diff --git a/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde b/processing/mode/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde
similarity index 100%
rename from examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde
rename to processing/mode/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde
diff --git a/examples/Basics/Control/Iteration/Iteration.pde b/processing/mode/examples/Basics/Control/Iteration/Iteration.pde
similarity index 100%
rename from examples/Basics/Control/Iteration/Iteration.pde
rename to processing/mode/examples/Basics/Control/Iteration/Iteration.pde
diff --git a/examples/Basics/Control/LogicalOperators/LogicalOperators.pde b/processing/mode/examples/Basics/Control/LogicalOperators/LogicalOperators.pde
similarity index 100%
rename from examples/Basics/Control/LogicalOperators/LogicalOperators.pde
rename to processing/mode/examples/Basics/Control/LogicalOperators/LogicalOperators.pde
diff --git a/examples/Basics/Data/CharactersStrings/CharactersStrings.pde b/processing/mode/examples/Basics/Data/CharactersStrings/CharactersStrings.pde
similarity index 100%
rename from examples/Basics/Data/CharactersStrings/CharactersStrings.pde
rename to processing/mode/examples/Basics/Data/CharactersStrings/CharactersStrings.pde
diff --git a/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw b/processing/mode/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw
similarity index 100%
rename from examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw
rename to processing/mode/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw
diff --git a/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg b/processing/mode/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg
similarity index 100%
rename from examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg
rename to processing/mode/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg
diff --git a/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde b/processing/mode/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde
similarity index 100%
rename from examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde
rename to processing/mode/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde
diff --git a/examples/Basics/Data/IntegersFloats/IntegersFloats.pde b/processing/mode/examples/Basics/Data/IntegersFloats/IntegersFloats.pde
similarity index 100%
rename from examples/Basics/Data/IntegersFloats/IntegersFloats.pde
rename to processing/mode/examples/Basics/Data/IntegersFloats/IntegersFloats.pde
diff --git a/examples/Basics/Data/TrueFalse/TrueFalse.pde b/processing/mode/examples/Basics/Data/TrueFalse/TrueFalse.pde
similarity index 100%
rename from examples/Basics/Data/TrueFalse/TrueFalse.pde
rename to processing/mode/examples/Basics/Data/TrueFalse/TrueFalse.pde
diff --git a/examples/Basics/Data/VariableScope/VariableScope.pde b/processing/mode/examples/Basics/Data/VariableScope/VariableScope.pde
similarity index 100%
rename from examples/Basics/Data/VariableScope/VariableScope.pde
rename to processing/mode/examples/Basics/Data/VariableScope/VariableScope.pde
diff --git a/examples/Basics/Data/Variables/Variables.pde b/processing/mode/examples/Basics/Data/Variables/Variables.pde
similarity index 100%
rename from examples/Basics/Data/Variables/Variables.pde
rename to processing/mode/examples/Basics/Data/Variables/Variables.pde
diff --git a/examples/Basics/Form/Bezier/Bezier.pde b/processing/mode/examples/Basics/Form/Bezier/Bezier.pde
similarity index 100%
rename from examples/Basics/Form/Bezier/Bezier.pde
rename to processing/mode/examples/Basics/Form/Bezier/Bezier.pde
diff --git a/examples/Basics/Form/BezierEllipse/BezierEllipse.pde b/processing/mode/examples/Basics/Form/BezierEllipse/BezierEllipse.pde
similarity index 100%
rename from examples/Basics/Form/BezierEllipse/BezierEllipse.pde
rename to processing/mode/examples/Basics/Form/BezierEllipse/BezierEllipse.pde
diff --git a/examples/Basics/Form/PieChart/PieChart.pde b/processing/mode/examples/Basics/Form/PieChart/PieChart.pde
similarity index 100%
rename from examples/Basics/Form/PieChart/PieChart.pde
rename to processing/mode/examples/Basics/Form/PieChart/PieChart.pde
diff --git a/examples/Basics/Form/PointsLines/PointsLines.pde b/processing/mode/examples/Basics/Form/PointsLines/PointsLines.pde
similarity index 100%
rename from examples/Basics/Form/PointsLines/PointsLines.pde
rename to processing/mode/examples/Basics/Form/PointsLines/PointsLines.pde
diff --git a/examples/Basics/Form/Primitives3D/Primitives3D.pde b/processing/mode/examples/Basics/Form/Primitives3D/Primitives3D.pde
similarity index 100%
rename from examples/Basics/Form/Primitives3D/Primitives3D.pde
rename to processing/mode/examples/Basics/Form/Primitives3D/Primitives3D.pde
diff --git a/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde b/processing/mode/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde
similarity index 100%
rename from examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde
rename to processing/mode/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde
diff --git a/examples/Basics/Form/SimpleCurves/SimpleCurves.pde b/processing/mode/examples/Basics/Form/SimpleCurves/SimpleCurves.pde
similarity index 100%
rename from examples/Basics/Form/SimpleCurves/SimpleCurves.pde
rename to processing/mode/examples/Basics/Form/SimpleCurves/SimpleCurves.pde
diff --git a/examples/Basics/Form/TriangleStrip/TriangleStrip.pde b/processing/mode/examples/Basics/Form/TriangleStrip/TriangleStrip.pde
similarity index 100%
rename from examples/Basics/Form/TriangleStrip/TriangleStrip.pde
rename to processing/mode/examples/Basics/Form/TriangleStrip/TriangleStrip.pde
diff --git a/examples/Basics/Form/Vertices/Vertices.pde b/processing/mode/examples/Basics/Form/Vertices/Vertices.pde
similarity index 100%
rename from examples/Basics/Form/Vertices/Vertices.pde
rename to processing/mode/examples/Basics/Form/Vertices/Vertices.pde
diff --git a/examples/Basics/Image/Alphamask/Alphamask.pde b/processing/mode/examples/Basics/Image/Alphamask/Alphamask.pde
similarity index 100%
rename from examples/Basics/Image/Alphamask/Alphamask.pde
rename to processing/mode/examples/Basics/Image/Alphamask/Alphamask.pde
diff --git a/examples/Basics/Image/Alphamask/data/mask.jpg b/processing/mode/examples/Basics/Image/Alphamask/data/mask.jpg
similarity index 100%
rename from examples/Basics/Image/Alphamask/data/mask.jpg
rename to processing/mode/examples/Basics/Image/Alphamask/data/mask.jpg
diff --git a/examples/Basics/Image/Alphamask/data/test.jpg b/processing/mode/examples/Basics/Image/Alphamask/data/test.jpg
similarity index 100%
rename from examples/Basics/Image/Alphamask/data/test.jpg
rename to processing/mode/examples/Basics/Image/Alphamask/data/test.jpg
diff --git a/examples/Basics/Image/BackgroundImage/BackgroundImage.pde b/processing/mode/examples/Basics/Image/BackgroundImage/BackgroundImage.pde
similarity index 100%
rename from examples/Basics/Image/BackgroundImage/BackgroundImage.pde
rename to processing/mode/examples/Basics/Image/BackgroundImage/BackgroundImage.pde
diff --git a/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg b/processing/mode/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg
similarity index 100%
rename from examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg
rename to processing/mode/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg
diff --git a/examples/Basics/Image/CreateImage/CreateImage.pde b/processing/mode/examples/Basics/Image/CreateImage/CreateImage.pde
similarity index 95%
rename from examples/Basics/Image/CreateImage/CreateImage.pde
rename to processing/mode/examples/Basics/Image/CreateImage/CreateImage.pde
index df095a75e..04e42d07b 100644
--- a/examples/Basics/Image/CreateImage/CreateImage.pde
+++ b/processing/mode/examples/Basics/Image/CreateImage/CreateImage.pde
@@ -11,6 +11,7 @@ void setup()
{
size(200, 200);
img = createImage(120, 120, ARGB);
+ img.loadPixels();
for(int i=0; i < img.pixels.length; i++) {
img.pixels[i] = color(0, 90, 102, i%img.width * 2);
}
diff --git a/examples/Basics/Image/CreateImage/data/mask.jpg b/processing/mode/examples/Basics/Image/CreateImage/data/mask.jpg
similarity index 100%
rename from examples/Basics/Image/CreateImage/data/mask.jpg
rename to processing/mode/examples/Basics/Image/CreateImage/data/mask.jpg
diff --git a/examples/Basics/Image/CreateImage/data/test.jpg b/processing/mode/examples/Basics/Image/CreateImage/data/test.jpg
similarity index 100%
rename from examples/Basics/Image/CreateImage/data/test.jpg
rename to processing/mode/examples/Basics/Image/CreateImage/data/test.jpg
diff --git a/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde b/processing/mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde
similarity index 93%
rename from examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde
rename to processing/mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde
index 8252c5cbc..495d1f124 100644
--- a/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde
+++ b/processing/mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde
@@ -12,7 +12,6 @@ void setup() {
// The file "jelly.jpg" must be in the data folder
// of the current sketch to load successfully
a = loadImage("jelly.jpg"); // Load the image into the program
- noLoop(); // Makes draw() only run once
}
void draw() {
diff --git a/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg b/processing/mode/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg
similarity index 100%
rename from examples/Basics/Image/LoadDisplayImage/data/jelly.jpg
rename to processing/mode/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg
diff --git a/examples/Basics/Image/Pointillism/Pointillism.pde b/processing/mode/examples/Basics/Image/Pointillism/Pointillism.pde
similarity index 100%
rename from examples/Basics/Image/Pointillism/Pointillism.pde
rename to processing/mode/examples/Basics/Image/Pointillism/Pointillism.pde
diff --git a/examples/Basics/Image/Pointillism/data/eames.jpg b/processing/mode/examples/Basics/Image/Pointillism/data/eames.jpg
similarity index 100%
rename from examples/Basics/Image/Pointillism/data/eames.jpg
rename to processing/mode/examples/Basics/Image/Pointillism/data/eames.jpg
diff --git a/examples/Basics/Image/Pointillism/data/sunflower.jpg b/processing/mode/examples/Basics/Image/Pointillism/data/sunflower.jpg
similarity index 100%
rename from examples/Basics/Image/Pointillism/data/sunflower.jpg
rename to processing/mode/examples/Basics/Image/Pointillism/data/sunflower.jpg
diff --git a/examples/Basics/Image/RequestImage/RequestImage.pde b/processing/mode/examples/Basics/Image/RequestImage/RequestImage.pde
similarity index 100%
rename from examples/Basics/Image/RequestImage/RequestImage.pde
rename to processing/mode/examples/Basics/Image/RequestImage/RequestImage.pde
diff --git a/examples/Basics/Image/Sprite/Sprite.pde b/processing/mode/examples/Basics/Image/Sprite/Sprite.pde
similarity index 100%
rename from examples/Basics/Image/Sprite/Sprite.pde
rename to processing/mode/examples/Basics/Image/Sprite/Sprite.pde
diff --git a/examples/Basics/Image/Sprite/data/teddy.gif b/processing/mode/examples/Basics/Image/Sprite/data/teddy.gif
similarity index 100%
rename from examples/Basics/Image/Sprite/data/teddy.gif
rename to processing/mode/examples/Basics/Image/Sprite/data/teddy.gif
diff --git a/examples/Basics/Image/Sprite2/Sprite2.pde b/processing/mode/examples/Basics/Image/Sprite2/Sprite2.pde
similarity index 100%
rename from examples/Basics/Image/Sprite2/Sprite2.pde
rename to processing/mode/examples/Basics/Image/Sprite2/Sprite2.pde
diff --git a/examples/Basics/Image/Sprite2/data/sky.jpg b/processing/mode/examples/Basics/Image/Sprite2/data/sky.jpg
similarity index 100%
rename from examples/Basics/Image/Sprite2/data/sky.jpg
rename to processing/mode/examples/Basics/Image/Sprite2/data/sky.jpg
diff --git a/examples/Basics/Image/Sprite2/data/teddy.gif b/processing/mode/examples/Basics/Image/Sprite2/data/teddy.gif
similarity index 100%
rename from examples/Basics/Image/Sprite2/data/teddy.gif
rename to processing/mode/examples/Basics/Image/Sprite2/data/teddy.gif
diff --git a/examples/Basics/Image/Transparency/Transparency.pde b/processing/mode/examples/Basics/Image/Transparency/Transparency.pde
similarity index 100%
rename from examples/Basics/Image/Transparency/Transparency.pde
rename to processing/mode/examples/Basics/Image/Transparency/Transparency.pde
diff --git a/examples/Basics/Image/Transparency/data/construct.jpg b/processing/mode/examples/Basics/Image/Transparency/data/construct.jpg
similarity index 100%
rename from examples/Basics/Image/Transparency/data/construct.jpg
rename to processing/mode/examples/Basics/Image/Transparency/data/construct.jpg
diff --git a/examples/Basics/Image/Transparency/data/wash.jpg b/processing/mode/examples/Basics/Image/Transparency/data/wash.jpg
similarity index 100%
rename from examples/Basics/Image/Transparency/data/wash.jpg
rename to processing/mode/examples/Basics/Image/Transparency/data/wash.jpg
diff --git a/examples/Basics/Input/Clock/Clock.pde b/processing/mode/examples/Basics/Input/Clock/Clock.pde
similarity index 100%
rename from examples/Basics/Input/Clock/Clock.pde
rename to processing/mode/examples/Basics/Input/Clock/Clock.pde
diff --git a/examples/Basics/Input/Constrain/Constrain.pde b/processing/mode/examples/Basics/Input/Constrain/Constrain.pde
similarity index 100%
rename from examples/Basics/Input/Constrain/Constrain.pde
rename to processing/mode/examples/Basics/Input/Constrain/Constrain.pde
diff --git a/examples/Basics/Input/Easing/Easing.pde b/processing/mode/examples/Basics/Input/Easing/Easing.pde
similarity index 100%
rename from examples/Basics/Input/Easing/Easing.pde
rename to processing/mode/examples/Basics/Input/Easing/Easing.pde
diff --git a/examples/Basics/Input/Keyboard/Keyboard.pde b/processing/mode/examples/Basics/Input/Keyboard/Keyboard.pde
similarity index 100%
rename from examples/Basics/Input/Keyboard/Keyboard.pde
rename to processing/mode/examples/Basics/Input/Keyboard/Keyboard.pde
diff --git a/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde b/processing/mode/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde
similarity index 100%
rename from examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde
rename to processing/mode/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde
diff --git a/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg b/processing/mode/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg
similarity index 100%
rename from examples/Basics/Input/KeyboardFunctions/data/brugges.jpg
rename to processing/mode/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg
diff --git a/examples/Basics/Input/Milliseconds/Milliseconds.pde b/processing/mode/examples/Basics/Input/Milliseconds/Milliseconds.pde
similarity index 100%
rename from examples/Basics/Input/Milliseconds/Milliseconds.pde
rename to processing/mode/examples/Basics/Input/Milliseconds/Milliseconds.pde
diff --git a/examples/Basics/Input/Mouse1D/Mouse1D.pde b/processing/mode/examples/Basics/Input/Mouse1D/Mouse1D.pde
similarity index 100%
rename from examples/Basics/Input/Mouse1D/Mouse1D.pde
rename to processing/mode/examples/Basics/Input/Mouse1D/Mouse1D.pde
diff --git a/examples/Basics/Input/Mouse2D/Mouse2D.pde b/processing/mode/examples/Basics/Input/Mouse2D/Mouse2D.pde
similarity index 100%
rename from examples/Basics/Input/Mouse2D/Mouse2D.pde
rename to processing/mode/examples/Basics/Input/Mouse2D/Mouse2D.pde
diff --git a/examples/Basics/Input/MouseFunctions/MouseFunctions.pde b/processing/mode/examples/Basics/Input/MouseFunctions/MouseFunctions.pde
similarity index 100%
rename from examples/Basics/Input/MouseFunctions/MouseFunctions.pde
rename to processing/mode/examples/Basics/Input/MouseFunctions/MouseFunctions.pde
diff --git a/examples/Basics/Input/MousePress/MousePress.pde b/processing/mode/examples/Basics/Input/MousePress/MousePress.pde
similarity index 100%
rename from examples/Basics/Input/MousePress/MousePress.pde
rename to processing/mode/examples/Basics/Input/MousePress/MousePress.pde
diff --git a/examples/Basics/Input/MouseSignals/MouseSignals.pde b/processing/mode/examples/Basics/Input/MouseSignals/MouseSignals.pde
similarity index 100%
rename from examples/Basics/Input/MouseSignals/MouseSignals.pde
rename to processing/mode/examples/Basics/Input/MouseSignals/MouseSignals.pde
diff --git a/examples/Basics/Input/StoringInput/StoringInput.pde b/processing/mode/examples/Basics/Input/StoringInput/StoringInput.pde
similarity index 100%
rename from examples/Basics/Input/StoringInput/StoringInput.pde
rename to processing/mode/examples/Basics/Input/StoringInput/StoringInput.pde
diff --git a/examples/Basics/Lights/Directional/Directional.pde b/processing/mode/examples/Basics/Lights/Directional/Directional.pde
similarity index 100%
rename from examples/Basics/Lights/Directional/Directional.pde
rename to processing/mode/examples/Basics/Lights/Directional/Directional.pde
diff --git a/examples/Basics/Lights/Mixture/Mixture.pde b/processing/mode/examples/Basics/Lights/Mixture/Mixture.pde
similarity index 100%
rename from examples/Basics/Lights/Mixture/Mixture.pde
rename to processing/mode/examples/Basics/Lights/Mixture/Mixture.pde
diff --git a/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde b/processing/mode/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde
similarity index 100%
rename from examples/Basics/Lights/MixtureGrid/MixtureGrid.pde
rename to processing/mode/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde
diff --git a/examples/Basics/Lights/OnOff/OnOff.pde b/processing/mode/examples/Basics/Lights/OnOff/OnOff.pde
similarity index 100%
rename from examples/Basics/Lights/OnOff/OnOff.pde
rename to processing/mode/examples/Basics/Lights/OnOff/OnOff.pde
diff --git a/examples/Basics/Lights/Reflection/Reflection.pde b/processing/mode/examples/Basics/Lights/Reflection/Reflection.pde
similarity index 100%
rename from examples/Basics/Lights/Reflection/Reflection.pde
rename to processing/mode/examples/Basics/Lights/Reflection/Reflection.pde
diff --git a/examples/Basics/Lights/Spot/Spot.pde b/processing/mode/examples/Basics/Lights/Spot/Spot.pde
similarity index 100%
rename from examples/Basics/Lights/Spot/Spot.pde
rename to processing/mode/examples/Basics/Lights/Spot/Spot.pde
diff --git a/examples/Basics/Math/AdditiveWave/AdditiveWave.pde b/processing/mode/examples/Basics/Math/AdditiveWave/AdditiveWave.pde
similarity index 100%
rename from examples/Basics/Math/AdditiveWave/AdditiveWave.pde
rename to processing/mode/examples/Basics/Math/AdditiveWave/AdditiveWave.pde
diff --git a/examples/Basics/Math/Arctangent/Arctangent.pde b/processing/mode/examples/Basics/Math/Arctangent/Arctangent.pde
similarity index 100%
rename from examples/Basics/Math/Arctangent/Arctangent.pde
rename to processing/mode/examples/Basics/Math/Arctangent/Arctangent.pde
diff --git a/examples/Basics/Math/Distance1D/Distance1D.pde b/processing/mode/examples/Basics/Math/Distance1D/Distance1D.pde
similarity index 100%
rename from examples/Basics/Math/Distance1D/Distance1D.pde
rename to processing/mode/examples/Basics/Math/Distance1D/Distance1D.pde
diff --git a/examples/Basics/Math/Distance2D/Distance2D.pde b/processing/mode/examples/Basics/Math/Distance2D/Distance2D.pde
similarity index 100%
rename from examples/Basics/Math/Distance2D/Distance2D.pde
rename to processing/mode/examples/Basics/Math/Distance2D/Distance2D.pde
diff --git a/examples/Basics/Math/DoubleRandom/DoubleRandom.pde b/processing/mode/examples/Basics/Math/DoubleRandom/DoubleRandom.pde
similarity index 100%
rename from examples/Basics/Math/DoubleRandom/DoubleRandom.pde
rename to processing/mode/examples/Basics/Math/DoubleRandom/DoubleRandom.pde
diff --git a/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde b/processing/mode/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde
similarity index 100%
rename from examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde
rename to processing/mode/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde
diff --git a/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde b/processing/mode/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde
similarity index 100%
rename from examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde
rename to processing/mode/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde
diff --git a/examples/Basics/Math/Modulo/Modulo.pde b/processing/mode/examples/Basics/Math/Modulo/Modulo.pde
similarity index 100%
rename from examples/Basics/Math/Modulo/Modulo.pde
rename to processing/mode/examples/Basics/Math/Modulo/Modulo.pde
diff --git a/examples/Basics/Math/Noise1D/Noise1D.pde b/processing/mode/examples/Basics/Math/Noise1D/Noise1D.pde
similarity index 100%
rename from examples/Basics/Math/Noise1D/Noise1D.pde
rename to processing/mode/examples/Basics/Math/Noise1D/Noise1D.pde
diff --git a/examples/Basics/Math/Noise2D/Noise2D.pde b/processing/mode/examples/Basics/Math/Noise2D/Noise2D.pde
similarity index 100%
rename from examples/Basics/Math/Noise2D/Noise2D.pde
rename to processing/mode/examples/Basics/Math/Noise2D/Noise2D.pde
diff --git a/examples/Basics/Math/Noise3D/Noise3D.pde b/processing/mode/examples/Basics/Math/Noise3D/Noise3D.pde
similarity index 100%
rename from examples/Basics/Math/Noise3D/Noise3D.pde
rename to processing/mode/examples/Basics/Math/Noise3D/Noise3D.pde
diff --git a/examples/Basics/Math/NoiseWave/NoiseWave.pde b/processing/mode/examples/Basics/Math/NoiseWave/NoiseWave.pde
similarity index 100%
rename from examples/Basics/Math/NoiseWave/NoiseWave.pde
rename to processing/mode/examples/Basics/Math/NoiseWave/NoiseWave.pde
diff --git a/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde b/processing/mode/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde
similarity index 100%
rename from examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde
rename to processing/mode/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde
diff --git a/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde b/processing/mode/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde
similarity index 100%
rename from examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde
rename to processing/mode/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde
diff --git a/examples/Basics/Math/Random/Random.pde b/processing/mode/examples/Basics/Math/Random/Random.pde
similarity index 100%
rename from examples/Basics/Math/Random/Random.pde
rename to processing/mode/examples/Basics/Math/Random/Random.pde
diff --git a/examples/Basics/Math/Sine/Sine.pde b/processing/mode/examples/Basics/Math/Sine/Sine.pde
similarity index 100%
rename from examples/Basics/Math/Sine/Sine.pde
rename to processing/mode/examples/Basics/Math/Sine/Sine.pde
diff --git a/examples/Basics/Math/SineCosine/SineCosine.pde b/processing/mode/examples/Basics/Math/SineCosine/SineCosine.pde
similarity index 100%
rename from examples/Basics/Math/SineCosine/SineCosine.pde
rename to processing/mode/examples/Basics/Math/SineCosine/SineCosine.pde
diff --git a/examples/Basics/Math/SineWave/SineWave.pde b/processing/mode/examples/Basics/Math/SineWave/SineWave.pde
similarity index 100%
rename from examples/Basics/Math/SineWave/SineWave.pde
rename to processing/mode/examples/Basics/Math/SineWave/SineWave.pde
diff --git a/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde
similarity index 100%
rename from examples/Basics/Objects/CompositeObjects/CompositeObjects.pde
rename to processing/mode/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde
diff --git a/examples/Basics/Objects/CompositeObjects/Egg.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/Egg.pde
similarity index 100%
rename from examples/Basics/Objects/CompositeObjects/Egg.pde
rename to processing/mode/examples/Basics/Objects/CompositeObjects/Egg.pde
diff --git a/examples/Basics/Objects/CompositeObjects/EggRing.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/EggRing.pde
similarity index 100%
rename from examples/Basics/Objects/CompositeObjects/EggRing.pde
rename to processing/mode/examples/Basics/Objects/CompositeObjects/EggRing.pde
diff --git a/examples/Basics/Objects/CompositeObjects/Ring.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/Ring.pde
similarity index 100%
rename from examples/Basics/Objects/CompositeObjects/Ring.pde
rename to processing/mode/examples/Basics/Objects/CompositeObjects/Ring.pde
diff --git a/examples/Basics/Objects/Inheritance/Inheritance.pde b/processing/mode/examples/Basics/Objects/Inheritance/Inheritance.pde
similarity index 100%
rename from examples/Basics/Objects/Inheritance/Inheritance.pde
rename to processing/mode/examples/Basics/Objects/Inheritance/Inheritance.pde
diff --git a/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde b/processing/mode/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde
similarity index 100%
rename from examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde
rename to processing/mode/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde
diff --git a/examples/Basics/Objects/Neighborhood/Neighborhood.pde b/processing/mode/examples/Basics/Objects/Neighborhood/Neighborhood.pde
similarity index 100%
rename from examples/Basics/Objects/Neighborhood/Neighborhood.pde
rename to processing/mode/examples/Basics/Objects/Neighborhood/Neighborhood.pde
diff --git a/examples/Basics/Objects/Objects/Objects.pde b/processing/mode/examples/Basics/Objects/Objects/Objects.pde
similarity index 100%
rename from examples/Basics/Objects/Objects/Objects.pde
rename to processing/mode/examples/Basics/Objects/Objects/Objects.pde
diff --git a/examples/Basics/Shape/DisableStyle/DisableStyle.pde b/processing/mode/examples/Basics/Shape/DisableStyle/DisableStyle.pde
similarity index 100%
rename from examples/Basics/Shape/DisableStyle/DisableStyle.pde
rename to processing/mode/examples/Basics/Shape/DisableStyle/DisableStyle.pde
diff --git a/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg b/processing/mode/examples/Basics/Shape/DisableStyle/data/bot1.svg
similarity index 100%
rename from examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg
rename to processing/mode/examples/Basics/Shape/DisableStyle/data/bot1.svg
diff --git a/examples/Basics/Shape/GetChild/GetChild.pde b/processing/mode/examples/Basics/Shape/GetChild/GetChild.pde
similarity index 100%
rename from examples/Basics/Shape/GetChild/GetChild.pde
rename to processing/mode/examples/Basics/Shape/GetChild/GetChild.pde
diff --git a/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg b/processing/mode/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg
similarity index 100%
rename from examples/Basics/Shape/GetChild/data/usa-wikipedia.svg
rename to processing/mode/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg
diff --git a/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde
similarity index 100%
rename from examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde
rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde
diff --git a/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl
similarity index 100%
rename from examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl
rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl
diff --git a/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj
similarity index 100%
rename from examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj
rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj
diff --git a/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png
similarity index 100%
rename from examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png
rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png
diff --git a/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde b/processing/mode/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde
similarity index 100%
rename from examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde
rename to processing/mode/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde
diff --git a/examples/Basics/Shape/ScaleShape/data/bot1.svg b/processing/mode/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg
similarity index 100%
rename from examples/Basics/Shape/ScaleShape/data/bot1.svg
rename to processing/mode/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg
diff --git a/examples/Basics/Shape/ScaleShape/ScaleShape.pde b/processing/mode/examples/Basics/Shape/ScaleShape/ScaleShape.pde
similarity index 100%
rename from examples/Basics/Shape/ScaleShape/ScaleShape.pde
rename to processing/mode/examples/Basics/Shape/ScaleShape/ScaleShape.pde
diff --git a/processing/mode/examples/Basics/Shape/ScaleShape/data/bot1.svg b/processing/mode/examples/Basics/Shape/ScaleShape/data/bot1.svg
new file mode 100644
index 000000000..3c56f2d60
--- /dev/null
+++ b/processing/mode/examples/Basics/Shape/ScaleShape/data/bot1.svg
@@ -0,0 +1,160 @@
+
+
+
+
+
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/Basics/Structure/Coordinates/Coordinates.pde b/processing/mode/examples/Basics/Structure/Coordinates/Coordinates.pde
similarity index 100%
rename from examples/Basics/Structure/Coordinates/Coordinates.pde
rename to processing/mode/examples/Basics/Structure/Coordinates/Coordinates.pde
diff --git a/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde b/processing/mode/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde
similarity index 100%
rename from examples/Basics/Structure/CreateGraphics/CreateGraphics.pde
rename to processing/mode/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde
diff --git a/examples/Basics/Structure/CreateGraphics/data/mask.jpg b/processing/mode/examples/Basics/Structure/CreateGraphics/data/mask.jpg
similarity index 100%
rename from examples/Basics/Structure/CreateGraphics/data/mask.jpg
rename to processing/mode/examples/Basics/Structure/CreateGraphics/data/mask.jpg
diff --git a/examples/Basics/Structure/CreateGraphics/data/test.jpg b/processing/mode/examples/Basics/Structure/CreateGraphics/data/test.jpg
similarity index 100%
rename from examples/Basics/Structure/CreateGraphics/data/test.jpg
rename to processing/mode/examples/Basics/Structure/CreateGraphics/data/test.jpg
diff --git a/examples/Basics/Structure/Functions/Functions.pde b/processing/mode/examples/Basics/Structure/Functions/Functions.pde
similarity index 100%
rename from examples/Basics/Structure/Functions/Functions.pde
rename to processing/mode/examples/Basics/Structure/Functions/Functions.pde
diff --git a/examples/Basics/Structure/Loop/Loop.pde b/processing/mode/examples/Basics/Structure/Loop/Loop.pde
similarity index 100%
rename from examples/Basics/Structure/Loop/Loop.pde
rename to processing/mode/examples/Basics/Structure/Loop/Loop.pde
diff --git a/examples/Basics/Structure/NoLoop/NoLoop.pde b/processing/mode/examples/Basics/Structure/NoLoop/NoLoop.pde
similarity index 100%
rename from examples/Basics/Structure/NoLoop/NoLoop.pde
rename to processing/mode/examples/Basics/Structure/NoLoop/NoLoop.pde
diff --git a/examples/Basics/Structure/Recursion/Recursion.pde b/processing/mode/examples/Basics/Structure/Recursion/Recursion.pde
similarity index 100%
rename from examples/Basics/Structure/Recursion/Recursion.pde
rename to processing/mode/examples/Basics/Structure/Recursion/Recursion.pde
diff --git a/examples/Basics/Structure/Recursion2/Recursion2.pde b/processing/mode/examples/Basics/Structure/Recursion2/Recursion2.pde
similarity index 100%
rename from examples/Basics/Structure/Recursion2/Recursion2.pde
rename to processing/mode/examples/Basics/Structure/Recursion2/Recursion2.pde
diff --git a/examples/Basics/Structure/Redraw/Redraw.pde b/processing/mode/examples/Basics/Structure/Redraw/Redraw.pde
similarity index 100%
rename from examples/Basics/Structure/Redraw/Redraw.pde
rename to processing/mode/examples/Basics/Structure/Redraw/Redraw.pde
diff --git a/examples/Basics/Structure/SetupDraw/SetupDraw.pde b/processing/mode/examples/Basics/Structure/SetupDraw/SetupDraw.pde
similarity index 100%
rename from examples/Basics/Structure/SetupDraw/SetupDraw.pde
rename to processing/mode/examples/Basics/Structure/SetupDraw/SetupDraw.pde
diff --git a/examples/Basics/Structure/StatementsComments/StatementsComments.pde b/processing/mode/examples/Basics/Structure/StatementsComments/StatementsComments.pde
similarity index 100%
rename from examples/Basics/Structure/StatementsComments/StatementsComments.pde
rename to processing/mode/examples/Basics/Structure/StatementsComments/StatementsComments.pde
diff --git a/examples/Basics/Structure/WidthHeight/WidthHeight.pde b/processing/mode/examples/Basics/Structure/WidthHeight/WidthHeight.pde
similarity index 100%
rename from examples/Basics/Structure/WidthHeight/WidthHeight.pde
rename to processing/mode/examples/Basics/Structure/WidthHeight/WidthHeight.pde
diff --git a/examples/Basics/Transform/Arm/Arm.pde b/processing/mode/examples/Basics/Transform/Arm/Arm.pde
similarity index 100%
rename from examples/Basics/Transform/Arm/Arm.pde
rename to processing/mode/examples/Basics/Transform/Arm/Arm.pde
diff --git a/examples/Basics/Transform/Rotate/Rotate.pde b/processing/mode/examples/Basics/Transform/Rotate/Rotate.pde
similarity index 100%
rename from examples/Basics/Transform/Rotate/Rotate.pde
rename to processing/mode/examples/Basics/Transform/Rotate/Rotate.pde
diff --git a/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde b/processing/mode/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde
similarity index 100%
rename from examples/Basics/Transform/RotatePushPop/RotatePushPop.pde
rename to processing/mode/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde
diff --git a/examples/Basics/Transform/RotateXY/RotateXY.pde b/processing/mode/examples/Basics/Transform/RotateXY/RotateXY.pde
similarity index 100%
rename from examples/Basics/Transform/RotateXY/RotateXY.pde
rename to processing/mode/examples/Basics/Transform/RotateXY/RotateXY.pde
diff --git a/examples/Basics/Transform/Scale/Scale.pde b/processing/mode/examples/Basics/Transform/Scale/Scale.pde
similarity index 100%
rename from examples/Basics/Transform/Scale/Scale.pde
rename to processing/mode/examples/Basics/Transform/Scale/Scale.pde
diff --git a/examples/Basics/Transform/Translate/Translate.pde b/processing/mode/examples/Basics/Transform/Translate/Translate.pde
similarity index 100%
rename from examples/Basics/Transform/Translate/Translate.pde
rename to processing/mode/examples/Basics/Transform/Translate/Translate.pde
diff --git a/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde b/processing/mode/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde
similarity index 100%
rename from examples/Basics/Transform/TriangleFlower/TriangleFlower.pde
rename to processing/mode/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde
diff --git a/examples/Basics/Typography/Letters/Letters.pde b/processing/mode/examples/Basics/Typography/Letters/Letters.pde
similarity index 100%
rename from examples/Basics/Typography/Letters/Letters.pde
rename to processing/mode/examples/Basics/Typography/Letters/Letters.pde
diff --git a/examples/Basics/Typography/Letters/data/CourierNew36.vlw b/processing/mode/examples/Basics/Typography/Letters/data/CourierNew36.vlw
similarity index 100%
rename from examples/Basics/Typography/Letters/data/CourierNew36.vlw
rename to processing/mode/examples/Basics/Typography/Letters/data/CourierNew36.vlw
diff --git a/examples/Basics/Typography/Words/Words.pde b/processing/mode/examples/Basics/Typography/Words/Words.pde
similarity index 100%
rename from examples/Basics/Typography/Words/Words.pde
rename to processing/mode/examples/Basics/Typography/Words/Words.pde
diff --git a/examples/Basics/Typography/Words/data/Ziggurat-HTF-Black-32.vlw b/processing/mode/examples/Basics/Typography/Words/data/Ziggurat-HTF-Black-32.vlw
similarity index 100%
rename from examples/Basics/Typography/Words/data/Ziggurat-HTF-Black-32.vlw
rename to processing/mode/examples/Basics/Typography/Words/data/Ziggurat-HTF-Black-32.vlw
diff --git a/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde b/processing/mode/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde
similarity index 100%
rename from examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde
rename to processing/mode/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde
diff --git a/examples/Basics/Web/LoadingImages/LoadingImages.pde b/processing/mode/examples/Basics/Web/LoadingImages/LoadingImages.pde
similarity index 100%
rename from examples/Basics/Web/LoadingImages/LoadingImages.pde
rename to processing/mode/examples/Basics/Web/LoadingImages/LoadingImages.pde
diff --git a/examples/Demos/Graphics/Particles/Particle.pde b/processing/mode/examples/Demos/Graphics/Particles/Particle.pde
similarity index 100%
rename from examples/Demos/Graphics/Particles/Particle.pde
rename to processing/mode/examples/Demos/Graphics/Particles/Particle.pde
diff --git a/examples/Demos/Graphics/Particles/ParticleSystem.pde b/processing/mode/examples/Demos/Graphics/Particles/ParticleSystem.pde
similarity index 100%
rename from examples/Demos/Graphics/Particles/ParticleSystem.pde
rename to processing/mode/examples/Demos/Graphics/Particles/ParticleSystem.pde
diff --git a/examples/Demos/Graphics/Particles/Particles.pde b/processing/mode/examples/Demos/Graphics/Particles/Particles.pde
similarity index 100%
rename from examples/Demos/Graphics/Particles/Particles.pde
rename to processing/mode/examples/Demos/Graphics/Particles/Particles.pde
diff --git a/examples/Demos/Graphics/Particles/data/sprite.png b/processing/mode/examples/Demos/Graphics/Particles/data/sprite.png
similarity index 100%
rename from examples/Demos/Graphics/Particles/data/sprite.png
rename to processing/mode/examples/Demos/Graphics/Particles/data/sprite.png
diff --git a/examples/Demos/Graphics/Patch/Patch.pde b/processing/mode/examples/Demos/Graphics/Patch/Patch.pde
similarity index 100%
rename from examples/Demos/Graphics/Patch/Patch.pde
rename to processing/mode/examples/Demos/Graphics/Patch/Patch.pde
diff --git a/examples/Demos/Graphics/Planets/Perlin.pde b/processing/mode/examples/Demos/Graphics/Planets/Perlin.pde
similarity index 100%
rename from examples/Demos/Graphics/Planets/Perlin.pde
rename to processing/mode/examples/Demos/Graphics/Planets/Perlin.pde
diff --git a/examples/Demos/Graphics/Planets/Planets.pde b/processing/mode/examples/Demos/Graphics/Planets/Planets.pde
similarity index 100%
rename from examples/Demos/Graphics/Planets/Planets.pde
rename to processing/mode/examples/Demos/Graphics/Planets/Planets.pde
diff --git a/examples/Demos/Graphics/Planets/data/mercury.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/mercury.jpg
similarity index 100%
rename from examples/Demos/Graphics/Planets/data/mercury.jpg
rename to processing/mode/examples/Demos/Graphics/Planets/data/mercury.jpg
diff --git a/examples/Demos/Graphics/Planets/data/planet.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/planet.jpg
similarity index 100%
rename from examples/Demos/Graphics/Planets/data/planet.jpg
rename to processing/mode/examples/Demos/Graphics/Planets/data/planet.jpg
diff --git a/examples/Demos/Graphics/Planets/data/starfield.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/starfield.jpg
similarity index 100%
rename from examples/Demos/Graphics/Planets/data/starfield.jpg
rename to processing/mode/examples/Demos/Graphics/Planets/data/starfield.jpg
diff --git a/examples/Demos/Graphics/Planets/data/sun.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/sun.jpg
similarity index 100%
rename from examples/Demos/Graphics/Planets/data/sun.jpg
rename to processing/mode/examples/Demos/Graphics/Planets/data/sun.jpg
diff --git a/examples/Demos/Graphics/Ribbons/ArcBall.pde b/processing/mode/examples/Demos/Graphics/Ribbons/ArcBall.pde
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/ArcBall.pde
rename to processing/mode/examples/Demos/Graphics/Ribbons/ArcBall.pde
diff --git a/examples/Demos/Graphics/Ribbons/BSpline.pde b/processing/mode/examples/Demos/Graphics/Ribbons/BSpline.pde
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/BSpline.pde
rename to processing/mode/examples/Demos/Graphics/Ribbons/BSpline.pde
diff --git a/examples/Demos/Graphics/Ribbons/Geometry.pde b/processing/mode/examples/Demos/Graphics/Ribbons/Geometry.pde
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/Geometry.pde
rename to processing/mode/examples/Demos/Graphics/Ribbons/Geometry.pde
diff --git a/examples/Demos/Graphics/Ribbons/PDB.pde b/processing/mode/examples/Demos/Graphics/Ribbons/PDB.pde
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/PDB.pde
rename to processing/mode/examples/Demos/Graphics/Ribbons/PDB.pde
diff --git a/examples/Demos/Graphics/Ribbons/Ribbons.pde b/processing/mode/examples/Demos/Graphics/Ribbons/Ribbons.pde
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/Ribbons.pde
rename to processing/mode/examples/Demos/Graphics/Ribbons/Ribbons.pde
diff --git a/examples/Demos/Graphics/Ribbons/data/1CBS.pdb b/processing/mode/examples/Demos/Graphics/Ribbons/data/1CBS.pdb
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/data/1CBS.pdb
rename to processing/mode/examples/Demos/Graphics/Ribbons/data/1CBS.pdb
diff --git a/examples/Demos/Graphics/Ribbons/data/2POR.pdb b/processing/mode/examples/Demos/Graphics/Ribbons/data/2POR.pdb
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/data/2POR.pdb
rename to processing/mode/examples/Demos/Graphics/Ribbons/data/2POR.pdb
diff --git a/examples/Demos/Graphics/Ribbons/data/4HHB.pdb b/processing/mode/examples/Demos/Graphics/Ribbons/data/4HHB.pdb
similarity index 100%
rename from examples/Demos/Graphics/Ribbons/data/4HHB.pdb
rename to processing/mode/examples/Demos/Graphics/Ribbons/data/4HHB.pdb
diff --git a/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde b/processing/mode/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde
similarity index 100%
rename from examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde
rename to processing/mode/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde
diff --git a/examples/Demos/Graphics/Trefoil/Surface.pde b/processing/mode/examples/Demos/Graphics/Trefoil/Surface.pde
similarity index 100%
rename from examples/Demos/Graphics/Trefoil/Surface.pde
rename to processing/mode/examples/Demos/Graphics/Trefoil/Surface.pde
diff --git a/examples/Demos/Graphics/Trefoil/Trefoil.pde b/processing/mode/examples/Demos/Graphics/Trefoil/Trefoil.pde
similarity index 100%
rename from examples/Demos/Graphics/Trefoil/Trefoil.pde
rename to processing/mode/examples/Demos/Graphics/Trefoil/Trefoil.pde
diff --git a/examples/Demos/Graphics/Trefoil/data/particle.png b/processing/mode/examples/Demos/Graphics/Trefoil/data/particle.png
similarity index 100%
rename from examples/Demos/Graphics/Trefoil/data/particle.png
rename to processing/mode/examples/Demos/Graphics/Trefoil/data/particle.png
diff --git a/examples/Demos/Graphics/Wiggling/Wiggling.pde b/processing/mode/examples/Demos/Graphics/Wiggling/Wiggling.pde
similarity index 100%
rename from examples/Demos/Graphics/Wiggling/Wiggling.pde
rename to processing/mode/examples/Demos/Graphics/Wiggling/Wiggling.pde
diff --git a/examples/Demos/Graphics/Yellowtail/Gesture.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Gesture.pde
similarity index 100%
rename from examples/Demos/Graphics/Yellowtail/Gesture.pde
rename to processing/mode/examples/Demos/Graphics/Yellowtail/Gesture.pde
diff --git a/examples/Demos/Graphics/Yellowtail/Polygon.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Polygon.pde
similarity index 100%
rename from examples/Demos/Graphics/Yellowtail/Polygon.pde
rename to processing/mode/examples/Demos/Graphics/Yellowtail/Polygon.pde
diff --git a/examples/Demos/Graphics/Yellowtail/Vec3f.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Vec3f.pde
similarity index 100%
rename from examples/Demos/Graphics/Yellowtail/Vec3f.pde
rename to processing/mode/examples/Demos/Graphics/Yellowtail/Vec3f.pde
diff --git a/examples/Demos/Graphics/Yellowtail/Yellowtail.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Yellowtail.pde
similarity index 100%
rename from examples/Demos/Graphics/Yellowtail/Yellowtail.pde
rename to processing/mode/examples/Demos/Graphics/Yellowtail/Yellowtail.pde
diff --git a/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde b/processing/mode/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde
similarity index 100%
rename from examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde
rename to processing/mode/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde
diff --git a/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde b/processing/mode/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde
similarity index 100%
rename from examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde
rename to processing/mode/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde
diff --git a/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde b/processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde
similarity index 100%
rename from examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde
rename to processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde
diff --git a/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png b/processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png
similarity index 100%
rename from examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png
rename to processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png
diff --git a/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde b/processing/mode/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde
similarity index 100%
rename from examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde
rename to processing/mode/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde
diff --git a/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png b/processing/mode/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png
similarity index 100%
rename from examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png
rename to processing/mode/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png
diff --git a/examples/Demos/Performance/Esfera/Esfera.pde b/processing/mode/examples/Demos/Performance/Esfera/Esfera.pde
similarity index 100%
rename from examples/Demos/Performance/Esfera/Esfera.pde
rename to processing/mode/examples/Demos/Performance/Esfera/Esfera.pde
diff --git a/examples/Demos/Performance/LineRendering/LineRendering.pde b/processing/mode/examples/Demos/Performance/LineRendering/LineRendering.pde
similarity index 100%
rename from examples/Demos/Performance/LineRendering/LineRendering.pde
rename to processing/mode/examples/Demos/Performance/LineRendering/LineRendering.pde
diff --git a/processing/mode/examples/Demos/Performance/P2DXDemo/P2DXDemo.pde b/processing/mode/examples/Demos/Performance/P2DXDemo/P2DXDemo.pde
new file mode 100644
index 000000000..c929f5a35
--- /dev/null
+++ b/processing/mode/examples/Demos/Performance/P2DXDemo/P2DXDemo.pde
@@ -0,0 +1,60 @@
+int join = MITER;
+int cap = SQUARE;
+
+boolean premultiply = true;
+
+float dev = 10; //deviation
+
+//change these parameters to benchmark various things
+int unit = 10;
+//line, triangle, rect, ellipse, point
+int[] amount = { 20, 15, 10, 5, 40 };
+
+void setup() {
+ fullScreen(P2DX);
+ strokeCap(cap);
+ strokeJoin(join);
+ PGraphics2DX.premultiplyMatrices = premultiply;
+
+ textFont(createFont("SansSerif", 15 * displayDensity));
+}
+
+public void draw() {
+ background(255);
+
+ strokeWeight(2 * displayDensity);
+ stroke(0);
+ fill(200);
+
+ for (int i = 0; i < amount[0]*unit; ++i) {
+ float x = random(width);
+ float y = random(height);
+ line(x, y, x + random(-dev, dev), y + random(-dev, dev));
+ }
+
+ for (int i = 0; i < amount[1]*unit; ++i) {
+ float x = random(width);
+ float y = random(height);
+ triangle(x, y,
+ x + random(-dev*2, dev*2), y + random(-dev*2, dev*2),
+ x + random(-dev*2, dev*2), y + random(-dev*2, dev*2));
+ }
+
+ for (int i = 0; i < amount[2]*unit; ++i) {
+ rect(random(width), random(height), random(dev), random(dev));
+ }
+
+ for (int i = 0; i < amount[3]*unit; ++i) {
+ ellipse(random(width), random(height), random(dev*2), random(dev*2));
+ }
+
+ for (int i = 0; i < amount[4]*unit; ++i) {
+ point(random(width), random(height));
+ }
+
+ //large ellipse to test smoothness of outline
+ ellipse(width/2, height/2, width/2, height/4);
+
+ fill(255, 0, 0);
+ text((int) frameRate + " fps", 30, 30);
+}
\ No newline at end of file
diff --git a/examples/Demos/Performance/QuadRendering/QuadRendering.pde b/processing/mode/examples/Demos/Performance/QuadRendering/QuadRendering.pde
similarity index 100%
rename from examples/Demos/Performance/QuadRendering/QuadRendering.pde
rename to processing/mode/examples/Demos/Performance/QuadRendering/QuadRendering.pde
diff --git a/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde b/processing/mode/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde
similarity index 100%
rename from examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde
rename to processing/mode/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde
diff --git a/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png b/processing/mode/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png
similarity index 100%
rename from examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png
rename to processing/mode/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png
diff --git a/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde b/processing/mode/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde
similarity index 100%
rename from examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde
rename to processing/mode/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde
diff --git a/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png b/processing/mode/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png
similarity index 100%
rename from examples/Demos/Performance/StaticParticlesRetained/data/sprite.png
rename to processing/mode/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png
diff --git a/examples/Demos/Performance/TextRendering/TextRendering.pde b/processing/mode/examples/Demos/Performance/TextRendering/TextRendering.pde
similarity index 100%
rename from examples/Demos/Performance/TextRendering/TextRendering.pde
rename to processing/mode/examples/Demos/Performance/TextRendering/TextRendering.pde
diff --git a/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde b/processing/mode/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde
similarity index 100%
rename from examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde
rename to processing/mode/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde
diff --git a/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde b/processing/mode/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde
similarity index 100%
rename from examples/Demos/Tests/OffscreenTest/OffscreenTest.pde
rename to processing/mode/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde
diff --git a/examples/Demos/Tests/RedrawTest/RedrawTest.pde b/processing/mode/examples/Demos/Tests/RedrawTest/RedrawTest.pde
similarity index 100%
rename from examples/Demos/Tests/RedrawTest/RedrawTest.pde
rename to processing/mode/examples/Demos/Tests/RedrawTest/RedrawTest.pde
diff --git a/examples/Sensors/Accelerometer/Accelerometer.pde b/processing/mode/examples/Sensors/Accelerometer/Accelerometer.pde
similarity index 99%
rename from examples/Sensors/Accelerometer/Accelerometer.pde
rename to processing/mode/examples/Sensors/Accelerometer/Accelerometer.pde
index e4524fc49..56e346d4a 100644
--- a/examples/Sensors/Accelerometer/Accelerometer.pde
+++ b/processing/mode/examples/Sensors/Accelerometer/Accelerometer.pde
@@ -46,4 +46,4 @@ public void accelerationEvent(float x, float y, float z) {
ay = y;
az = z;
redraw();
-}
+}
\ No newline at end of file
diff --git a/examples/Sensors/Accelerometer/AccelerometerManager.java b/processing/mode/examples/Sensors/Accelerometer/AccelerometerManager.java
similarity index 93%
rename from examples/Sensors/Accelerometer/AccelerometerManager.java
rename to processing/mode/examples/Sensors/Accelerometer/AccelerometerManager.java
index b4340fc2b..1952ea8c5 100644
--- a/examples/Sensors/Accelerometer/AccelerometerManager.java
+++ b/processing/mode/examples/Sensors/Accelerometer/AccelerometerManager.java
@@ -1,3 +1,5 @@
+import processing.core.PApplet;
+
import java.lang.reflect.*;
import java.util.List;
@@ -32,11 +34,12 @@ public class AccelerometerManager {
/** indicates whether or not Accelerometer Sensor is running */
private boolean running = false;
- Context context;
-
+ PApplet parent;
+ Context context;
- public AccelerometerManager(Context parent) {
- this.context = parent;
+ public AccelerometerManager(PApplet parent) {
+ this.parent = parent;
+ this.context = parent.getActivity();
try {
shakeEventMethod =
@@ -57,8 +60,8 @@ public AccelerometerManager(Context parent) {
}
- public AccelerometerManager(Context context, int threshold, int interval) {
- this(context);
+ public AccelerometerManager(PApplet parent, int threshold, int interval) {
+ this(parent);
this.threshold = threshold;
this.interval = interval;
}
@@ -211,7 +214,7 @@ public void onSensorChanged(SensorEvent event) {
// listener.onShake(force);
if (shakeEventMethod != null) {
try {
- shakeEventMethod.invoke(context, new Object[] { new Float(force) });
+ shakeEventMethod.invoke(parent, new Object[] { new Float(force) });
} catch (Exception e) {
e.printStackTrace();
shakeEventMethod = null;
@@ -230,7 +233,7 @@ public void onSensorChanged(SensorEvent event) {
// listener.onAccelerationChanged(x, y, z);
if (accelerationEventMethod != null) {
try {
- accelerationEventMethod.invoke(context, new Object[] { x, y, z });
+ accelerationEventMethod.invoke(parent, new Object[] { x, y, z });
} catch (Exception e) {
e.printStackTrace();
accelerationEventMethod = null;
@@ -238,5 +241,4 @@ public void onSensorChanged(SensorEvent event) {
}
}
};
-}
-
+}
\ No newline at end of file
diff --git a/examples/Sensors/Compass/Compass.pde b/processing/mode/examples/Sensors/Compass/Compass.pde
similarity index 100%
rename from examples/Sensors/Compass/Compass.pde
rename to processing/mode/examples/Sensors/Compass/Compass.pde
diff --git a/examples/Sensors/Compass/CompassManager.java b/processing/mode/examples/Sensors/Compass/CompassManager.java
similarity index 90%
rename from examples/Sensors/Compass/CompassManager.java
rename to processing/mode/examples/Sensors/Compass/CompassManager.java
index c45238f8d..f3cd99d71 100644
--- a/examples/Sensors/Compass/CompassManager.java
+++ b/processing/mode/examples/Sensors/Compass/CompassManager.java
@@ -1,3 +1,5 @@
+import processing.core.PApplet;
+
import java.lang.reflect.*;
import java.util.List;
@@ -18,11 +20,12 @@ public class CompassManager {
private Boolean supported;
private boolean running = false;
+ PApplet parent;
Context context;
-
-
- public CompassManager(Context parent) {
- this.context = parent;
+
+ public CompassManager(PApplet parent) {
+ this.parent = parent;
+ this.context = parent.getActivity();
try {
compassEventMethod =
@@ -118,7 +121,7 @@ public void onSensorChanged(SensorEvent event) {
if (compassEventMethod != null) {
try {
- compassEventMethod.invoke(context, new Object[] { x, y, z });
+ compassEventMethod.invoke(parent, new Object[] { x, y, z });
} catch (Exception e) {
e.printStackTrace();
compassEventMethod = null;
@@ -127,7 +130,7 @@ public void onSensorChanged(SensorEvent event) {
if (directionEventMethod != null) {
try {
- directionEventMethod.invoke(context, new Object[] { (float) (-x * Math.PI / 180) });
+ directionEventMethod.invoke(parent, new Object[] { (float) (-x * Math.PI / 180) });
} catch (Exception e) {
e.printStackTrace();
directionEventMethod = null;
@@ -135,5 +138,4 @@ public void onSensorChanged(SensorEvent event) {
}
}
};
-}
-
+}
\ No newline at end of file
diff --git a/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde b/processing/mode/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde
similarity index 100%
rename from examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde
rename to processing/mode/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde
diff --git a/examples/Topics/Advanced Data/ArrayListClass/Ball.pde b/processing/mode/examples/Topics/Advanced Data/ArrayListClass/Ball.pde
similarity index 100%
rename from examples/Topics/Advanced Data/ArrayListClass/Ball.pde
rename to processing/mode/examples/Topics/Advanced Data/ArrayListClass/Ball.pde
diff --git a/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde b/processing/mode/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde
similarity index 100%
rename from examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde
rename to processing/mode/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde
diff --git a/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde b/processing/mode/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde
similarity index 100%
rename from examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde
rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde
diff --git a/examples/Topics/Advanced Data/HashMapClass/Word.pde b/processing/mode/examples/Topics/Advanced Data/HashMapClass/Word.pde
similarity index 100%
rename from examples/Topics/Advanced Data/HashMapClass/Word.pde
rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/Word.pde
diff --git a/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt b/processing/mode/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt
similarity index 100%
rename from examples/Topics/Advanced Data/HashMapClass/data/dracula.txt
rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt
diff --git a/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt b/processing/mode/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt
similarity index 100%
rename from examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt
rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt
diff --git a/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde b/processing/mode/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde
diff --git a/examples/Topics/Animation/AnimatedSprite/Animation.pde b/processing/mode/examples/Topics/Animation/AnimatedSprite/Animation.pde
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/Animation.pde
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/Animation.pde
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0000.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0000.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0000.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0000.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0001.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0001.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0001.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0001.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0002.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0002.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0002.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0002.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0003.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0003.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0003.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0003.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0004.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0004.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0004.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0004.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0005.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0005.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0005.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0005.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0006.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0006.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0006.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0006.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0007.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0007.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0007.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0007.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0008.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0008.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0008.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0008.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0009.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0009.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0009.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0009.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0010.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0010.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0010.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0010.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0011.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0011.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0011.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0011.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0012.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0012.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0012.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0012.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0013.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0013.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0013.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0013.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0014.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0014.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0014.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0014.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0015.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0015.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0015.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0015.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0016.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0016.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0016.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0016.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0017.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0017.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0017.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0017.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0018.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0018.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0018.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0018.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0019.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0019.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0019.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0019.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0020.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0020.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0020.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0020.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0021.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0021.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0021.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0021.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0022.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0022.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0022.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0022.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0023.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0023.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0023.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0023.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0024.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0024.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0024.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0024.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0025.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0025.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0025.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0025.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0026.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0026.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0026.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0026.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0027.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0027.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0027.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0027.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0028.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0028.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0028.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0028.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0029.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0029.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0029.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0029.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0030.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0030.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0030.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0030.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0031.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0031.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0031.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0031.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0032.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0032.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0032.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0032.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0033.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0033.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0033.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0033.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0034.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0034.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0034.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0034.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0035.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0035.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0035.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0035.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0036.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0036.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0036.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0036.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0037.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0037.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0037.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Shifty_0037.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0000.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0000.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0000.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0000.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0001.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0001.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0001.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0001.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0002.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0002.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0002.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0002.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0003.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0003.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0003.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0003.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0004.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0004.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0004.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0004.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0005.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0005.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0005.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0005.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0006.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0006.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0006.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0006.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0007.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0007.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0007.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0007.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0008.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0008.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0008.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0008.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0009.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0009.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0009.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0009.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0010.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0010.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0010.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0010.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0011.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0011.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0011.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0011.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0012.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0012.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0012.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0012.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0013.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0013.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0013.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0013.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0014.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0014.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0014.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0014.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0015.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0015.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0015.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0015.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0016.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0016.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0016.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0016.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0017.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0017.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0017.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0017.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0018.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0018.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0018.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0018.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0019.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0019.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0019.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0019.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0020.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0020.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0020.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0020.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0021.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0021.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0021.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0021.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0022.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0022.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0022.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0022.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0023.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0023.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0023.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0023.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0024.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0024.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0024.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0024.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0025.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0025.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0025.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0025.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0026.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0026.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0026.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0026.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0027.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0027.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0027.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0027.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0028.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0028.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0028.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0028.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0029.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0029.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0029.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0029.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0030.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0030.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0030.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0030.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0031.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0031.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0031.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0031.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0032.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0032.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0032.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0032.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0033.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0033.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0033.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0033.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0034.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0034.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0034.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0034.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0035.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0035.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0035.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0035.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0036.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0036.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0036.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0036.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0037.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0037.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0037.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0037.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0038.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0038.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0038.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0038.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0039.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0039.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0039.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0039.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0040.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0040.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0040.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0040.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0041.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0041.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0041.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0041.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0042.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0042.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0042.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0042.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0043.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0043.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0043.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0043.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0044.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0044.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0044.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0044.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0045.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0045.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0045.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0045.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0046.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0046.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0046.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0046.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0047.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0047.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0047.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0047.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0048.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0048.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0048.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0048.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0049.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0049.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0049.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0049.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0050.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0050.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0050.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0050.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0051.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0051.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0051.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0051.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0052.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0052.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0052.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0052.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0053.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0053.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0053.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0053.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0054.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0054.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0054.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0054.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0055.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0055.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0055.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0055.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0056.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0056.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0056.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0056.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0057.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0057.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0057.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0057.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0058.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0058.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0058.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0058.gif
diff --git a/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0059.gif b/processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0059.gif
similarity index 100%
rename from examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0059.gif
rename to processing/mode/examples/Topics/Animation/AnimatedSprite/data/PT_Teddy_0059.gif
diff --git a/examples/Topics/Animation/Sequential/Sequential.pde b/processing/mode/examples/Topics/Animation/Sequential/Sequential.pde
similarity index 100%
rename from examples/Topics/Animation/Sequential/Sequential.pde
rename to processing/mode/examples/Topics/Animation/Sequential/Sequential.pde
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0000.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0000.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0000.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0000.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0001.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0001.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0001.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0001.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0002.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0002.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0002.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0002.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0003.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0003.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0003.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0003.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0004.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0004.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0004.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0004.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0005.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0005.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0005.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0005.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0006.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0006.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0006.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0006.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0007.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0007.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0007.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0007.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0008.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0008.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0008.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0008.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0009.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0009.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0009.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0009.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0010.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0010.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0010.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0010.gif
diff --git a/examples/Topics/Animation/Sequential/data/PT_anim0011.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0011.gif
similarity index 100%
rename from examples/Topics/Animation/Sequential/data/PT_anim0011.gif
rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0011.gif
diff --git a/examples/Topics/Cellular Automata/Conway/Conway.pde b/processing/mode/examples/Topics/Cellular Automata/Conway/Conway.pde
similarity index 100%
rename from examples/Topics/Cellular Automata/Conway/Conway.pde
rename to processing/mode/examples/Topics/Cellular Automata/Conway/Conway.pde
diff --git a/examples/Topics/Cellular Automata/Spore1/Spore1.pde b/processing/mode/examples/Topics/Cellular Automata/Spore1/Spore1.pde
similarity index 100%
rename from examples/Topics/Cellular Automata/Spore1/Spore1.pde
rename to processing/mode/examples/Topics/Cellular Automata/Spore1/Spore1.pde
diff --git a/examples/Topics/Cellular Automata/Spore2/Spore2.pde b/processing/mode/examples/Topics/Cellular Automata/Spore2/Spore2.pde
similarity index 100%
rename from examples/Topics/Cellular Automata/Spore2/Spore2.pde
rename to processing/mode/examples/Topics/Cellular Automata/Spore2/Spore2.pde
diff --git a/examples/Topics/Cellular Automata/Wolfram/CA.pde b/processing/mode/examples/Topics/Cellular Automata/Wolfram/CA.pde
similarity index 100%
rename from examples/Topics/Cellular Automata/Wolfram/CA.pde
rename to processing/mode/examples/Topics/Cellular Automata/Wolfram/CA.pde
diff --git a/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde b/processing/mode/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde
similarity index 100%
rename from examples/Topics/Cellular Automata/Wolfram/Wolfram.pde
rename to processing/mode/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde
diff --git a/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde b/processing/mode/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde
similarity index 100%
rename from examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde
rename to processing/mode/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde
diff --git a/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde b/processing/mode/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde
similarity index 100%
rename from examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde
rename to processing/mode/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde
diff --git a/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde
similarity index 100%
rename from examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde
rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde
diff --git a/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde
similarity index 100%
rename from examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde
rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde
diff --git a/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde
similarity index 100%
rename from examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde
rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde
diff --git a/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png
similarity index 100%
rename from examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png
rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png
diff --git a/examples/Topics/Create Shapes/PathPShape/PathPShape.pde b/processing/mode/examples/Topics/Create Shapes/PathPShape/PathPShape.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PathPShape/PathPShape.pde
rename to processing/mode/examples/Topics/Create Shapes/PathPShape/PathPShape.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde
diff --git a/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde
rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde
diff --git a/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde b/processing/mode/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde
similarity index 100%
rename from examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde
rename to processing/mode/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde
diff --git a/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde b/processing/mode/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde
similarity index 100%
rename from examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde
rename to processing/mode/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde
diff --git a/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde b/processing/mode/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde
similarity index 100%
rename from examples/Topics/Create Shapes/WigglePShape/Wiggler.pde
rename to processing/mode/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde
diff --git a/examples/Topics/Drawing/Animator/Animator.pde b/processing/mode/examples/Topics/Drawing/Animator/Animator.pde
similarity index 100%
rename from examples/Topics/Drawing/Animator/Animator.pde
rename to processing/mode/examples/Topics/Drawing/Animator/Animator.pde
diff --git a/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde b/processing/mode/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde
similarity index 100%
rename from examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde
rename to processing/mode/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde
diff --git a/examples/Topics/Drawing/CustomTool/CustomTool.pde b/processing/mode/examples/Topics/Drawing/CustomTool/CustomTool.pde
similarity index 100%
rename from examples/Topics/Drawing/CustomTool/CustomTool.pde
rename to processing/mode/examples/Topics/Drawing/CustomTool/CustomTool.pde
diff --git a/examples/Topics/Drawing/CustomTool/data/milan.jpg b/processing/mode/examples/Topics/Drawing/CustomTool/data/milan.jpg
similarity index 100%
rename from examples/Topics/Drawing/CustomTool/data/milan.jpg
rename to processing/mode/examples/Topics/Drawing/CustomTool/data/milan.jpg
diff --git a/examples/Topics/Drawing/CustomTool/data/paris.jpg b/processing/mode/examples/Topics/Drawing/CustomTool/data/paris.jpg
similarity index 100%
rename from examples/Topics/Drawing/CustomTool/data/paris.jpg
rename to processing/mode/examples/Topics/Drawing/CustomTool/data/paris.jpg
diff --git a/examples/Topics/Drawing/Pattern/Pattern.pde b/processing/mode/examples/Topics/Drawing/Pattern/Pattern.pde
similarity index 100%
rename from examples/Topics/Drawing/Pattern/Pattern.pde
rename to processing/mode/examples/Topics/Drawing/Pattern/Pattern.pde
diff --git a/examples/Topics/Drawing/Pulses/Pulses.pde b/processing/mode/examples/Topics/Drawing/Pulses/Pulses.pde
similarity index 100%
rename from examples/Topics/Drawing/Pulses/Pulses.pde
rename to processing/mode/examples/Topics/Drawing/Pulses/Pulses.pde
diff --git a/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde b/processing/mode/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde
similarity index 100%
rename from examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde
rename to processing/mode/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde
diff --git a/examples/Topics/Effects/FireCube/FireCube.pde b/processing/mode/examples/Topics/Effects/FireCube/FireCube.pde
similarity index 99%
rename from examples/Topics/Effects/FireCube/FireCube.pde
rename to processing/mode/examples/Topics/Effects/FireCube/FireCube.pde
index 7c7247fc0..2565eb3bc 100644
--- a/examples/Topics/Effects/FireCube/FireCube.pde
+++ b/processing/mode/examples/Topics/Effects/FireCube/FireCube.pde
@@ -28,6 +28,7 @@ void setup(){
// Create buffered image for 3d cube
pg = createGraphics(width, height, P3D);
+ pg.loadPixels();
calc1 = new int[width];
calc3 = new int[width];
diff --git a/examples/Topics/Effects/Lens/Lens.pde b/processing/mode/examples/Topics/Effects/Lens/Lens.pde
similarity index 97%
rename from examples/Topics/Effects/Lens/Lens.pde
rename to processing/mode/examples/Topics/Effects/Lens/Lens.pde
index 804ff14fd..b9b63287b 100644
--- a/examples/Topics/Effects/Lens/Lens.pde
+++ b/processing/mode/examples/Topics/Effects/Lens/Lens.pde
@@ -28,7 +28,7 @@ int dy = 1;
void setup() {
- size(640, 360);
+ size(640, 360, P2D);
// Create buffered image for lens effect
lensEffect = createGraphics(width, height, P2D);
@@ -78,14 +78,13 @@ void draw() {
xx += dx;
yy += dy;
- lensImage = createGraphics(lensD, lensD, P2D);
-
// save the backgrounlensD of lensHeight*lensWilensDth pixels rectangle at the coorlensDinates
// where the lens effect will be applielensD.
lensImage2.copy(lensEffect, xx, yy, lensD, lensD, 0, 0, lensD, lensD);
// output into a bufferelensD image for reuse
lensImage.loadPixels();
+ lensImage2.loadPixels();
// For each pixel in the destination rectangle, apply the color
// from the appropriate pixel in the saved background. The lensArray
diff --git a/examples/Topics/Effects/Lens/data/red_smoke.jpg b/processing/mode/examples/Topics/Effects/Lens/data/red_smoke.jpg
similarity index 100%
rename from examples/Topics/Effects/Lens/data/red_smoke.jpg
rename to processing/mode/examples/Topics/Effects/Lens/data/red_smoke.jpg
diff --git a/examples/Topics/Effects/Metaball/Metaball.pde b/processing/mode/examples/Topics/Effects/Metaball/Metaball.pde
similarity index 98%
rename from examples/Topics/Effects/Metaball/Metaball.pde
rename to processing/mode/examples/Topics/Effects/Metaball/Metaball.pde
index cde5d1c4a..bf21121f1 100644
--- a/examples/Topics/Effects/Metaball/Metaball.pde
+++ b/processing/mode/examples/Topics/Effects/Metaball/Metaball.pde
@@ -20,7 +20,7 @@ PGraphics pg;
int[][] vy,vx;
void setup() {
- size(640, 360);
+ size(640, 360, P2D);
pg = createGraphics(160, 90, P2D);
vy = new int[numBlobs][pg.height];
vx = new int[numBlobs][pg.width];
diff --git a/examples/Topics/Effects/Plasma/Plasma.pde b/processing/mode/examples/Topics/Effects/Plasma/Plasma.pde
similarity index 98%
rename from examples/Topics/Effects/Plasma/Plasma.pde
rename to processing/mode/examples/Topics/Effects/Plasma/Plasma.pde
index 04b20d84d..17162ba8c 100644
--- a/examples/Topics/Effects/Plasma/Plasma.pde
+++ b/processing/mode/examples/Topics/Effects/Plasma/Plasma.pde
@@ -11,7 +11,7 @@ int pixelSize=2;
PGraphics pg;
void setup(){
- size(640, 360);
+ size(640, 360, P2D);
// Create buffered image for plasma effect
pg = createGraphics(160, 90, P2D);
colorMode(HSB);
diff --git a/examples/Topics/Effects/Tunnel/Tunnel.pde b/processing/mode/examples/Topics/Effects/Tunnel/Tunnel.pde
similarity index 99%
rename from examples/Topics/Effects/Tunnel/Tunnel.pde
rename to processing/mode/examples/Topics/Effects/Tunnel/Tunnel.pde
index 5e98f3e47..c80eda08f 100644
--- a/examples/Topics/Effects/Tunnel/Tunnel.pde
+++ b/processing/mode/examples/Topics/Effects/Tunnel/Tunnel.pde
@@ -24,7 +24,7 @@ int[][] shadeTable;
int w, h;
void setup() {
- size(640, 360);
+ size(640, 360, P2D);
// Load texture 512 x 512
textureImg = loadImage("red_smoke.jpg");
diff --git a/examples/Topics/Effects/Tunnel/data/red_smoke.jpg b/processing/mode/examples/Topics/Effects/Tunnel/data/red_smoke.jpg
similarity index 100%
rename from examples/Topics/Effects/Tunnel/data/red_smoke.jpg
rename to processing/mode/examples/Topics/Effects/Tunnel/data/red_smoke.jpg
diff --git a/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde b/processing/mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde
similarity index 95%
rename from examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde
rename to processing/mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde
index 6395eeaa1..e027e1e99 100644
--- a/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde
+++ b/processing/mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde
@@ -27,7 +27,7 @@ void setup() {
// Create blank surfaces to draw on
for (int i = 0; i < spriteFrames.length; i++) {
- spriteFrames[i] = createGraphics(width, height, JAVA2D);
+ spriteFrames[i] = createGraphics(width, height);
}
}
diff --git a/examples/Topics/Effects/UnlimitedSprites/data/Aqua-Ball-48x48.png b/processing/mode/examples/Topics/Effects/UnlimitedSprites/data/Aqua-Ball-48x48.png
similarity index 100%
rename from examples/Topics/Effects/UnlimitedSprites/data/Aqua-Ball-48x48.png
rename to processing/mode/examples/Topics/Effects/UnlimitedSprites/data/Aqua-Ball-48x48.png
diff --git a/examples/Topics/Effects/Wormhole/Wormhole.pde b/processing/mode/examples/Topics/Effects/Wormhole/Wormhole.pde
similarity index 100%
rename from examples/Topics/Effects/Wormhole/Wormhole.pde
rename to processing/mode/examples/Topics/Effects/Wormhole/Wormhole.pde
diff --git a/examples/Topics/Effects/Wormhole/data/texture.gif b/processing/mode/examples/Topics/Effects/Wormhole/data/texture.gif
similarity index 100%
rename from examples/Topics/Effects/Wormhole/data/texture.gif
rename to processing/mode/examples/Topics/Effects/Wormhole/data/texture.gif
diff --git a/examples/Topics/Effects/Wormhole/data/wormhole.png b/processing/mode/examples/Topics/Effects/Wormhole/data/wormhole.png
similarity index 100%
rename from examples/Topics/Effects/Wormhole/data/wormhole.png
rename to processing/mode/examples/Topics/Effects/Wormhole/data/wormhole.png
diff --git a/examples/Topics/File IO/LoadFile1/LoadFile1.pde b/processing/mode/examples/Topics/File IO/LoadFile1/LoadFile1.pde
similarity index 100%
rename from examples/Topics/File IO/LoadFile1/LoadFile1.pde
rename to processing/mode/examples/Topics/File IO/LoadFile1/LoadFile1.pde
diff --git a/examples/Topics/File IO/LoadFile1/data/positions.txt b/processing/mode/examples/Topics/File IO/LoadFile1/data/positions.txt
similarity index 100%
rename from examples/Topics/File IO/LoadFile1/data/positions.txt
rename to processing/mode/examples/Topics/File IO/LoadFile1/data/positions.txt
diff --git a/examples/Topics/File IO/LoadFile2/LoadFile2.pde b/processing/mode/examples/Topics/File IO/LoadFile2/LoadFile2.pde
similarity index 100%
rename from examples/Topics/File IO/LoadFile2/LoadFile2.pde
rename to processing/mode/examples/Topics/File IO/LoadFile2/LoadFile2.pde
diff --git a/examples/Topics/File IO/LoadFile2/Record.pde b/processing/mode/examples/Topics/File IO/LoadFile2/Record.pde
similarity index 100%
rename from examples/Topics/File IO/LoadFile2/Record.pde
rename to processing/mode/examples/Topics/File IO/LoadFile2/Record.pde
diff --git a/examples/Topics/File IO/LoadFile2/data/TheSans-Plain-12.vlw b/processing/mode/examples/Topics/File IO/LoadFile2/data/TheSans-Plain-12.vlw
similarity index 100%
rename from examples/Topics/File IO/LoadFile2/data/TheSans-Plain-12.vlw
rename to processing/mode/examples/Topics/File IO/LoadFile2/data/TheSans-Plain-12.vlw
diff --git a/examples/Topics/File IO/LoadFile2/data/cars2.tsv b/processing/mode/examples/Topics/File IO/LoadFile2/data/cars2.tsv
similarity index 100%
rename from examples/Topics/File IO/LoadFile2/data/cars2.tsv
rename to processing/mode/examples/Topics/File IO/LoadFile2/data/cars2.tsv
diff --git a/examples/Topics/File IO/SaveFile1/SaveFile1.pde b/processing/mode/examples/Topics/File IO/SaveFile1/SaveFile1.pde
similarity index 100%
rename from examples/Topics/File IO/SaveFile1/SaveFile1.pde
rename to processing/mode/examples/Topics/File IO/SaveFile1/SaveFile1.pde
diff --git a/examples/Topics/File IO/SaveFile2/SaveFile2.pde b/processing/mode/examples/Topics/File IO/SaveFile2/SaveFile2.pde
similarity index 100%
rename from examples/Topics/File IO/SaveFile2/SaveFile2.pde
rename to processing/mode/examples/Topics/File IO/SaveFile2/SaveFile2.pde
diff --git a/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde b/processing/mode/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde
similarity index 100%
rename from examples/Topics/File IO/SaveManyImages/SaveManyImages.pde
rename to processing/mode/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde
diff --git a/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde b/processing/mode/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde
similarity index 100%
rename from examples/Topics/File IO/SaveOneImage/SaveOneImage.pde
rename to processing/mode/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde
diff --git a/examples/Topics/File IO/TileImages/TileImages.pde b/processing/mode/examples/Topics/File IO/TileImages/TileImages.pde
similarity index 100%
rename from examples/Topics/File IO/TileImages/TileImages.pde
rename to processing/mode/examples/Topics/File IO/TileImages/TileImages.pde
diff --git a/examples/Topics/Fractals and L-Systems/Koch/Koch.pde b/processing/mode/examples/Topics/Fractals and L-Systems/Koch/Koch.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/Koch/Koch.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/Koch/Koch.pde
diff --git a/examples/Topics/Fractals and L-Systems/Mandelbrot/Mandelbrot.pde b/processing/mode/examples/Topics/Fractals and L-Systems/Mandelbrot/Mandelbrot.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/Mandelbrot/Mandelbrot.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/Mandelbrot/Mandelbrot.pde
diff --git a/examples/Topics/Fractals and L-Systems/PenroseSnowflake/LSystem.pde b/processing/mode/examples/Topics/Fractals and L-Systems/PenroseSnowflake/LSystem.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/PenroseSnowflake/LSystem.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/PenroseSnowflake/LSystem.pde
diff --git a/examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflake.pde b/processing/mode/examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflake.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflake.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflake.pde
diff --git a/examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflakeLSystem.pde b/processing/mode/examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflakeLSystem.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflakeLSystem.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/PenroseSnowflake/PenroseSnowflakeLSystem.pde
diff --git a/examples/Topics/Fractals and L-Systems/PenroseTile/LSystem.pde b/processing/mode/examples/Topics/Fractals and L-Systems/PenroseTile/LSystem.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/PenroseTile/LSystem.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/PenroseTile/LSystem.pde
diff --git a/examples/Topics/Fractals and L-Systems/PenroseTile/PenroseLSystem.pde b/processing/mode/examples/Topics/Fractals and L-Systems/PenroseTile/PenroseLSystem.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/PenroseTile/PenroseLSystem.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/PenroseTile/PenroseLSystem.pde
diff --git a/examples/Topics/Fractals and L-Systems/PenroseTile/PenroseTile.pde b/processing/mode/examples/Topics/Fractals and L-Systems/PenroseTile/PenroseTile.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/PenroseTile/PenroseTile.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/PenroseTile/PenroseTile.pde
diff --git a/examples/Topics/Fractals and L-Systems/Pentigree/LSystem.pde b/processing/mode/examples/Topics/Fractals and L-Systems/Pentigree/LSystem.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/Pentigree/LSystem.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/Pentigree/LSystem.pde
diff --git a/examples/Topics/Fractals and L-Systems/Pentigree/Pentigree.pde b/processing/mode/examples/Topics/Fractals and L-Systems/Pentigree/Pentigree.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/Pentigree/Pentigree.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/Pentigree/Pentigree.pde
diff --git a/examples/Topics/Fractals and L-Systems/Pentigree/PentigreeLSystem.pde b/processing/mode/examples/Topics/Fractals and L-Systems/Pentigree/PentigreeLSystem.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/Pentigree/PentigreeLSystem.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/Pentigree/PentigreeLSystem.pde
diff --git a/examples/Topics/Fractals and L-Systems/Tree/Tree.pde b/processing/mode/examples/Topics/Fractals and L-Systems/Tree/Tree.pde
similarity index 100%
rename from examples/Topics/Fractals and L-Systems/Tree/Tree.pde
rename to processing/mode/examples/Topics/Fractals and L-Systems/Tree/Tree.pde
diff --git a/examples/Topics/GUI/Button/Button.pde b/processing/mode/examples/Topics/GUI/Button/Button.pde
similarity index 100%
rename from examples/Topics/GUI/Button/Button.pde
rename to processing/mode/examples/Topics/GUI/Button/Button.pde
diff --git a/examples/Topics/GUI/Buttons/Buttons.pde b/processing/mode/examples/Topics/GUI/Buttons/Buttons.pde
similarity index 100%
rename from examples/Topics/GUI/Buttons/Buttons.pde
rename to processing/mode/examples/Topics/GUI/Buttons/Buttons.pde
diff --git a/examples/Topics/GUI/Handles/Handles.pde b/processing/mode/examples/Topics/GUI/Handles/Handles.pde
similarity index 100%
rename from examples/Topics/GUI/Handles/Handles.pde
rename to processing/mode/examples/Topics/GUI/Handles/Handles.pde
diff --git a/examples/Topics/GUI/ImageButton/ImageButton.pde b/processing/mode/examples/Topics/GUI/ImageButton/ImageButton.pde
similarity index 100%
rename from examples/Topics/GUI/ImageButton/ImageButton.pde
rename to processing/mode/examples/Topics/GUI/ImageButton/ImageButton.pde
diff --git a/examples/Topics/GUI/ImageButton/data/base.gif b/processing/mode/examples/Topics/GUI/ImageButton/data/base.gif
similarity index 100%
rename from examples/Topics/GUI/ImageButton/data/base.gif
rename to processing/mode/examples/Topics/GUI/ImageButton/data/base.gif
diff --git a/examples/Topics/GUI/ImageButton/data/down.gif b/processing/mode/examples/Topics/GUI/ImageButton/data/down.gif
similarity index 100%
rename from examples/Topics/GUI/ImageButton/data/down.gif
rename to processing/mode/examples/Topics/GUI/ImageButton/data/down.gif
diff --git a/examples/Topics/GUI/ImageButton/data/roll.gif b/processing/mode/examples/Topics/GUI/ImageButton/data/roll.gif
similarity index 100%
rename from examples/Topics/GUI/ImageButton/data/roll.gif
rename to processing/mode/examples/Topics/GUI/ImageButton/data/roll.gif
diff --git a/examples/Topics/GUI/Rollover/Rollover.pde b/processing/mode/examples/Topics/GUI/Rollover/Rollover.pde
similarity index 100%
rename from examples/Topics/GUI/Rollover/Rollover.pde
rename to processing/mode/examples/Topics/GUI/Rollover/Rollover.pde
diff --git a/examples/Topics/GUI/Scrollbar/Scrollbar.pde b/processing/mode/examples/Topics/GUI/Scrollbar/Scrollbar.pde
similarity index 100%
rename from examples/Topics/GUI/Scrollbar/Scrollbar.pde
rename to processing/mode/examples/Topics/GUI/Scrollbar/Scrollbar.pde
diff --git a/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg b/processing/mode/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg
similarity index 100%
rename from examples/Topics/GUI/Scrollbar/data/seedBottom.jpg
rename to processing/mode/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg
diff --git a/examples/Topics/GUI/Scrollbar/data/seedTop.jpg b/processing/mode/examples/Topics/GUI/Scrollbar/data/seedTop.jpg
similarity index 100%
rename from examples/Topics/GUI/Scrollbar/data/seedTop.jpg
rename to processing/mode/examples/Topics/GUI/Scrollbar/data/seedTop.jpg
diff --git a/examples/Topics/Geometry/Icosahedra/Dimension3D.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Dimension3D.pde
similarity index 100%
rename from examples/Topics/Geometry/Icosahedra/Dimension3D.pde
rename to processing/mode/examples/Topics/Geometry/Icosahedra/Dimension3D.pde
diff --git a/examples/Topics/Geometry/Icosahedra/Icosahedra.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedra.pde
similarity index 100%
rename from examples/Topics/Geometry/Icosahedra/Icosahedra.pde
rename to processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedra.pde
diff --git a/examples/Topics/Geometry/Icosahedra/Icosahedron.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedron.pde
similarity index 100%
rename from examples/Topics/Geometry/Icosahedra/Icosahedron.pde
rename to processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedron.pde
diff --git a/examples/Topics/Geometry/Icosahedra/Shape3D.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Shape3D.pde
similarity index 100%
rename from examples/Topics/Geometry/Icosahedra/Shape3D.pde
rename to processing/mode/examples/Topics/Geometry/Icosahedra/Shape3D.pde
diff --git a/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde b/processing/mode/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde
similarity index 100%
rename from examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde
rename to processing/mode/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde
diff --git a/examples/Topics/Geometry/RGBCube/RGBCube.pde b/processing/mode/examples/Topics/Geometry/RGBCube/RGBCube.pde
similarity index 100%
rename from examples/Topics/Geometry/RGBCube/RGBCube.pde
rename to processing/mode/examples/Topics/Geometry/RGBCube/RGBCube.pde
diff --git a/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde b/processing/mode/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde
similarity index 100%
rename from examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde
rename to processing/mode/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde
diff --git a/examples/Topics/Geometry/SpaceJunk/Cube.pde b/processing/mode/examples/Topics/Geometry/SpaceJunk/Cube.pde
similarity index 100%
rename from examples/Topics/Geometry/SpaceJunk/Cube.pde
rename to processing/mode/examples/Topics/Geometry/SpaceJunk/Cube.pde
diff --git a/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde b/processing/mode/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde
similarity index 100%
rename from examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde
rename to processing/mode/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde
diff --git a/examples/Topics/Geometry/Toroid/Toroid.pde b/processing/mode/examples/Topics/Geometry/Toroid/Toroid.pde
similarity index 100%
rename from examples/Topics/Geometry/Toroid/Toroid.pde
rename to processing/mode/examples/Topics/Geometry/Toroid/Toroid.pde
diff --git a/examples/Topics/Geometry/Vertices/Vertices.pde b/processing/mode/examples/Topics/Geometry/Vertices/Vertices.pde
similarity index 100%
rename from examples/Topics/Geometry/Vertices/Vertices.pde
rename to processing/mode/examples/Topics/Geometry/Vertices/Vertices.pde
diff --git a/examples/Topics/Image Processing/Blur/Blur.pde b/processing/mode/examples/Topics/Image Processing/Blur/Blur.pde
similarity index 98%
rename from examples/Topics/Image Processing/Blur/Blur.pde
rename to processing/mode/examples/Topics/Image Processing/Blur/Blur.pde
index 67e9ef586..3eb7103d2 100644
--- a/examples/Topics/Image Processing/Blur/Blur.pde
+++ b/processing/mode/examples/Topics/Image Processing/Blur/Blur.pde
@@ -18,6 +18,7 @@ img.loadPixels();
// Create an opaque image of the same size as the original
PImage edgeImg = createImage(img.width, img.height, RGB);
+edgeImg.loadPixels();
// Loop through every pixel in the image.
for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
diff --git a/examples/Topics/Image Processing/Blur/data/trees.jpg b/processing/mode/examples/Topics/Image Processing/Blur/data/trees.jpg
similarity index 100%
rename from examples/Topics/Image Processing/Blur/data/trees.jpg
rename to processing/mode/examples/Topics/Image Processing/Blur/data/trees.jpg
diff --git a/examples/Topics/Image Processing/Brightness/Brightness.pde b/processing/mode/examples/Topics/Image Processing/Brightness/Brightness.pde
similarity index 100%
rename from examples/Topics/Image Processing/Brightness/Brightness.pde
rename to processing/mode/examples/Topics/Image Processing/Brightness/Brightness.pde
diff --git a/examples/Topics/Image Processing/Brightness/data/wires.jpg b/processing/mode/examples/Topics/Image Processing/Brightness/data/wires.jpg
similarity index 100%
rename from examples/Topics/Image Processing/Brightness/data/wires.jpg
rename to processing/mode/examples/Topics/Image Processing/Brightness/data/wires.jpg
diff --git a/examples/Topics/Image Processing/Convolution/Convolution.pde b/processing/mode/examples/Topics/Image Processing/Convolution/Convolution.pde
similarity index 99%
rename from examples/Topics/Image Processing/Convolution/Convolution.pde
rename to processing/mode/examples/Topics/Image Processing/Convolution/Convolution.pde
index bb3f7eda5..5640c89b1 100644
--- a/examples/Topics/Image Processing/Convolution/Convolution.pde
+++ b/processing/mode/examples/Topics/Image Processing/Convolution/Convolution.pde
@@ -20,6 +20,7 @@ void setup() {
size(200, 200);
frameRate(30);
img = loadImage("end.jpg");
+ img.loadPixels();
}
void draw() {
diff --git a/examples/Topics/Image Processing/Convolution/data/end.jpg b/processing/mode/examples/Topics/Image Processing/Convolution/data/end.jpg
similarity index 100%
rename from examples/Topics/Image Processing/Convolution/data/end.jpg
rename to processing/mode/examples/Topics/Image Processing/Convolution/data/end.jpg
diff --git a/examples/Topics/Image Processing/Convolution/data/sunflower.jpg b/processing/mode/examples/Topics/Image Processing/Convolution/data/sunflower.jpg
similarity index 100%
rename from examples/Topics/Image Processing/Convolution/data/sunflower.jpg
rename to processing/mode/examples/Topics/Image Processing/Convolution/data/sunflower.jpg
diff --git a/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde b/processing/mode/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde
similarity index 100%
rename from examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde
rename to processing/mode/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde
diff --git a/examples/Topics/Image Processing/EdgeDetection/data/house.jpg b/processing/mode/examples/Topics/Image Processing/EdgeDetection/data/house.jpg
similarity index 100%
rename from examples/Topics/Image Processing/EdgeDetection/data/house.jpg
rename to processing/mode/examples/Topics/Image Processing/EdgeDetection/data/house.jpg
diff --git a/examples/Topics/Image Processing/Histogram/Histogram.pde b/processing/mode/examples/Topics/Image Processing/Histogram/Histogram.pde
similarity index 100%
rename from examples/Topics/Image Processing/Histogram/Histogram.pde
rename to processing/mode/examples/Topics/Image Processing/Histogram/Histogram.pde
diff --git a/examples/Topics/Image Processing/Histogram/data/cdi01_g.jpg b/processing/mode/examples/Topics/Image Processing/Histogram/data/cdi01_g.jpg
similarity index 100%
rename from examples/Topics/Image Processing/Histogram/data/cdi01_g.jpg
rename to processing/mode/examples/Topics/Image Processing/Histogram/data/cdi01_g.jpg
diff --git a/examples/Topics/Image Processing/Histogram/data/ystone08.jpg b/processing/mode/examples/Topics/Image Processing/Histogram/data/ystone08.jpg
similarity index 100%
rename from examples/Topics/Image Processing/Histogram/data/ystone08.jpg
rename to processing/mode/examples/Topics/Image Processing/Histogram/data/ystone08.jpg
diff --git a/examples/Topics/Image Processing/LinearImage/LinearImage.pde b/processing/mode/examples/Topics/Image Processing/LinearImage/LinearImage.pde
similarity index 100%
rename from examples/Topics/Image Processing/LinearImage/LinearImage.pde
rename to processing/mode/examples/Topics/Image Processing/LinearImage/LinearImage.pde
diff --git a/examples/Topics/Image Processing/LinearImage/data/florence03.jpg b/processing/mode/examples/Topics/Image Processing/LinearImage/data/florence03.jpg
similarity index 100%
rename from examples/Topics/Image Processing/LinearImage/data/florence03.jpg
rename to processing/mode/examples/Topics/Image Processing/LinearImage/data/florence03.jpg
diff --git a/examples/Topics/Image Processing/PixelArray/PixelArray.pde b/processing/mode/examples/Topics/Image Processing/PixelArray/PixelArray.pde
similarity index 100%
rename from examples/Topics/Image Processing/PixelArray/PixelArray.pde
rename to processing/mode/examples/Topics/Image Processing/PixelArray/PixelArray.pde
diff --git a/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg b/processing/mode/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg
similarity index 100%
rename from examples/Topics/Image Processing/PixelArray/data/ystone08.jpg
rename to processing/mode/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg
diff --git a/examples/Topics/Interaction/Follow1/Follow1.pde b/processing/mode/examples/Topics/Interaction/Follow1/Follow1.pde
similarity index 100%
rename from examples/Topics/Interaction/Follow1/Follow1.pde
rename to processing/mode/examples/Topics/Interaction/Follow1/Follow1.pde
diff --git a/examples/Topics/Interaction/Follow2/Follow2.pde b/processing/mode/examples/Topics/Interaction/Follow2/Follow2.pde
similarity index 100%
rename from examples/Topics/Interaction/Follow2/Follow2.pde
rename to processing/mode/examples/Topics/Interaction/Follow2/Follow2.pde
diff --git a/examples/Topics/Interaction/Follow3/Follow3.pde b/processing/mode/examples/Topics/Interaction/Follow3/Follow3.pde
similarity index 100%
rename from examples/Topics/Interaction/Follow3/Follow3.pde
rename to processing/mode/examples/Topics/Interaction/Follow3/Follow3.pde
diff --git a/processing/mode/examples/Topics/Interaction/Multitouch/Multitouch.pde b/processing/mode/examples/Topics/Interaction/Multitouch/Multitouch.pde
new file mode 100644
index 000000000..dd0b19dba
--- /dev/null
+++ b/processing/mode/examples/Topics/Interaction/Multitouch/Multitouch.pde
@@ -0,0 +1,33 @@
+float scale;
+
+void setup() {
+ fullScreen();
+ noStroke();
+ scale = displayDensity / 2.65;
+ colorMode(HSB, 350, 100, 100);
+ textFont(createFont("SansSerif", scale * 60));
+}
+
+void draw() {
+ background(30, 0, 100);
+ fill(30, 0, 20);
+ text("Number of touch points: " + touches.length, 20, scale * 100);
+ for (int i = 0; i < touches.length; i++) {
+ float s = scale * map(touches[i].area, 0, 1, 50, 500);
+ println(touches[i].area);
+ fill(30, map(touches[i].pressure, 0.6, 1.6, 0, 100), 70, 200);
+ ellipse(touches[i].x, touches[i].y, s, s);
+ }
+}
+
+void touchStarted() {
+ println("Touch started");
+}
+
+void touchEnded() {
+ println("Touch ended");
+}
+
+void touchMoved() {
+ println("Touch moved");
+}
\ No newline at end of file
diff --git a/examples/Topics/Interaction/Reach1/Reach1.pde b/processing/mode/examples/Topics/Interaction/Reach1/Reach1.pde
similarity index 100%
rename from examples/Topics/Interaction/Reach1/Reach1.pde
rename to processing/mode/examples/Topics/Interaction/Reach1/Reach1.pde
diff --git a/examples/Topics/Interaction/Reach2/Reach2.pde b/processing/mode/examples/Topics/Interaction/Reach2/Reach2.pde
similarity index 100%
rename from examples/Topics/Interaction/Reach2/Reach2.pde
rename to processing/mode/examples/Topics/Interaction/Reach2/Reach2.pde
diff --git a/examples/Topics/Interaction/Reach3/Reach3.pde b/processing/mode/examples/Topics/Interaction/Reach3/Reach3.pde
similarity index 100%
rename from examples/Topics/Interaction/Reach3/Reach3.pde
rename to processing/mode/examples/Topics/Interaction/Reach3/Reach3.pde
diff --git a/examples/Topics/Interaction/Tickle/Tickle.pde b/processing/mode/examples/Topics/Interaction/Tickle/Tickle.pde
similarity index 100%
rename from examples/Topics/Interaction/Tickle/Tickle.pde
rename to processing/mode/examples/Topics/Interaction/Tickle/Tickle.pde
diff --git a/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw b/processing/mode/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw
similarity index 100%
rename from examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw
rename to processing/mode/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw
diff --git a/examples/Topics/Motion/Bounce/Bounce.pde b/processing/mode/examples/Topics/Motion/Bounce/Bounce.pde
similarity index 100%
rename from examples/Topics/Motion/Bounce/Bounce.pde
rename to processing/mode/examples/Topics/Motion/Bounce/Bounce.pde
diff --git a/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde b/processing/mode/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde
similarity index 100%
rename from examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde
rename to processing/mode/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde
diff --git a/examples/Topics/Motion/Brownian/Brownian.pde b/processing/mode/examples/Topics/Motion/Brownian/Brownian.pde
similarity index 100%
rename from examples/Topics/Motion/Brownian/Brownian.pde
rename to processing/mode/examples/Topics/Motion/Brownian/Brownian.pde
diff --git a/examples/Topics/Motion/CircleCollision/Ball.pde b/processing/mode/examples/Topics/Motion/CircleCollision/Ball.pde
similarity index 100%
rename from examples/Topics/Motion/CircleCollision/Ball.pde
rename to processing/mode/examples/Topics/Motion/CircleCollision/Ball.pde
diff --git a/examples/Topics/Motion/CircleCollision/CircleCollision.pde b/processing/mode/examples/Topics/Motion/CircleCollision/CircleCollision.pde
similarity index 100%
rename from examples/Topics/Motion/CircleCollision/CircleCollision.pde
rename to processing/mode/examples/Topics/Motion/CircleCollision/CircleCollision.pde
diff --git a/examples/Topics/Motion/Collision/Collision.pde b/processing/mode/examples/Topics/Motion/Collision/Collision.pde
similarity index 100%
rename from examples/Topics/Motion/Collision/Collision.pde
rename to processing/mode/examples/Topics/Motion/Collision/Collision.pde
diff --git a/examples/Topics/Motion/Linear/Linear.pde b/processing/mode/examples/Topics/Motion/Linear/Linear.pde
similarity index 100%
rename from examples/Topics/Motion/Linear/Linear.pde
rename to processing/mode/examples/Topics/Motion/Linear/Linear.pde
diff --git a/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde b/processing/mode/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde
similarity index 100%
rename from examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde
rename to processing/mode/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde
diff --git a/examples/Topics/Motion/Puff/Puff.pde b/processing/mode/examples/Topics/Motion/Puff/Puff.pde
similarity index 100%
rename from examples/Topics/Motion/Puff/Puff.pde
rename to processing/mode/examples/Topics/Motion/Puff/Puff.pde
diff --git a/examples/Topics/Motion/Reflection1/Reflection1.pde b/processing/mode/examples/Topics/Motion/Reflection1/Reflection1.pde
similarity index 100%
rename from examples/Topics/Motion/Reflection1/Reflection1.pde
rename to processing/mode/examples/Topics/Motion/Reflection1/Reflection1.pde
diff --git a/examples/Topics/Motion/Reflection2/Ground.pde b/processing/mode/examples/Topics/Motion/Reflection2/Ground.pde
similarity index 100%
rename from examples/Topics/Motion/Reflection2/Ground.pde
rename to processing/mode/examples/Topics/Motion/Reflection2/Ground.pde
diff --git a/examples/Topics/Motion/Reflection2/Orb.pde b/processing/mode/examples/Topics/Motion/Reflection2/Orb.pde
similarity index 100%
rename from examples/Topics/Motion/Reflection2/Orb.pde
rename to processing/mode/examples/Topics/Motion/Reflection2/Orb.pde
diff --git a/examples/Topics/Motion/Reflection2/Reflection2.pde b/processing/mode/examples/Topics/Motion/Reflection2/Reflection2.pde
similarity index 100%
rename from examples/Topics/Motion/Reflection2/Reflection2.pde
rename to processing/mode/examples/Topics/Motion/Reflection2/Reflection2.pde
diff --git a/examples/Topics/Shaders/BlurFilter/BlurFilter.pde b/processing/mode/examples/Topics/Shaders/BlurFilter/BlurFilter.pde
similarity index 100%
rename from examples/Topics/Shaders/BlurFilter/BlurFilter.pde
rename to processing/mode/examples/Topics/Shaders/BlurFilter/BlurFilter.pde
diff --git a/processing/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl b/processing/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl
new file mode 100644
index 000000000..2aa9d6079
--- /dev/null
+++ b/processing/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl
@@ -0,0 +1,42 @@
+#ifdef GL_ES
+precision mediump float;
+precision mediump int;
+#endif
+
+#define PROCESSING_TEXTURE_SHADER
+
+uniform sampler2D texture;
+uniform vec2 texOffset;
+
+varying vec4 vertColor;
+varying vec4 vertTexCoord;
+
+void main(void) {
+ // Grouping texcoord variables in order to make it work in the GMA 950. See post #13
+ // in this thread:
+ // http://www.idevgames.com/forums/thread-3467.html
+ vec2 tc0 = vertTexCoord.st + vec2(-texOffset.s, -texOffset.t);
+ vec2 tc1 = vertTexCoord.st + vec2( 0.0, -texOffset.t);
+ vec2 tc2 = vertTexCoord.st + vec2(+texOffset.s, -texOffset.t);
+ vec2 tc3 = vertTexCoord.st + vec2(-texOffset.s, 0.0);
+ vec2 tc4 = vertTexCoord.st + vec2( 0.0, 0.0);
+ vec2 tc5 = vertTexCoord.st + vec2(+texOffset.s, 0.0);
+ vec2 tc6 = vertTexCoord.st + vec2(-texOffset.s, +texOffset.t);
+ vec2 tc7 = vertTexCoord.st + vec2( 0.0, +texOffset.t);
+ vec2 tc8 = vertTexCoord.st + vec2(+texOffset.s, +texOffset.t);
+
+ vec4 col0 = texture2D(texture, tc0);
+ vec4 col1 = texture2D(texture, tc1);
+ vec4 col2 = texture2D(texture, tc2);
+ vec4 col3 = texture2D(texture, tc3);
+ vec4 col4 = texture2D(texture, tc4);
+ vec4 col5 = texture2D(texture, tc5);
+ vec4 col6 = texture2D(texture, tc6);
+ vec4 col7 = texture2D(texture, tc7);
+ vec4 col8 = texture2D(texture, tc8);
+
+ vec4 sum = (1.0 * col0 + 2.0 * col1 + 1.0 * col2 +
+ 2.0 * col3 + 4.0 * col4 + 2.0 * col4 +
+ 1.0 * col5 + 2.0 * col6 + 1.0 * col7) / 16.0;
+ gl_FragColor = vec4(sum.rgb, 1.0) * vertColor;
+}
diff --git a/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde b/processing/mode/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde
similarity index 100%
rename from examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde
rename to processing/mode/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde
diff --git a/examples/Topics/Shaders/EdgeDetect/data/edges.glsl b/processing/mode/examples/Topics/Shaders/EdgeDetect/data/edges.glsl
similarity index 100%
rename from examples/Topics/Shaders/EdgeDetect/data/edges.glsl
rename to processing/mode/examples/Topics/Shaders/EdgeDetect/data/edges.glsl
diff --git a/processing/mode/examples/Topics/Shaders/EdgeDetect/data/leaves.jpg b/processing/mode/examples/Topics/Shaders/EdgeDetect/data/leaves.jpg
new file mode 100644
index 000000000..72c86a092
Binary files /dev/null and b/processing/mode/examples/Topics/Shaders/EdgeDetect/data/leaves.jpg differ
diff --git a/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde b/processing/mode/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde
similarity index 100%
rename from examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde
rename to processing/mode/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde
diff --git a/examples/Topics/Shaders/EdgeFilter/data/edges.glsl b/processing/mode/examples/Topics/Shaders/EdgeFilter/data/edges.glsl
similarity index 100%
rename from examples/Topics/Shaders/EdgeFilter/data/edges.glsl
rename to processing/mode/examples/Topics/Shaders/EdgeFilter/data/edges.glsl
diff --git a/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde b/processing/mode/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde
similarity index 100%
rename from examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde
rename to processing/mode/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde
diff --git a/examples/Topics/Shaders/LowLevelGL/data/frag.glsl b/processing/mode/examples/Topics/Shaders/LowLevelGL/data/frag.glsl
similarity index 100%
rename from examples/Topics/Shaders/LowLevelGL/data/frag.glsl
rename to processing/mode/examples/Topics/Shaders/LowLevelGL/data/frag.glsl
diff --git a/examples/Topics/Shaders/LowLevelGL/data/vert.glsl b/processing/mode/examples/Topics/Shaders/LowLevelGL/data/vert.glsl
similarity index 100%
rename from examples/Topics/Shaders/LowLevelGL/data/vert.glsl
rename to processing/mode/examples/Topics/Shaders/LowLevelGL/data/vert.glsl
diff --git a/examples/Topics/Shaders/ToonShading/ToonShading.pde b/processing/mode/examples/Topics/Shaders/ToonShading/ToonShading.pde
similarity index 100%
rename from examples/Topics/Shaders/ToonShading/ToonShading.pde
rename to processing/mode/examples/Topics/Shaders/ToonShading/ToonShading.pde
diff --git a/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl b/processing/mode/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl
similarity index 100%
rename from examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl
rename to processing/mode/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl
diff --git a/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl b/processing/mode/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl
similarity index 100%
rename from examples/Topics/Shaders/ToonShading/data/ToonVert.glsl
rename to processing/mode/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl
diff --git a/examples/Topics/Simulate/Chain/Chain.pde b/processing/mode/examples/Topics/Simulate/Chain/Chain.pde
similarity index 100%
rename from examples/Topics/Simulate/Chain/Chain.pde
rename to processing/mode/examples/Topics/Simulate/Chain/Chain.pde
diff --git a/examples/Topics/Simulate/Flocking/Boid.pde b/processing/mode/examples/Topics/Simulate/Flocking/Boid.pde
similarity index 100%
rename from examples/Topics/Simulate/Flocking/Boid.pde
rename to processing/mode/examples/Topics/Simulate/Flocking/Boid.pde
diff --git a/examples/Topics/Simulate/Flocking/Flock.pde b/processing/mode/examples/Topics/Simulate/Flocking/Flock.pde
similarity index 100%
rename from examples/Topics/Simulate/Flocking/Flock.pde
rename to processing/mode/examples/Topics/Simulate/Flocking/Flock.pde
diff --git a/examples/Topics/Simulate/Flocking/Flocking.pde b/processing/mode/examples/Topics/Simulate/Flocking/Flocking.pde
similarity index 100%
rename from examples/Topics/Simulate/Flocking/Flocking.pde
rename to processing/mode/examples/Topics/Simulate/Flocking/Flocking.pde
diff --git a/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde b/processing/mode/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde
similarity index 100%
rename from examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde
rename to processing/mode/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde
diff --git a/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde b/processing/mode/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde
similarity index 100%
rename from examples/Topics/Simulate/ForcesWithVectors/Liquid.pde
rename to processing/mode/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde
diff --git a/examples/Topics/Simulate/ForcesWithVectors/Mover.pde b/processing/mode/examples/Topics/Simulate/ForcesWithVectors/Mover.pde
similarity index 100%
rename from examples/Topics/Simulate/ForcesWithVectors/Mover.pde
rename to processing/mode/examples/Topics/Simulate/ForcesWithVectors/Mover.pde
diff --git a/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde b/processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde
similarity index 100%
rename from examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde
rename to processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde
diff --git a/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde b/processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde
similarity index 100%
rename from examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde
rename to processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde
diff --git a/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde b/processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde
similarity index 100%
rename from examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde
rename to processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde
diff --git a/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde
similarity index 100%
rename from examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde
rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde
diff --git a/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde
similarity index 100%
rename from examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde
rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde
diff --git a/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde
similarity index 100%
rename from examples/Topics/Simulate/MultipleParticleSystems/Particle.pde
rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde
diff --git a/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde
similarity index 100%
rename from examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde
rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde
diff --git a/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde b/processing/mode/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde
similarity index 100%
rename from examples/Topics/Simulate/SimpleParticleSystem/Particle.pde
rename to processing/mode/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde
diff --git a/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde
similarity index 100%
rename from examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde
rename to processing/mode/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde
diff --git a/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde
similarity index 100%
rename from examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde
rename to processing/mode/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde
diff --git a/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde
similarity index 100%
rename from examples/Topics/Simulate/SmokeParticleSystem/Particle.pde
rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde
diff --git a/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde
similarity index 100%
rename from examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde
rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde
diff --git a/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde
similarity index 100%
rename from examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde
rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde
diff --git a/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif
similarity index 100%
rename from examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif
rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif
diff --git a/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png
similarity index 100%
rename from examples/Topics/Simulate/SmokeParticleSystem/data/texture.png
rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png
diff --git a/examples/Topics/Simulate/SoftBody/SoftBody.pde b/processing/mode/examples/Topics/Simulate/SoftBody/SoftBody.pde
similarity index 100%
rename from examples/Topics/Simulate/SoftBody/SoftBody.pde
rename to processing/mode/examples/Topics/Simulate/SoftBody/SoftBody.pde
diff --git a/examples/Topics/Simulate/Spring/Spring.pde b/processing/mode/examples/Topics/Simulate/Spring/Spring.pde
similarity index 100%
rename from examples/Topics/Simulate/Spring/Spring.pde
rename to processing/mode/examples/Topics/Simulate/Spring/Spring.pde
diff --git a/examples/Topics/Simulate/Springs/Springs.pde b/processing/mode/examples/Topics/Simulate/Springs/Springs.pde
similarity index 100%
rename from examples/Topics/Simulate/Springs/Springs.pde
rename to processing/mode/examples/Topics/Simulate/Springs/Springs.pde
diff --git a/examples/Topics/Textures/TextureCube/TextureCube.pde b/processing/mode/examples/Topics/Textures/TextureCube/TextureCube.pde
similarity index 100%
rename from examples/Topics/Textures/TextureCube/TextureCube.pde
rename to processing/mode/examples/Topics/Textures/TextureCube/TextureCube.pde
diff --git a/examples/Topics/Textures/TextureCube/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureCube/data/berlin-1.jpg
similarity index 100%
rename from examples/Topics/Textures/TextureCube/data/berlin-1.jpg
rename to processing/mode/examples/Topics/Textures/TextureCube/data/berlin-1.jpg
diff --git a/examples/Topics/Textures/TextureCube/data/uvtex.jpg b/processing/mode/examples/Topics/Textures/TextureCube/data/uvtex.jpg
similarity index 100%
rename from examples/Topics/Textures/TextureCube/data/uvtex.jpg
rename to processing/mode/examples/Topics/Textures/TextureCube/data/uvtex.jpg
diff --git a/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde b/processing/mode/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde
similarity index 100%
rename from examples/Topics/Textures/TextureCylinder/TextureCylinder.pde
rename to processing/mode/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde
diff --git a/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg
similarity index 100%
rename from examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg
rename to processing/mode/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg
diff --git a/examples/Topics/Textures/TextureQuad/TextureQuad.pde b/processing/mode/examples/Topics/Textures/TextureQuad/TextureQuad.pde
similarity index 100%
rename from examples/Topics/Textures/TextureQuad/TextureQuad.pde
rename to processing/mode/examples/Topics/Textures/TextureQuad/TextureQuad.pde
diff --git a/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg
similarity index 100%
rename from examples/Topics/Textures/TextureQuad/data/berlin-1.jpg
rename to processing/mode/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg
diff --git a/examples/Topics/Textures/TextureSphere/TextureSphere.pde b/processing/mode/examples/Topics/Textures/TextureSphere/TextureSphere.pde
similarity index 100%
rename from examples/Topics/Textures/TextureSphere/TextureSphere.pde
rename to processing/mode/examples/Topics/Textures/TextureSphere/TextureSphere.pde
diff --git a/examples/Topics/Textures/TextureSphere/data/world32k.jpg b/processing/mode/examples/Topics/Textures/TextureSphere/data/world32k.jpg
similarity index 100%
rename from examples/Topics/Textures/TextureSphere/data/world32k.jpg
rename to processing/mode/examples/Topics/Textures/TextureSphere/data/world32k.jpg
diff --git a/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde b/processing/mode/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde
similarity index 100%
rename from examples/Topics/Textures/TextureTriangle/TextureTriangle.pde
rename to processing/mode/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde
diff --git a/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg
similarity index 100%
rename from examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg
rename to processing/mode/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg
diff --git a/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde b/processing/mode/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde
similarity index 100%
rename from examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde
rename to processing/mode/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde
diff --git a/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde b/processing/mode/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde
similarity index 100%
rename from examples/Topics/Vectors/AccelerationWithVectors/Mover.pde
rename to processing/mode/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde
diff --git a/examples/Topics/Vectors/BouncingBall/BouncingBall.pde b/processing/mode/examples/Topics/Vectors/BouncingBall/BouncingBall.pde
similarity index 100%
rename from examples/Topics/Vectors/BouncingBall/BouncingBall.pde
rename to processing/mode/examples/Topics/Vectors/BouncingBall/BouncingBall.pde
diff --git a/examples/Topics/Vectors/Normalize/Normalize.pde b/processing/mode/examples/Topics/Vectors/Normalize/Normalize.pde
similarity index 100%
rename from examples/Topics/Vectors/Normalize/Normalize.pde
rename to processing/mode/examples/Topics/Vectors/Normalize/Normalize.pde
diff --git a/examples/Topics/Vectors/VectorMath/VectorMath.pde b/processing/mode/examples/Topics/Vectors/VectorMath/VectorMath.pde
similarity index 100%
rename from examples/Topics/Vectors/VectorMath/VectorMath.pde
rename to processing/mode/examples/Topics/Vectors/VectorMath/VectorMath.pde
diff --git a/processing/mode/examples/Topics/Wallpapers/Circles/Circles.pde b/processing/mode/examples/Topics/Wallpapers/Circles/Circles.pde
new file mode 100644
index 000000000..4a66fb071
--- /dev/null
+++ b/processing/mode/examples/Topics/Wallpapers/Circles/Circles.pde
@@ -0,0 +1,17 @@
+void setup() {
+ fullScreen(P2D);
+ noStroke();
+ background(255);
+}
+
+void draw() {
+ if (mousePressed) {
+ background(255);
+ }
+ float x = random(width);
+ float y = random(height);
+ color c = color(random(255), random(255), random(255));
+ float r = random(10, 30);
+ fill(c);
+ ellipse(x, y, r, r);
+}
\ No newline at end of file
diff --git a/processing/mode/examples/Topics/Wallpapers/Circles/code/sketch.properties b/processing/mode/examples/Topics/Wallpapers/Circles/code/sketch.properties
new file mode 100644
index 000000000..181123ebe
--- /dev/null
+++ b/processing/mode/examples/Topics/Wallpapers/Circles/code/sketch.properties
@@ -0,0 +1 @@
+component=wallpaper
diff --git a/processing/mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde b/processing/mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde
new file mode 100644
index 000000000..78285839d
--- /dev/null
+++ b/processing/mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde
@@ -0,0 +1,28 @@
+float angle = 0;
+
+void setup() {
+ fullScreen(P2D);
+ frameRate(15);
+}
+
+void draw() {
+ translate(0, +wearInsets().bottom/2);
+
+ if (wearAmbient()) {
+ background(0);
+ stroke(255);
+ noFill();
+ } else {
+ background(157);
+ stroke(0);
+ fill(255);
+ }
+
+ line(0, 0, width, height);
+ line(width, 0, 0, height);
+
+ translate(width/2, height/2);
+ rotate(angle);
+ rect(-50, -50, 100, 100);
+ angle += 0.01;
+}
\ No newline at end of file
diff --git a/processing/mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties b/processing/mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties
new file mode 100644
index 000000000..a70fa2ba6
--- /dev/null
+++ b/processing/mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties
@@ -0,0 +1 @@
+component=watchface
diff --git a/processing/mode/icons/launcher_144.png b/processing/mode/icons/launcher_144.png
new file mode 100644
index 000000000..78fed6592
Binary files /dev/null and b/processing/mode/icons/launcher_144.png differ
diff --git a/processing/mode/icons/launcher_192.png b/processing/mode/icons/launcher_192.png
new file mode 100644
index 000000000..48d3a34fe
Binary files /dev/null and b/processing/mode/icons/launcher_192.png differ
diff --git a/processing/mode/icons/launcher_36.png b/processing/mode/icons/launcher_36.png
new file mode 100644
index 000000000..e276bbd6f
Binary files /dev/null and b/processing/mode/icons/launcher_36.png differ
diff --git a/processing/mode/icons/launcher_48.png b/processing/mode/icons/launcher_48.png
new file mode 100644
index 000000000..9dde1a58f
Binary files /dev/null and b/processing/mode/icons/launcher_48.png differ
diff --git a/processing/mode/icons/launcher_72.png b/processing/mode/icons/launcher_72.png
new file mode 100644
index 000000000..ff769c587
Binary files /dev/null and b/processing/mode/icons/launcher_72.png differ
diff --git a/processing/mode/icons/launcher_96.png b/processing/mode/icons/launcher_96.png
new file mode 100644
index 000000000..d9b25dba2
Binary files /dev/null and b/processing/mode/icons/launcher_96.png differ
diff --git a/processing/mode/icons/preview_circular.png b/processing/mode/icons/preview_circular.png
new file mode 100644
index 000000000..40a11bf0d
Binary files /dev/null and b/processing/mode/icons/preview_circular.png differ
diff --git a/processing/mode/icons/preview_rectangular.png b/processing/mode/icons/preview_rectangular.png
new file mode 100644
index 000000000..9c9d9ed41
Binary files /dev/null and b/processing/mode/icons/preview_rectangular.png differ
diff --git a/processing/mode/keywords.txt b/processing/mode/keywords.txt
new file mode 100644
index 000000000..b16ac21d4
--- /dev/null
+++ b/processing/mode/keywords.txt
@@ -0,0 +1,50 @@
+# Android-specific keywords
+
+# For an explanation of these tags, see Token.java
+# processing/app/src/processing/app/syntax/Token.java
+
+VR LITERAL2
+AR LITERAL2
+STEREO LITERAL2
+MONO LITERAL2
+PORTRAIT LITERAL2
+LANDSCAPE LITERAL2
+displayDensity KEYWORD4
+
+orientation FUNCTION1
+hasPermission FUNCTION1
+requestPermission FUNCTION1
+touches KEYWORD4
+touchEnded FUNCTION4
+touchMoved FUNCTION4
+touchStarted FUNCTION4
+closeKeyboard FUNCTION1
+openKeyboard FUNCTION1
+wallpaperHomeCount FUNCTION1
+wallpaperOffset FUNCTION1
+wallpaperPreview FUNCTION1
+wearAmbient FUNCTION1
+wearBurnIn FUNCTION1
+wearInsets FUNCTION1
+wearInteractive FUNCTION1
+wearLowBit FUNCTION1
+wearRound FUNCTION1
+wearSquare FUNCTION1
+cameraUp FUNCTION1
+eye FUNCTION1
+getEyeMatrix FUNCTION1
+getObjectMatrix FUNCTION1
+getRayFromScreen FUNCTION1
+intersectsSphere FUNCTION1
+intersectsBox FUNCTION1
+intersectsPlane FUNCTION1
+calculate FUNCTION1
+push FUNCTION1
+pop FUNCTION1
+circle FUNCTION1
+square FUNCTION1
+
+VRCamera KEYWORD5
+ARTracker KEYWORD5
+ARTrackable KEYWORD5
+ARAnchor KEYWORD5
diff --git a/processing/mode/languages/mode.properties b/processing/mode/languages/mode.properties
new file mode 100644
index 000000000..a87ae41d9
--- /dev/null
+++ b/processing/mode/languages/mode.properties
@@ -0,0 +1,328 @@
+
+
+# ---------------------------------------
+# Language: English (en) (default)
+# ---------------------------------------
+
+
+# ---------------------------------------
+# Menu
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | File |
+
+menu.sketch.stop = Stop
+menu.file.new = New
+menu.file.open = Open
+menu.file.save = Save
+
+menu.file.export_signed_package = Export Signed Package
+menu.file.export_signed_bundle = Export Signed Bundle
+menu.file.export_android_project = Export Android Project
+
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Sketch |
+
+menu.sketch.run_on_device = Run on Device
+menu.sketch.run_in_emulator = Run in Emulator
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Android |
+
+menu.android = Android
+menu.android.sketch_permissions = Sketch Permissions
+menu.android.app = App
+menu.android.wallpaper = Wallpaper
+menu.android.watch_face = Watch Face
+menu.android.vr = VR
+menu.android.ar = AR
+menu.android.devices = Devices
+menu.android.devices.no_connected_devices = No connected devices
+menu.android.sdk_updater = SDK Updater
+menu.android.reset_adb = Reset ADB
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Help |
+
+menu.help.processing_for_android_site = Processing for Android Site
+menu.help.android_developer_site = Android Developer Site
+
+# ---------------------------------------
+# Android Build
+
+android_build.error.build_folder = Build folder: %s
+android_build.error.export_file_does_not_exist = "%s" is mentioned in export.txt, but it's a big fat lie and does not exist.
+android_build.error.cannot_create_build_folder = Cannot create temp dir "%s" to build android sketch
+android_build.error.zip_files_not_allowed = .zip files are not allowed in Android libraries.\nPlease rename "%s to be a .jar file.
+android_build.error.cannot_copy_icons = Problem while copying icons.
+android_build.error.cannot_create_icon_folder = Could not create "%s" folder
+android_build.warn.cannot_find_zipalign.title = Cannot find zipaling...
+android_build.warn.cannot_find_zipalign.body = The zipalign build tool needed to prepare the export bundle is missing.\nMake sure that your Android SDK was downloaded correctly.
+
+# ---------------------------------------
+# Android Debugger
+
+android_debugger.info.attaching_debugger = Attaching debugger...
+android_debugger.info.debugger_attached = Debugger attached
+android_debugger.error.debugger_exception = Debugger error: %s
+
+# ---------------------------------------
+# Android Editor
+
+android_editor.status.exporting_project = Exporting an Android project of the sketch...
+android_editor.status.project_export_completed = Done with project export.
+android_editor.status.project_export_failed = Error with project export.
+android_editor.status.exporting_package = Exporting signed package...
+android_editor.status.package_export_completed = Done with package export.
+android_editor.status.package_export_failed = Error with package export.
+android_editor.status.exporting_bundle = Exporting signed bundle...
+android_editor.status.bundle_export_completed = Done with bundle export.
+android_editor.status.bundle_export_failed = Error with bundle export.
+
+android_editor.error.cannot_create_sketch_properties = Error While creating sketch properties file "%s": %s
+
+# ---------------------------------------
+# Android Keystore
+
+android_keystore.warn.cannot_create_folders.title = Folders, folders, folders
+android_keystore.warn.cannot_create_folders.body = Could not create the necessary folders to build.\nPerhaps you have some file permissions to sort out?
+android_keystore.warn.cannot_find_keystore.title = Well, this is unexpected...
+android_keystore.warn.cannot_find_keystore.body = The keystore was succesfully created but cannot be found.\nPerhaps was it deleted accidentally?
+android_keystore.error.cannot_create_keystore = The keystore could not be created, due to the following error:
+
+# ---------------------------------------
+# Android Mode
+
+android_mode.dialog.watchface_debug_title = Is the watch connected to the computer?
+android_mode.dialog.watchface_debug_body = Processing will install watch faces on a smartwatch either over Wi-Fi or via Bluetooth, in which case the watch needs to be paired with a phone. Read this guide on debugging an Android Wear App for more details.
+android_mode.dialog.wallpaper_installed_title = Wallpaper installed!
+android_mode.dialog.wallpaper_installed_body = Processing just built and installed your sketch as a live wallpaper on the selected device. You need to open the wallpaper picker in the device in order to select it as the new background.
+android_mode.dialog.watchface_installed_title = Watch face installed!
+android_mode.dialog.watchface_installed_body = Processing just built and installed your sketch as a watch face on the selected device. You need to add it as a favourite watch face on the device and then select it from the watch face picker in order to run it.
+android_mode.dialog.cannot_export_package_title = Cannot complete export...
+android_mode.dialog.cannot_export_package_body = The sketch still has the default package name. Not good, since this name will uniquely identify your app on the Play store... for ever! Come up with a different package name and write in the AndroidManifest.xml file in the sketch folder, after the "package=" attribute inside the manifest tag, which also contains version code and name. Once you have done that, try exporting the sketch again. For more info on distributing apps from Processing, check this online tutorial .
+android_mode.dialog.cannot_use_default_icons_title = Cannot complete export...
+android_mode.dialog.cannot_use_default_icons_body = The sketch does not include all required app icons. Processing could use its default set of Android icons, which are okay to test the app on your device, but a bad idea to distribute it on the Play store. Create a full set of unique icons for your app, and copy them into the sketch folder. Once you have done that, try exporting the sketch again. For more info on distributing apps from Processing, check this online tutorial .
+android_mode.warn.cannot_load_sdk_title = Bad news...
+android_mode.warn.cannot_load_sdk_body = The Android SDK could not be loaded.\nThe Android Mode will be disabled.
+android_mode.info.cannot_open_sdk_path = "Android SDK path couldn't be opened.";
+android_mode.error.cannot_create_avd = "Could not create a virtual device for the emulator.";
+android_mode.error.emulator_installation_failed = "Encountered Issues with the emulator installation. Result Code is non-zero";
+android_mode.dialog.no_devices_found_title = No devices found!
+android_mode.dialog.no_devices_found_body = Processing did not find any device where to run\nyour sketch on. Make sure that your handheld or\nwearable is properly connected to the computer\nand that USB or Bluetooth debugging is enabled.
+android_mode.status.no_devices_found = No devices found.
+android_mode.status.starting_project_build = Starting build...
+android_mode.status.building_project = Building Android project...
+android_mode.status.project_build_failed = Build failed.
+android_mode.status.downloading_emulator = Downloading Emulator...
+android_mode.status.downloading_emulator_successful = Emulator installation was successful.
+
+# ---------------------------------------
+# Android Preprocessor
+
+android_preprocessor.error.cannot_parse_size = More about the size() command on Android can be\nfound here: http://wiki.processing.org/w/Android
+android_preprocessor.error.cannot_parse_size_exception = Could not parse the size() command.
+android_preprocessor.error.cannot_parse_smooth = More about the smooth() command on Android can be\nfound here: http://wiki.processing.org/w/Android
+android_preprocessor.error.cannot_parse_smooth_exception = Could not parse the smooth() command.
+android_preprocessor.warn.cannot_find_smooth_level_title = Could not find smooth level
+android_preprocessor.warn.cannot_find_smooth_level_body = The smooth level of this applet could not automatically\nbe determined from your code. Use only a numeric\nvalue (not variables) for the smooth() command.\nSee the smooth() reference for an explanation.
+
+# ---------------------------------------
+# Android Runner
+
+android_runner.status.waiting_for_device = Waiting for %s to become available...
+android_runner.status.lost_connection_with_device = Lost connection with %s while launching. Try again.
+android_runner.status.cannot_install_sketch = Could not install the sketch.
+android_runner.warn.non_watch_device_title = Selected device is not a watch...
+android_runner.warn.non_watch_device_body = You are trying to install a watch face on a non-watch device.\n" + "Select the correct device, or use the emulator.
+android_runner.warn.watch_device_title = Selected device is a watch...
+android_runner.warn.watch_device_body = You are trying to install a non-watch app on a watch. Select the correct device, or use the emulator.
+android_runner.status.installing_sketch = Installing sketch on %s
+android_runner.status.lost_connection = Lost connection with %s while installing. Try again.
+android_runner.status.sketch_installed = Sketch installed
+android_runner.status.cannot_install_sketch = Could not install the sketch.
+android_runner.status.launching_sketch = Starting sketch on %s
+android_runner.status.sketch_launched= Sketch installed
+android_runner.status.cannot_launch_sketch = Could not start the sketch.
+android_runner.status.in_emulator = in the emulator
+android_runner.status.on_device = on the device
+android_runner.status.cancel_waiting_for_device = No, on second thought, I'm giving up on waiting for that device to show up.
+android_runner.error.cannot_parse_stacktrace = Can't parse this exception line:
+android_runner.status.unknwon_exception = Unknown exception
+
+# ---------------------------------------
+# Android SDK
+
+android_sdk.dialog.found_installed_sdk_title = Found an Android SDK!
+android_sdk.dialog.found_installed_sdk_body = Processing found a valid Android SDK that seems to be in use already. Processing could use this SDK too, or download a new one. Sharing the same SDK across different development tools, like Processing and Android Studio, will save space (the SDK may use up to several GBs), but when one tool updates the SDK, it can create problems in the other. If Processing downloads a new SDK, it will keep it separate from the one it just found. What do you want to do?
+android_sdk.option.use_existing_sdk = Use existing SDK
+android_sdk.option.download_new_sdk = Download new SDK
+android_sdk.dialog.cannot_find_sdk_title = Cannot find an Android SDK...
+android_sdk.dialog.cannot_find_sdk_body = Processing did not find an Android SDK on this computer. If there is one, and you know where it is, click "Locate SDK path" to select it, or "Download SDK" to let Processing download the SDK automatically. If you want to download the SDK manually, you can get the command line tools from here . Make sure to install the SDK platform for API %s.
+android_sdk.dialog.invalid_sdk_title = Android SDK is not valid...
+android_sdk.dialog.invalid_sdk_body = Processing found an Android SDK, but is not valid. It could be missing some files, or might not be including the required platform for API %s. If a valid SDK is available in a different location, click "Locate SDK path" to select it, or "Download SDK" to let Processing download the SDK automatically. If you want to download the SDK manually, you can get the command line tools from here . Make sure to install the SDK platform for API %s.
+android_sdk.option.download_sdk = Download SDK automatically
+android_sdk.option.locate_sdk = Locate SDK path manually
+android_sdk.dialog.download_phone_image_title = Download phone system image?
+android_sdk.dialog.download_phone_image_body = The system image needed by the emulator does not appear to be installed. Do you want Processing to download and install it now?
+android_sdk.dialog.download_watch_image_title = Download watch system image?
+android_sdk.dialog.download_watch_image_body = The system image needed by the emulator does not appear to be installed. Do you want Processing to download and install it now?
+android_sdk.dialog.select_sdk_folder = Choose the location of the Android SDK
+android_sdk.error.sdk_selection_canceled = User canceled attempt to find SDK
+android_sdk.error.sdk_download_canceled = User canceled SDK download
+android_sdk.error.sdk_download_failed = SDK could not be downloaded
+android_sdk.dialog.sdk_installed_title = SDK installed!
+android_sdk.dialog.sdk_installed_body = Processing just downloaded and installed the Android SDK successfully. The Android mode is now ready to use! For documentation, examples, and tutorials, visit the Processing for Android website , and if you updated from version 3 of the mode, check the what's new page .
+android_sdk.dialog.install_usb_driver = If you are planning to use Google Nexus devices, then you need the Google USB Driver to connect them to Processing. You will have to install the driver manually following these instructions . The installation files are available in this folder:%s
+android_sdk.dialog.sdk_license_rejected_title = SDK license not accepted
+android_sdk.dialog.sdk_license_rejected_body = The Android SDK was installed, but will not be usable. You can accept the license at a later time by opening a terminal, changing to the SDK folder, and then running the following command: tools/bin/sdkmanager --licenses
+android_sdk.dialog.32bit_system_title = System is 32 bit...
+android_sdk.dialog.32bit_system_body = The Android SDK no longer includes 32 bit platform tools (adb, etc.), and so they will not work.This thread provides some possible workarounds.
+android_sdk.error.emulator_download_canceled = User canceled emulator download
+android_sdk.error.emulator_download_failed = Emulator could not be downloaded
+android_sdk.dialog.using_existing_sdk_title = SDK configured!
+android_sdk.dialog.using_existing_sdk_body = Processing will use the existing Android SDK. The Android mode is now ready to use! For documentation, examples, and tutorials, visit the Processing for Android website , and if you updated from version 3 of the mode, check the what's new page .
+android_sdk.dialog.accept_sdk_license_title = Accept SDK license?
+android_sdk.dialog.accept_sdk_license_body = You need to accept the terms of the Android SDK license from Google in order to use the SDK. Read the license from here .
+android_sdk.warn.cannot_run_adb_title = Trouble with adb!
+android_sdk.warn.cannot_run_adb_body = Could not run the adb tool from the Android SDK.\nOne possibility is that its executable permission\nis not properly set. You can try setting this\npermission manually, or re-installing the SDK.\n\nThe mode will be disabled until this problem is fixed.\n
+android_sdk.error.missing_sdk_folder = %s does not exist
+android_sdk.error.missing_cmdtools_folder_found_sdktools = There is no cmdline-tools/latest folder in %s and SDK Tools(sdk/tools) got deprecated.\nInstall cmdline-tools in the existing SDK specifically or Create New SDK.
+android_sdk.error.missing_cmdtools_folder = There is no `cmdline-tools/latest` folder in %s \nInstall cmdline-tools in the existing SDK specifically or Create New SDK.
+android_sdk.error.missing_platform_tools_folder = There is no platform-tools folder in %s
+android_sdk.error.missing_build_tools_folder = There is no build-tools folder in %s
+android_sdk.error.missing_platforms_folder = There is no platforms folder in %s
+android_sdk.error.missing_target_platform = There is no Android %s in %s
+android_sdk.error.missing_android_jar = android.jar for plaform %s is missing from %s
+android_sdk.error.missing_emulator = The emulator files are missing
+android_debugger.info.removing_expired_keystore = Removing expired debug.keystore file.
+android_debugger.error.cannot_remove_expired_keystore = Could not remove the expired debug.keystore file.
+android_debugger.error.request_removing_keystore = Please remove the file %s
+android_debugger.error.invalid_keystore_timestamp = The date '%s' could not be parsed.
+android_debugger.error.request_bug_report = Please report this as a bug so we can fix it.
+
+# ---------------------------------------
+# AVD
+
+android_avd.error.cannot_create_avd_title = Could not create the AVD
+android_avd.error.cannot_create_avd_body = The default Android emulator could not be set up. Make sure that the Android SDK is installed properly, and that the system images are installed for level %s. (Between you and me, occasionally, this error is a red herring, and your sketch may be launching shortly.)
+
+android_avd.error.cannot_load_avd_title = Could not load the AVD
+android_avd.error.cannot_load_avd_body = This could mean that the Android tools need to be updated, or that the Processing AVD should be deleted (it will automatically re-created the next time you run Processing). You can use the avdmanager command line tool to create AVDs manually and list the current AVDs.
+
+
+android_avd.error.sdk_wrong_install_title = The SDK is not properly installed
+android_avd.error.sdk_wrong_install_body = Please re-read the installation instructions for Processing found in this online tutorial .
+
+# ---------------------------------------
+# Devices
+
+android_devices.error.cannot_get_device_list = Received unfamiliar output from \u201Cadb devices\u201D.\nThe device list may have errors.
+
+android_devices.error.no_permissions_title = Found devices with no permissions!
+
+android_devices.error.no_permissions_body = Make sure that the device has USB debugging enabled, and that the required USB drivers are installed on Windows, and that permissions are properly configured on Linux. Also, on Linux, don't set the USB configuration to "charging" while debugging. Read this guide on running apps on hardware device for more details.
+
+ private static final String DEVICE_PERMISSIONS_URL =
+ "https://developer.android.com/studio/run/device.html";
+
+ private static final String DEVICE_PERMISSIONS_TITLE =
+ "";
+
+ private static final String DEVICE_PERMISSIONS_MESSAGE =
+
+# ---------------------------------------
+# Keystore manager
+
+keystore_manager.top_label = Please enter the information below so we can generate a private key for you. Fields marked bold are required, though you may consider to fill some of optional fields below those to avoid potential problems. More about private keys can be found here .
+keystore_manager.reset_password = Reset password
+keystore_manager.dialog.reset_keyboard_title = Reset password
+keystore_manager.dialog.reset_keyboard_body_part1 = Are you sure you want to reset the password?
+keystore_manager.dialog.reset_keyboard_body_part2 = We will have to reset the keystore to do this, which means \nyou won't be able to upload an update for your app signed with\nthe new keystore to Google Play.\n\nWe will make a backup for the old keystore.
+keystore_manager.warn.cannot_remove_keystore_title = Android keystore
+keystore_manager.warn.cannot_remove_keystore_body = Failed to remove keystore
+keystore_manager.warn.password_missmatch_title = Passwords
+keystore_manager.warn.password_missmatch_body = Keystore passwords do not match
+keystore_manager.warn.short_password_title = Passwords
+keystore_manager.warn.short_password_body = Keystore password should be at least 6 characters long
+keystore_manager.password_label = Keystore password:
+keystore_manager.repeat_password_label = Repeat keystore password:
+keystore_manager.issuer_credentials_header = Keystore issuer credentials
+keystore_manager.common_name_label = First and last name:
+keystore_manager.organizational_unitl_label = Organizational unit:
+keystore_manager.organization_name_label = Organization name:
+keystore_manager.city_name_label = City or locality:
+keystore_manager.state_name_label = State name:
+keystore_manager.country_code_label = Country code (XX):
+
+# ---------------------------------------
+# Manifest
+
+manifest.warn.cannot_handle_file_title = Error handling %s
+manifest.warn.cannot_handle_file_body = Errors occurred while reading or writing %s\nwhich means lots of things are likely to stop working properly.\nTo prevent losing any data, it's recommended that you use âÂÂSave AsâÂÂ\n"to save a separate copy of your sketch, and then restart Processing.";
+
+# ---------------------------------------
+# Permissions
+
+permissions.dialog.label = Android applications must specifically ask for permission\nto do things like connect to the internet, write a file,\nor make phone calls. When installing your application,\nusers will be asked whether they want to allow such access.
+permissions.dialog.url = More about permissions can be found here .
+
+# ---------------------------------------
+# SDK Downloader
+
+sdk_downloader.error_cannot_find_platform_files = Cannot find the platform files
+sdk_downloader.error_cannot_find_platform_tools = Cannot find the platform-tools
+sdk_downloader.error_cannot_find_build_tools = Cannot find the build-tools
+sdk_downloader.error_cannot_find_tools = Cannot find the tools
+sdk_downloader.error_cannot_find_emulator = Cannot find the emulator
+sdk_downloader.error.cannot_unpack_platform = Error unpacking platform to "%s"
+sdk_downloader.download_title = SDK download
+sdk_downloader.download_sdk_label = Downloading Android SDK...
+
+# ---------------------------------------
+# System image downloader
+
+sys_image_downloader.dialog.select_image_title = Choose system image type to download...
+sys_image_downloader.dialog.select_image_body = The Android emulator requires a system image to run. There are two types of system images available:1) ARM image - slow but compatible with all computers, no extra configuration needed.2) x86 image - fast but compatible only with Intel CPUs, extra configuration may be needed, see this guide for more details.
+sys_image_downloader.dialog.accel_images_title = Some words of caution...
+sys_image_downloader.dialog.haxm_install_body = Processing will install x86 images in the emulator. These images are fast, but also need the Intel Hardware Accelerated Execution Manager (Intel HAXM). Processing will try to run the HAXM installer now, which may ask for your administrator password or additional permissions.
+sys_image_downloader.dialog.kvm_config_body = You chose to run x86 images in the emulator. This is great but you need to configure VM acceleration on Linux using the KVM package. Follow these instructions to configure KVM.
+sys_image_downloader.dialog.ia32libs_title = Additional setup may be required...
+sys_image_downloader.dialog.ia32libs_body = Looks like you are running a 64-bit version of Linux. In order to create the SD card in the emulator, Processing needs the ia32-libs compatibility package. On Ubuntu Linux, you can install it by runing the following command: sudo apt-get install lib32stdc++6
+sys_image_downloader.option.x86_image = Use x86 image
+sys_image_downloader.option.arm_image = Use ARM image
+sys_image_downloader.download_title = System image download
+sys_image_downloader.download_watch_label = Downloading watch system image...
+sys_image_downloader.download_phone_label = Downloading phone system image...
+
+# ---------------------------------------
+# Download strings
+
+download_property.change_event_total = total
+download_property.change_event_downloaded = downloaded
+download_prompt.cancel = Cancel download
+
+# ---------------------------------------
+# SDK Updater tool
+
+sdk_updater.name_column = Package name
+sdk_updater.version_column = Installed version
+sdk_updater.available_column = Available update
+
+sdk_updater.query_message = Querying packages...
+
+sdk_updater.no_updates_message = No updates available
+sdk_updater.one_updates_message = 1 update found!
+sdk_updater.many_updates_message = "%d" updates found!
+
+sdk_updater.warning_failed_finding_package = Failed to find package "%s"
+sdk_updater.warning_failed_computing_dependency_list = Unable to compute a complete list of dependencies.
+
+sdk_updater.refresh_package_message = Refreshing packages...
+sdk_updater.download_package_message = Downloading available updates...
+sdk_updater.download_canceled_message = Download canceled
+
+sdk_updater.update_button_label = Update
+sdk_updater.cancel_button_label = Cancel
+sdk_updater.close_button_label = Close
diff --git a/processing/mode/languages/mode_ko.properties b/processing/mode/languages/mode_ko.properties
new file mode 100644
index 000000000..2890bf447
--- /dev/null
+++ b/processing/mode/languages/mode_ko.properties
@@ -0,0 +1,31 @@
+
+
+# ---------------------------------------
+# KOREAN (ko)
+# ---------------------------------------
+
+
+# ---------------------------------------
+# Menu
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | File |
+
+menu.file.export_signed_package = 서명 된 패키지 내보내기
+menu.file.export_signed_bundle = 안드로이드 번들 내보내기
+menu.file.export_android_project = 안드로이드 프로젝트 내보내기
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Android |
+
+menu.android = 안드로이드
+menu.android.sketch_permissions = 권한 스케치
+menu.android.app = 앱
+menu.android.wallpaper = 벽지
+menu.android.watch_face = 시계 얼굴
+menu.android.vr = VR
+menu.android.ar = AR
+menu.android.devices = 장치들
+menu.android.devices.no_connected_devices = 연결된 기기 없음
+menu.android.sdk_updater = SDK 업데이터
+menu.android.reset_adb = ADB 재설정
diff --git a/processing/mode/languages/mode_zh-Hans.properties b/processing/mode/languages/mode_zh-Hans.properties
new file mode 100644
index 000000000..a43428a19
--- /dev/null
+++ b/processing/mode/languages/mode_zh-Hans.properties
@@ -0,0 +1,328 @@
+
+
+# ---------------------------------------
+# Language: Chinese (zh-Hans)
+# ---------------------------------------
+
+
+# ---------------------------------------
+# Menu
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | File |
+
+menu.sketch.stop = 停止
+menu.file.new = 新建
+menu.file.open = 打开
+menu.file.save = 保存
+
+menu.file.export_signed_package = 导出已签名的软件包
+menu.file.export_signed_bundle = 导出已签名的捆绑包
+menu.file.export_android_project = 导出Android项目
+
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Sketch |
+
+menu.sketch.run_on_device = 运行在设备上
+menu.sketch.run_in_emulator = 在模拟器中运行
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Android |
+
+menu.android = Android
+menu.android.sketch_permissions = 画图权限
+menu.android.app = 应用
+menu.android.wallpaper = 壁纸
+menu.android.watch_face = 手表表盘
+menu.android.vr = VR
+menu.android.ar = AR
+menu.android.devices = 设备
+menu.android.devices.no_connected_devices = 没有连接设备
+menu.android.sdk_updater = SDK 更新器
+menu.android.reset_adb = 重置 ADB
+
+# | File | Edit | Sketch | Android | Tools | Help |
+# | Help |
+
+menu.help.processing_for_android_site = Processing for Android 网站
+menu.help.android_developer_site = Android 开发者网站
+
+# ---------------------------------------
+# Android Build
+
+android_build.error.build_folder = 构建文件夹: %s
+android_build.error.export_file_does_not_exist = “%s” 在 export.txt 中被提及,但它是虚假的,不存在。
+android_build.error.cannot_create_build_folder = 无法创建临时目录“%s”以构建 Android Sketch
+android_build.error.zip_files_not_allowed = Android 库不允许使用 .zip 文件。\n请将 “%s” 重命名为 .jar 文件。
+android_build.error.cannot_copy_icons = 复制图标时出现问题。
+android_build.error.cannot_create_icon_folder = 无法创建 “%s” 文件夹
+android_build.warn.cannot_find_zipalign.title = 找不到 zipalign...
+android_build.warn.cannot_find_zipalign.body = 需要 zipalign 构建工具来准备导出包。\n请确保您的 Android SDK 已正确下载。
+
+# ---------------------------------------
+# Android Debugger
+
+android_debugger.info.attaching_debugger = 正在附加调试器...
+android_debugger.info.debugger_attached = 调试器已附加
+android_debugger.error.debugger_exception = 调试器错误:%s
+
+# ---------------------------------------
+# Android Editor
+
+android_editor.status.exporting_project = 正在导出 Sketch 的 Android 项目...
+android_editor.status.project_export_completed = 项目导出完成。
+android_editor.status.project_export_failed = 项目导出失败。
+android_editor.status.exporting_package = 正在导出已签名的包...
+android_editor.status.package_export_completed = 包导出完成。
+android_editor.status.package_export_failed = 包导出失败。
+android_editor.status.exporting_bundle = 正在导出已签名的 bundle...
+android_editor.status.bundle_export_completed = bundle 导出完成。
+android_editor.status.bundle_export_failed = bundle 导出失败。
+
+android_editor.error.cannot_create_sketch_properties = 创建 Sketch 属性文件“%s”时出错:%s
+
+# ---------------------------------------
+# Android Keystore
+
+android_keystore.warn.cannot_create_folders.title = 文件夹,文件夹,文件夹
+android_keystore.warn.cannot_create_folders.body = 无法创建必要的文件夹以进行构建。\n也许您需要解决一些文件权限问题吗?
+android_keystore.warn.cannot_find_keystore.title = 哦,这出乎意料...
+android_keystore.warn.cannot_find_keystore.body = Keystore 已成功创建,但无法找到。\n难道是意外删除了吗?
+android_keystore.error.cannot_create_keystore = 无法创建 Keystore,原因如下:
+
+# ---------------------------------------
+# Android Mode
+
+android_mode.dialog.watchface_debug_title = 手表是否已连接到计算机?
+android_mode.dialog.watchface_debug_body = Processing 将通过 Wi-Fi 或蓝牙在智能手表上安装手表面板,此时手表需要与手机配对。 阅读有关调试 Android Wear 应用程序 的指南以获取更多详细信息。
+android_mode.dialog.wallpaper_installed_title = 壁纸已安装!
+android_mode.dialog.wallpaper_installed_body = Processing 刚刚将您的 Sketch 构建并作为一款动态壁纸安装在所选设备上。 您需要在设备中打开壁纸选择器,才能将其作为新的背景选择。
+android_mode.dialog.watchface_installed_title = 手表面板已安装!
+android_mode.dialog.watchface_installed_body = Processing 刚刚将您的 Sketch 构建并作为手表面板安装在所选设备上。 您需要将其添加为设备上最喜欢的手表面板,然后在手表面板选择器中选择它才能运行它。
+android_mode.dialog.cannot_export_package_title = 无法完成导出...
+android_mode.dialog.cannot_export_package_body = Sketch 仍然使用默认软件包名称。这不好,因为此名称将永久地唯一标识您的应用程序在 Play 商店中...。想出不同的软件包名称,并在 AndroidManifest.xml 文件中的 Sketch 文件夹中写入“package=”属性内的清单标记之后。一旦您完成此操作,请再次尝试导出 Sketch。 了解有关从 Processing 发布应用程序的更多信息,请查看此在线教程 。
+android_mode.dialog.cannot_use_default_icons_title = 无法完成导出...
+android_mode.dialog.cannot_use_default_icons_body = Sketch 不包含所有所需的应用程序图标。Processing 可以使用其默认的 Android 图标集在您的设备上测试应用程序,但在 Play 商店中分发它是一个糟糕的想法。为您的应用程序创建一组完整的独特图标,并将它们复制到 Sketch 文件夹中。一旦您完成此操作,请再次尝试导出 Sketch。 了解有关从 Processing 发布应用程序的更多信息,请查看此在线教程 。
+android_mode.warn.cannot_load_sdk_title = 坏消息...
+android_mode.warn.cannot_load_sdk_body = 无法加载 Android SDK。\n将禁用 Android Mode。
+android_mode.info.cannot_open_sdk_path = “Android SDK 路径无法打开。”;
+android_mode.error.cannot_create_avd = “无法为模拟器创建虚拟设备。”;
+android_mode.error.emulator_installation_failed = “安装模拟器时遇到问题。结果代码不为零”;
+android_mode.dialog.no_devices_found_title = 找不到设备!
+android_mode.dialog.no_devices_found_body = Processing 没有发现任何可运行 Sketch 的设备。\n确保您的手持设备或可穿戴设备已正确连接到计算机,并且 USB 或蓝牙调试已启用。
+android_mode.status.no_devices_found = 找不到设备。
+android_mode.status.starting_project_build = 开始构建...
+android_mode.status.building_project = 正在构建 Android 项目...
+android_mode.status.project_build_failed = 构建失败。
+android_mode.status.downloading_emulator = 下载模拟器...
+android_mode.status.downloading_emulator_successful = 模拟器安装成功。
+
+# ---------------------------------------
+# Android 预处理器
+
+android_preprocessor.error.cannot_parse_size = 关于在 Android 上使用 size() 命令的更多信息,请参阅此处:http://wiki.processing.org/w/Android
+android_preprocessor.error.cannot_parse_size_exception = 无法解析 size() 命令。
+android_preprocessor.error.cannot_parse_smooth = 关于在 Android 上使用 smooth() 命令的更多信息,请参阅此处:http://wiki.processing.org/w/Android
+android_preprocessor.error.cannot_parse_smooth_exception = 无法解析 smooth() 命令。
+android_preprocessor.warn.cannot_find_smooth_level_title = 找不到平滑级别
+android_preprocessor.warn.cannot_find_smooth_level_body = 该应用程序的平滑级别无法从您的代码中自动确定。\n仅针对 smooth() 命令使用数字值(而非变量)。\n请查看 smooth() 参考文档以获取解释。
+
+# ---------------------------------------
+# Android Runner
+
+android_runner.status.waiting_for_device = 正在等待 %s 可用...
+android_runner.status.lost_connection_with_device = 在启动时与 %s 的连接断开。请重试。
+android_runner.status.cannot_install_sketch = 无法安装 Sketch。
+android_runner.warn.non_watch_device_title = 所选设备不是手表...
+android_runner.warn.non_watch_device_body = 您正在尝试在非手表设备上安装手表面板。请选择正确的设备或使用模拟器。
+android_runner.warn.watch_device_title = 所选设备是手表...
+android_runner.warn.watch_device_body = 您正在尝试在手表上安装非手表应用程序。请选择正确的设备或使用模拟器。
+android_runner.status.installing_sketch = 正在在 %s 上安装 Sketch
+android_runner.status.lost_connection = 在安装时与 %s 的连接断开。请重试。
+android_runner.status.sketch_installed = Sketch 已安装
+android_runner.status.cannot_install_sketch = 无法安装 Sketch。
+android_runner.status.launching_sketch = 正在启动 %s 上的 Sketch
+android_runner.status.sketch_launched= Sketch 已启动
+android_runner.status.cannot_launch_sketch = 无法启动 Sketch。
+android_runner.status.in_emulator = 在模拟器中
+android_runner.status.on_device = 在设备上
+android_runner.status.cancel_waiting_for_device = 不,我改变主意了,我放弃等待那个
+android_runner.error.cannot_parse_stacktrace = 无法解析此异常行:
+android_runner.status.unknwon_exception = 未知异常
+
+# ---------------------------------------
+# Android SDK
+
+android_sdk.dialog.found_installed_sdk_title = 发现 Android SDK!
+android_sdk.dialog.found_installed_sdk_body = Processing 发现了一个有效的、似乎已经在使用的 Android SDK。Processing 也可以使用这个 SDK,或者下载一个新的。 在不同的开发工具(如 Processing 和 Android Studio)之间共享相同的 SDK 将节省空间(SDK 可以使用几个 GB),但当一个工具更新 SDK 时,可能会在另一个工具上出现问题。如果 Processing 下载了一个新的 SDK,它将与刚刚发现的 SDK 分开。 你想怎么办?
+android_sdk.option.use_existing_sdk = 使用现有的 SDK
+android_sdk.option.download_new_sdk = 下载新的 SDK
+android_sdk.dialog.cannot_find_sdk_title = 找不到 Android SDK...
+android_sdk.dialog.cannot_find_sdk_body = Processing 在这台计算机上没有找到 Android SDK。如果有一个 SDK,并且你知道它在哪里,请点击“定位 SDK 路径”选择它,或者点击“下载 SDK”让 Processing 自动下载 SDK。 如果你想手动下载 SDK,可以从这里获取命令行工具:这里 。确保安装 API %s 的 SDK 平台。
+android_sdk.dialog.invalid_sdk_title = Android SDK 无效...
+android_sdk.dialog.invalid_sdk_body = Processing 发现了一个 Android SDK,但是它无效。可能缺少一些文件,或者可能没有包括所需的 API %s 平台。 如果在不同的位置上有一个有效的 SDK,请点击“定位 SDK 路径”选择它,或者点击“下载 SDK”让 Processing 自动下载 SDK。 如果你想手动下载 SDK,可以从这里获取命令行工具:这里 。确保安装 API %s 的 SDK 平台。
+android_sdk.option.download_sdk = 自动下载 SDK
+android_sdk.option.locate_sdk = 手动选择 SDK 路径
+android_sdk.dialog.download_phone_image_title = 下载手机系统镜像?
+android_sdk.dialog.download_phone_image_body = 模拟器需要的系统镜像似乎没有安装。你想让 Processing 现在下载并安装它吗?
+android_sdk.dialog.download_watch_image_title = 下载手表系统镜像?
+android_sdk.dialog.download_watch_image_body = 模拟器需要的系统镜像似乎没有安装。你想让 Processing 现在下载并安装它吗?
+android_sdk.dialog.select_sdk_folder = 选择 Android SDK 的位置
+android_sdk.error.sdk_selection_canceled = 用户取消了查找 SDK 的尝试
+android_sdk.error.sdk_download_canceled = 用户取消了 SDK 下载
+android_sdk.error.sdk_download_failed = 无法下载 SDK
+android_sdk.dialog.sdk_installed_title = SDK 已安装!
+android_sdk.dialog.sdk_installed_body = Processing 刚刚成功下载并安装了 Android SDK。Android 模式现在可以使用了! 有关文档、示例和教程,请访问Processing for Android 网站 ,如果您从模式的第 3 版更新,请查看新内容页面 。
+android_sdk.dialog.install_usb_driver = 如果您计划使用 Google Nexus 设备,则需要 Google USB 驱动程序将它们连接到 Processing。您将不得不按照这些说明 手动安装驱动程序。 安装文件可在此文件夹中找到:%s
+android_sdk.dialog.sdk_license_rejected_title = SDK 许可证未被接受
+android_sdk.dialog.sdk_license_rejected_body = Android SDK 已安装,但不能使用。您可以稍后接受许可证,方法是打开终端,切换到 SDK 文件夹,然后运行以下命令: tools/bin/sdkmanager --licenses
+android_sdk.dialog.32bit_system_title = 系统是 32 位的...
+android_sdk.dialog.32bit_system_body = Android SDK 不再包括 32 位平台工具(adb 等),因此它们将无法工作。这个线程 提供了一些可能的解决方法。
+android_sdk.error.emulator_download_canceled = 用户取消了模拟器下载
+android_sdk.error.emulator_download_failed = 无法下载模拟器
+android_sdk.dialog.using_existing_sdk_title = SDK 配置完成!
+android_sdk.dialog.using_existing_sdk_body = Processing 将使用现有的 Android SDK。Android 模式现在可以使用了! 有关文档、示例和教程,请访问Processing for Android 网站 ,如果您从模式的第 3 版更新,请查看新内容页面 。
+android_sdk.dialog.accept_sdk_license_title = 接受 SDK 许可证?
+android_sdk.dialog.accept_sdk_license_body = 你需要接受 Google 的 Android SDK 许可证条款才能使用 SDK。阅读这里的许可证 。
+android_sdk.warn.cannot_run_adb_title = adb 出了问题!
+android_sdk.warn.cannot_run_adb_body = 无法运行 Android SDK 中的 adb 工具。\n一种可能是它的可执行权限\n没有正确设置。您可以尝试手动设置此\n权限,或重新安装 SDK。\n\n在解决此问题之前,该模式将被禁用。\n
+android_sdk.error.missing_sdk_folder = %s 不存在
+android_sdk.error.missing_cmdtools_folder_found_sdktools = 在%s中没有cmdline-tools/latest文件夹,而SDK工具(sdk/tools)已被弃用。\n请在现有SDK中专门安装cmdline-tools,或创建新的SDK。
+android_sdk.error.missing_cmdtools_folder = 在%s中没有`cmdline-tools/latest`文件夹。\n请在现有SDK中专门安装cmdline-tools,或创建新的SDK。
+android_sdk.error.missing_platform_tools_folder = 在%s中没有platform-tools文件夹。
+android_sdk.error.missing_build_tools_folder = 在%s中没有build-tools文件夹。
+android_sdk.error.missing_platforms_folder = 在%s中没有platforms文件夹。
+android_sdk.error.missing_target_platform = 在%s中没有Android版本%s。
+android_sdk.error.missing_android_jar = 在%s中缺少平台%s的android.jar文件。
+android_sdk.error.missing_emulator = 缺少模拟器文件。
+android_debugger.info.removing_expired_keystore = 正在删除过期的debug.keystore文件。
+android_debugger.error.cannot_remove_expired_keystore = 无法删除过期的debug.keystore文件。
+android_debugger.error.request_removing_keystore = 请删除文件%s。
+android_debugger.error.invalid_keystore_timestamp = 日期“%s”无法解析。
+android_debugger.error.request_bug_report = 请报告此问题,以便我们可以修复它。
+
+# ---------------------------------------
+# AVD
+
+android_avd.error.cannot_create_avd_title = 无法创建AVD(Android虚拟设备)
+android_avd.error.cannot_create_avd_body = 默认的Android模拟器无法设置。请确保Android SDK已正确安装,且系统映像已安装到级别%s。(私下里,偶尔此错误是一个误导,你的sketch可能很快就会启动。)
+
+android_avd.error.cannot_load_avd_title = 无法加载AVD(Android虚拟设备)
+android_avd.error.cannot_load_avd_body = 这可能意味着需要更新Android工具或者Processing AVD应该被删除(下一次运行Processing时,它将自动重新创建)。您可以使用avdmanager命令行工具手动创建AVDs并列出当前AVDs。
+
+
+android_avd.error.sdk_wrong_install_title = SDK(软件开发工具包)未正确安装
+android_avd.error.sdk_wrong_install_body = 请重新阅读Processing的安装说明,该说明在此在线教程 中找到。
+
+# ---------------------------------------
+# Devices
+
+android_devices.error.cannot_get_device_list = “adb devices”收到了陌生的输出。设备列表可能有错误。
+
+android_devices.error.no_permissions_title = 发现未授权的设备!
+
+android_devices.error.no_permissions_body = 确保设备已启用USB调试,并在Windows上安装了所需的USB驱动程序,在Linux上正确配置了权限。此外,在调试期间,在Linux上不要将USB配置设置为“充电”。了解更多详细信息,请阅读有关在硬件设备上运行应用 的指南。
+
+private static final String DEVICE_PERMISSIONS_URL =
+ "https://developer.android.com/studio/run/device.html";
+
+private static final String DEVICE_PERMISSIONS_TITLE =
+ "";
+
+private static final String DEVICE_PERMISSIONS_MESSAGE =
+
+# ---------------------------------------
+# 密钥库管理器
+
+keystore_manager.top_label = 请填写以下信息,以便我们为您生成一个私密密钥。 加粗的字段为必填项,不过您可能要考虑在下面的可选字段中填写一些内容,以避免潜在的问题。 有关私密密钥的更多信息可以在此处 找到。