diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..93c4b27b4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: processing +custom: https://processingfoundation.org/ diff --git a/.gitignore b/.gitignore index 89ea1adfd..073f9ee0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,7 @@ -mode/processing-core.zip -mode/mode/*.jar - -mode/libraries/vr/library -mode/tools/SDKUpdated/tool +.gradle +.idea -studio/.gradle -studio/.idea -studio/gradle -studio/gradlew* +**/examples/**/AndroidManifest.xml **/*.iml **/.DS_Store @@ -18,3 +12,4 @@ studio/gradlew* **/local.properties .gradle +.java-version diff --git a/README.md b/README.md index fb039c407..743c8422c 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,4 @@ 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. -The core and VR libraries are available on JCentral, so they can be easily imported -into Gradle projects: - -[processing-core](https://bintray.com/p5android/processing-android/processing-core) - -[processing-vr](https://bintray.com/p5android/processing-android/processing-vr) - diff --git a/apps/armarkers/build.gradle b/apps/armarkers/build.gradle new file mode 100644 index 000000000..bd89203e2 --- /dev/null +++ b/apps/armarkers/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdkVersion 33 + defaultConfig { + applicationId "processing.tests.armarkers" + minSdkVersion 23 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + namespace 'armarkers' +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + testImplementation 'junit:junit:4.13.2' + implementation project(':libs:processing-core') + implementation project(':libs:processing-ar') + implementation 'androidx.appcompat:appcompat:1.6.0' + implementation 'com.google.ar:core:1.35.0' +} \ No newline at end of file diff --git a/apps/armarkers/gradle.properties b/apps/armarkers/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/apps/armarkers/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/apps/armarkers/src/main/AndroidManifest.xml b/apps/armarkers/src/main/AndroidManifest.xml new file mode 100644 index 000000000..fcf1ffe6b --- /dev/null +++ b/apps/armarkers/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/apps/armarkers/src/main/assets/earth.jpg b/apps/armarkers/src/main/assets/earth.jpg new file mode 100644 index 000000000..73ebe8431 Binary files /dev/null and b/apps/armarkers/src/main/assets/earth.jpg differ diff --git a/apps/armarkers/src/main/java/armarkers/MainActivity.java b/apps/armarkers/src/main/java/armarkers/MainActivity.java new file mode 100644 index 000000000..beaf9990f --- /dev/null +++ b/apps/armarkers/src/main/java/armarkers/MainActivity.java @@ -0,0 +1,111 @@ +package armarkers; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.content.Intent; +import android.provider.Settings; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import processing.android.PFragment; +import processing.android.CompatUtils; +import processing.core.PApplet; + +public class MainActivity extends AppCompatActivity { + private static final int CAMERA_PERMISSION_CODE = 0; + private static boolean CAMERA_PERMISSION_REQUESTED = false; + private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; + private static final String CAMERA_PERMISSION_MESSAGE = "Camera permission is needed to use AR"; + + private PApplet sketch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FrameLayout frame = new FrameLayout(this); + frame.setId(CompatUtils.getUniqueViewId()); + setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + sketch = new Sketch(); + PFragment fragment = new PFragment(sketch); + fragment.setView(frame, this); + } + + @Override + protected void onResume() { + super.onResume(); + if (!hasCameraPermission()) requestCameraPermission(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (!hasCameraPermission()) { + Toast.makeText(this, CAMERA_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); + if (!shouldShowRequestPermissionRationale()) { + launchPermissionSettings(); + } + finish(); + } + + if (sketch != null) { + sketch.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + CAMERA_PERMISSION_REQUESTED = false; + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (sketch != null) { + sketch.onNewIntent(intent); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (sketch != null) { + sketch.onActivityResult(requestCode, resultCode, data); + } + } + + @Override + public void onBackPressed() { + if (sketch != null) { + sketch.onBackPressed(); + } + } + + private boolean hasCameraPermission() { + int res = ContextCompat.checkSelfPermission(this, CAMERA_PERMISSION); + return res == PackageManager.PERMISSION_GRANTED; + } + + private void requestCameraPermission() { + if (!CAMERA_PERMISSION_REQUESTED) { + CAMERA_PERMISSION_REQUESTED = true; + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_CODE); + } + } + + private boolean shouldShowRequestPermissionRationale() { + return ActivityCompat.shouldShowRequestPermissionRationale(this, CAMERA_PERMISSION); + } + + private void launchPermissionSettings() { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", this.getPackageName(), null)); + this.startActivity(intent); + } +} diff --git a/apps/armarkers/src/main/java/armarkers/Sketch.java b/apps/armarkers/src/main/java/armarkers/Sketch.java new file mode 100644 index 000000000..c6e51856c --- /dev/null +++ b/apps/armarkers/src/main/java/armarkers/Sketch.java @@ -0,0 +1,51 @@ +package armarkers; + +import java.util.ArrayList; + +import processing.ar.*; +import processing.core.PApplet; +import processing.core.PImage; +import processing.core.PShape; + +public class Sketch extends PApplet { + ARTracker tracker; + ARAnchor anchor; + PShape earth; + + public void settings() { + fullScreen(AR); + } + + public void setup() { + fullScreen(AR); + + tracker = new ARTracker(this); + + PImage earthImg = loadImage("earth.jpg"); + tracker.start(); + tracker.addImage("earth", earthImg); + + earth = createShape(SPHERE, 0.15f); + } + + public void draw() { + lights(); + + if (mousePressed) { + // Create new anchor at the current touch point + if (anchor != null) anchor.dispose(); + ARTrackable hit = tracker.get(mouseX, mouseY); + if (hit != null && hit.isImage() && hit.getName().equals("earth")) { + anchor = new ARAnchor(hit); + } + else anchor = null; + } + + if (anchor != null) { + anchor.attach(); + shape(earth); + anchor.detach(); + } + } + +} diff --git a/studio/apps/vrcube/src/main/res/layout/main.xml b/apps/armarkers/src/main/res/layout/main.xml similarity index 100% rename from studio/apps/vrcube/src/main/res/layout/main.xml rename to apps/armarkers/src/main/res/layout/main.xml diff --git a/studio/apps/fast2d/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/armarkers/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from studio/apps/fast2d/src/main/res/mipmap-hdpi/ic_launcher.png rename to apps/armarkers/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/studio/apps/fast2d/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/armarkers/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from studio/apps/fast2d/src/main/res/mipmap-mdpi/ic_launcher.png rename to apps/armarkers/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/studio/apps/fast2d/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/armarkers/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from studio/apps/fast2d/src/main/res/mipmap-xhdpi/ic_launcher.png rename to apps/armarkers/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/studio/apps/fast2d/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/armarkers/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/fast2d/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to apps/armarkers/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/studio/apps/fast2d/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/armarkers/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/fast2d/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to apps/armarkers/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/apps/armarkers/src/main/res/values/strings.xml b/apps/armarkers/src/main/res/values/strings.xml new file mode 100644 index 000000000..f57f444bd --- /dev/null +++ b/apps/armarkers/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + AR Test + diff --git a/apps/armarkers/src/main/res/values/styles.xml b/apps/armarkers/src/main/res/values/styles.xml new file mode 100644 index 000000000..375954d4a --- /dev/null +++ b/apps/armarkers/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/arscene/build.gradle b/apps/arscene/build.gradle new file mode 100644 index 000000000..ea151d4d9 --- /dev/null +++ b/apps/arscene/build.gradle @@ -0,0 +1,35 @@ +plugins { + id 'com.android.application' +} + +android { + defaultConfig { + applicationId "processing.tests.arscene" + minSdkVersion 23 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + namespace 'arscene' +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + testImplementation 'junit:junit:4.13.2' + implementation project(':libs:processing-core') + implementation project(':libs:processing-ar') + implementation 'androidx.appcompat:appcompat:1.6.0' + implementation 'com.google.ar:core:1.35.0' +} diff --git a/apps/arscene/gradle.properties b/apps/arscene/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/apps/arscene/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/apps/arscene/src/main/AndroidManifest.xml b/apps/arscene/src/main/AndroidManifest.xml new file mode 100644 index 000000000..fcf1ffe6b --- /dev/null +++ b/apps/arscene/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/apps/arscene/src/main/java/arscene/MainActivity.java b/apps/arscene/src/main/java/arscene/MainActivity.java new file mode 100644 index 000000000..8e1837290 --- /dev/null +++ b/apps/arscene/src/main/java/arscene/MainActivity.java @@ -0,0 +1,111 @@ +package arscene; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.content.Intent; +import android.provider.Settings; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import processing.android.PFragment; +import processing.android.CompatUtils; +import processing.core.PApplet; + +public class MainActivity extends AppCompatActivity { + private static final int CAMERA_PERMISSION_CODE = 0; + private static boolean CAMERA_PERMISSION_REQUESTED = false; + private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; + private static final String CAMERA_PERMISSION_MESSAGE = "Camera permission is needed to use AR"; + + private PApplet sketch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FrameLayout frame = new FrameLayout(this); + frame.setId(CompatUtils.getUniqueViewId()); + setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + sketch = new Sketch(); + PFragment fragment = new PFragment(sketch); + fragment.setView(frame, this); + } + + @Override + protected void onResume() { + super.onResume(); + if (!hasCameraPermission()) requestCameraPermission(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (!hasCameraPermission()) { + Toast.makeText(this, CAMERA_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); + if (!shouldShowRequestPermissionRationale()) { + launchPermissionSettings(); + } + finish(); + } + + if (sketch != null) { + sketch.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + CAMERA_PERMISSION_REQUESTED = false; + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (sketch != null) { + sketch.onNewIntent(intent); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (sketch != null) { + sketch.onActivityResult(requestCode, resultCode, data); + } + } + + @Override + public void onBackPressed() { + if (sketch != null) { + sketch.onBackPressed(); + } + } + + private boolean hasCameraPermission() { + int res = ContextCompat.checkSelfPermission(this, CAMERA_PERMISSION); + return res == PackageManager.PERMISSION_GRANTED; + } + + private void requestCameraPermission() { + if (!CAMERA_PERMISSION_REQUESTED) { + CAMERA_PERMISSION_REQUESTED = true; + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_CODE); + } + } + + private boolean shouldShowRequestPermissionRationale() { + return ActivityCompat.shouldShowRequestPermissionRationale(this, CAMERA_PERMISSION); + } + + private void launchPermissionSettings() { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", this.getPackageName(), null)); + this.startActivity(intent); + } +} diff --git a/apps/arscene/src/main/java/arscene/Sketch.java b/apps/arscene/src/main/java/arscene/Sketch.java new file mode 100644 index 000000000..cf01c9b38 --- /dev/null +++ b/apps/arscene/src/main/java/arscene/Sketch.java @@ -0,0 +1,103 @@ +package arscene; + +import java.util.ArrayList; + +import processing.ar.*; +import processing.core.PApplet; + +public class Sketch extends PApplet { + ARTracker tracker; + ARAnchor touchAnchor; + ArrayList trackAnchors; + float angle; + + public void settings() { + fullScreen(AR); + } + + public void setup() { + tracker = new ARTracker(this); + tracker.start(); + trackAnchors = new ArrayList(); + } + + public void draw() { + // The AR Core session, frame and camera can be accessed through Processing's surface object + // to obtain the full information about the AR scene: +// ARSurface surface = (ARSurface) getSurface(); +// surface.camera.getPose(); +// surface.frame.getLightEstimate(); + + lights(); + + if (mousePressed) { + // Create new anchor at the current touch point + if (touchAnchor != null) touchAnchor.dispose(); + ARTrackable hit = tracker.get(mouseX, mouseY); + if (hit != null) touchAnchor = new ARAnchor(hit); + else touchAnchor = null; + } + + // Draw objects attached to each anchor + for (ARAnchor anchor : trackAnchors) { + if (anchor.isTracking()) drawBox(anchor, 255, 255, 255); + + // It is very important to dispose anchors once they are no longer tracked. + if (anchor.isStopped()) anchor.dispose(); + } + if (touchAnchor != null) drawBox(touchAnchor, 255, 0, 0); + + // Conveniency function in the tracker object to remove disposed anchors from a list + tracker.clearAnchors(trackAnchors); + + // Draw trackable planes + for (int i = 0; i < tracker.count(); i++) { + ARTrackable trackable = tracker.get(i); + if (!trackable.isTracking()) continue; + + pushMatrix(); + trackable.transform(); + if (mousePressed && trackable.isSelected(mouseX, mouseY)) { + fill(255, 0, 0, 100); + } else { + fill(255, 100); + } + beginShape(); + float[] points = trackable.getPolygon(); + for (int n = 0; n < points.length / 2; n++) { + float x = points[2 * n]; + float z = points[2 * n + 1]; + vertex(x, 0, z); + } + endShape(); + popMatrix(); + } + + angle += 0.1; + } + + public void drawBox(ARAnchor anchor, int r, int g, int b) { + anchor.attach(); + fill(r, g, b); + rotateY(angle); + box(0.15f); + anchor.detach(); + } + + public void trackableEvent(ARTrackable t) { + if (trackAnchors.size() < 10) { + float x0 = 0, y0 = 0; + if (t.isWallPlane()) { + // The new trackable is a wall, so adding the anchor 0.3 meters to its side + x0 = 0.3f; + } else if (t.isFloorPlane()) { + // The new trackable is a floor plane, so adding the anchor 0.3 meters above it + y0 = 0.3f; + } else { + // The new trackable is a floor plane, so adding the anchor 0.3 meters below it + y0 = -0.3f; + } + trackAnchors.add(new ARAnchor(t, x0, y0, 0)); + } + } +} diff --git a/apps/arscene/src/main/res/layout/main.xml b/apps/arscene/src/main/res/layout/main.xml new file mode 100644 index 000000000..4b602d5f6 --- /dev/null +++ b/apps/arscene/src/main/res/layout/main.xml @@ -0,0 +1,7 @@ + diff --git a/studio/apps/simple/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/arscene/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from studio/apps/simple/src/main/res/mipmap-hdpi/ic_launcher.png rename to apps/arscene/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/studio/apps/simple/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/arscene/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from studio/apps/simple/src/main/res/mipmap-mdpi/ic_launcher.png rename to apps/arscene/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/studio/apps/simple/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/arscene/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from studio/apps/simple/src/main/res/mipmap-xhdpi/ic_launcher.png rename to apps/arscene/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/studio/apps/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/arscene/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to apps/arscene/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/studio/apps/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/arscene/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to apps/arscene/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/apps/arscene/src/main/res/values/strings.xml b/apps/arscene/src/main/res/values/strings.xml new file mode 100644 index 000000000..f57f444bd --- /dev/null +++ b/apps/arscene/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + AR Test + diff --git a/apps/arscene/src/main/res/values/styles.xml b/apps/arscene/src/main/res/values/styles.xml new file mode 100644 index 000000000..375954d4a --- /dev/null +++ b/apps/arscene/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + diff --git a/studio/apps/fast2d/build.gradle b/apps/fast2d/build.gradle similarity index 63% rename from studio/apps/fast2d/build.gradle rename to apps/fast2d/build.gradle index 0eb962e88..9a69129be 100644 --- a/studio/apps/fast2d/build.gradle +++ b/apps/fast2d/build.gradle @@ -1,11 +1,12 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' +} android { - compileSdkVersion 26 defaultConfig { applicationId "processing.tests.fast2d" minSdkVersion 17 - targetSdkVersion 26 + targetSdkVersion 33 versionCode 1 versionName "1.0" } @@ -18,14 +19,15 @@ android { productFlavors { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } + namespace 'fast2d' } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' implementation project(':libs:processing-core') - implementation 'com.android.support:appcompat-v7:26.0.2' + implementation 'androidx.appcompat:appcompat:1.6.0' } diff --git a/apps/fast2d/gradle.properties b/apps/fast2d/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/apps/fast2d/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/studio/apps/fast2d/src/main/AndroidManifest.xml b/apps/fast2d/src/main/AndroidManifest.xml similarity index 81% rename from studio/apps/fast2d/src/main/AndroidManifest.xml rename to apps/fast2d/src/main/AndroidManifest.xml index b36c2301f..5204a615a 100644 --- a/studio/apps/fast2d/src/main/AndroidManifest.xml +++ b/apps/fast2d/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + - + diff --git a/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl b/apps/fast2d/src/main/assets/blur.glsl similarity index 100% rename from mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl rename to apps/fast2d/src/main/assets/blur.glsl diff --git a/mode/examples/Basics/Shape/DisableStyle/data/bot1.svg b/apps/fast2d/src/main/assets/bot1.svg similarity index 100% rename from mode/examples/Basics/Shape/DisableStyle/data/bot1.svg rename to apps/fast2d/src/main/assets/bot1.svg diff --git a/apps/fast2d/src/main/assets/edges.glsl b/apps/fast2d/src/main/assets/edges.glsl new file mode 100644 index 000000000..42d4383ba --- /dev/null +++ b/apps/fast2d/src/main/assets/edges.glsl @@ -0,0 +1,36 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; +varying vec2 vertTexCoord; +varying float vertTexFactor; + +uniform sampler2D texture; +uniform vec2 texScale; + +void main(void) { + vec2 tc0 = vertTexCoord.st + vec2(-texScale.s, -texScale.t); + vec2 tc1 = vertTexCoord.st + vec2( 0.0, -texScale.t); + vec2 tc2 = vertTexCoord.st + vec2(+texScale.s, -texScale.t); + vec2 tc3 = vertTexCoord.st + vec2(-texScale.s, 0.0); + vec2 tc4 = vertTexCoord.st + vec2( 0.0, 0.0); + vec2 tc5 = vertTexCoord.st + vec2(+texScale.s, 0.0); + vec2 tc6 = vertTexCoord.st + vec2(-texScale.s, +texScale.t); + vec2 tc7 = vertTexCoord.st + vec2( 0.0, +texScale.t); + vec2 tc8 = vertTexCoord.st + vec2(+texScale.s, +texScale.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 = 8.0 * col4 - (col0 + col1 + col2 + col3 + col5 + col6 + col7 + col8); + gl_FragColor = vec4(sum.rgb, 1.0) * vertColor; +} diff --git a/apps/fast2d/src/main/assets/frag.glsl b/apps/fast2d/src/main/assets/frag.glsl new file mode 100644 index 000000000..94fd39b36 --- /dev/null +++ b/apps/fast2d/src/main/assets/frag.glsl @@ -0,0 +1,10 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 vertColor; + +void main() { + gl_FragColor = vertColor; +} diff --git a/studio/apps/fast2d/src/main/assets/img.png b/apps/fast2d/src/main/assets/img.png similarity index 100% rename from studio/apps/fast2d/src/main/assets/img.png rename to apps/fast2d/src/main/assets/img.png diff --git a/mode/examples/Topics/Shaders/EdgeDetect/data/leaves.jpg b/apps/fast2d/src/main/assets/leaves.jpg similarity index 100% rename from mode/examples/Topics/Shaders/EdgeDetect/data/leaves.jpg rename to apps/fast2d/src/main/assets/leaves.jpg diff --git a/apps/fast2d/src/main/assets/vert.glsl b/apps/fast2d/src/main/assets/vert.glsl new file mode 100644 index 000000000..6c0069068 --- /dev/null +++ b/apps/fast2d/src/main/assets/vert.glsl @@ -0,0 +1,16 @@ +uniform mat4 transform; + +attribute vec3 position; +attribute vec4 color; + +varying vec4 vertColor; + +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; +} diff --git a/apps/fast2d/src/main/java/fast2d/MainActivity.java b/apps/fast2d/src/main/java/fast2d/MainActivity.java new file mode 100644 index 000000000..c164255fa --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/MainActivity.java @@ -0,0 +1,104 @@ +package fast2d; + +import android.os.Bundle; +import android.content.Intent; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import androidx.appcompat.app.AppCompatActivity; + +import processing.android.PFragment; +import processing.android.CompatUtils; +import processing.core.PApplet; + +public class MainActivity extends AppCompatActivity { +// private int TEST = 1; // Basic self-intersecting polygon +// private int TEST = 2; // Mouse controlled polygon +// private int TEST = 3; // Textured poly +// private int TEST = 4; // Text rendering + private int TEST = 5; // Shapes benchmark +// private int TEST = 6; // Duplicated vertex +// private int TEST = 7; // User-defined contours +// private int TEST = 8; // Primitive types +// private int TEST = 9; // Arc test +// private int TEST = 10; // Arc test +// private int TEST = 11; // Load and display SVG +// private int TEST = 12; // Filter test +// private int TEST = 13; // Custom shader test (texture) +// private int TEST = 14; // Custom shader test (no texture) + + private PApplet sketch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FrameLayout frame = new FrameLayout(this); + frame.setId(CompatUtils.getUniqueViewId()); + setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + if (TEST == 1) { + sketch = new SketchBasicPoly(); + } else if (TEST == 2) { + sketch = new SketchMousePoly(); + } else if (TEST == 3) { + sketch = new SketchTexturedPoly(); + } else if (TEST == 4) { + sketch = new SketchDisplayText(); + } else if (TEST == 5) { + sketch = new SketchShapeBenchmark(); + } else if (TEST == 6) { + sketch = new SketchDuplicatedVert(); + } else if (TEST == 7) { + sketch = new SketchUserDefinedContours(); + } else if (TEST == 8) { + sketch = new SketchPrimitiveTypes(); + } else if (TEST == 9) { + sketch = new SketchArcTest(); + } else if (TEST == 10) { + sketch = new SketchCurveTest(); + } else if (TEST == 11) { + sketch = new SketchLoadDisplaySVG(); + } else if (TEST == 12) { + sketch = new SketchFilterTest(); + } else if (TEST == 13) { + sketch = new SketchCustomShader(); + } else if (TEST == 14) { + sketch = new SketchShaderNoTex(); + } + + PFragment fragment = new PFragment(sketch); + fragment.setView(frame, this); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (sketch != null) { + sketch.onRequestPermissionsResult( + requestCode, permissions, grantResults); + } + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (sketch != null) { + sketch.onNewIntent(intent); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (sketch != null) { + sketch.onActivityResult(requestCode, resultCode, data); + } + } + + @Override + public void onBackPressed() { + if (sketch != null) { + sketch.onBackPressed(); + } + } +} diff --git a/studio/apps/fast2d/src/main/java/fast2d/Sketch.java b/apps/fast2d/src/main/java/fast2d/Sketch.java similarity index 97% rename from studio/apps/fast2d/src/main/java/fast2d/Sketch.java rename to apps/fast2d/src/main/java/fast2d/Sketch.java index f2972e9d4..4bd118d34 100644 --- a/studio/apps/fast2d/src/main/java/fast2d/Sketch.java +++ b/apps/fast2d/src/main/java/fast2d/Sketch.java @@ -13,11 +13,6 @@ public class Sketch extends PApplet { boolean keyboard = false; - - static final String P2DX = "processing.opengl.PGraphics2DX"; -// static final String P2DX = P2D; - - boolean wireframe = false; int join = MITER, cap = SQUARE, mode = OPEN; @@ -47,7 +42,7 @@ public void setup() { // orientation(LANDSCAPE); //pardon the silly image - img = loadImage("balmer_developers_poster.png"); + img = loadImage("leaves.jpg"); font = createFont("SansSerif", displayDensity * 72); //setup for demo 2 @@ -81,7 +76,7 @@ public void draw() { // println("FRAME #" + frameCount); // println(); - if (frameCount % 10 == 0) println((int)frameRate + " fps"); + if (frameCount % 10 == 0) println((int) frameRate + " fps"); strokeCap(cap); strokeJoin(join); @@ -548,9 +543,9 @@ public void keyPressed() { } else if (key == 'f') { mode = CLOSE; } else if (key == 't') { - PGraphics2DX.premultiplyMatrices = true; +// PGraphics2DX.premultiplyMatrices = true; } else if (key == 'g') { - PGraphics2DX.premultiplyMatrices = false; +// PGraphics2DX.premultiplyMatrices = false; } else if (key == ' ') { // PJOGL pgl = (PJOGL)((PGraphics2D)this.g).pgl; // if (wireframe) diff --git a/apps/fast2d/src/main/java/fast2d/SketchArcTest.java b/apps/fast2d/src/main/java/fast2d/SketchArcTest.java new file mode 100644 index 000000000..d873db96c --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchArcTest.java @@ -0,0 +1,52 @@ +package fast2d; + +import processing.core.PApplet; +import processing.core.PVector; + +public class SketchArcTest extends PApplet { + float weight = 1; + + int join = MITER; + int cap = SQUARE; + int mode = OPEN; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + strokeWeight(4 * displayDensity); + stroke(127, 0, 0); + fill(255, 255, 255); + + //testing the behavior of floating point % operator (for dealing with angles) + float py = 0; + for (int i = 0; i < width; ++i) { + float x = (i - width/2) * 0.1f; + float y = height/2 - (x % PI) * 10; + line(i, y, i - 1, py); + py = y; + } + + //testing the behavior of P2D arc() at various angles + //NOTE: arcs with negative angle aren't drawn + arc(100, 100, 100, 100, -1, new PVector(mouseX, mouseY).sub(100, 100).heading()); + + //test for whether LINES primitive type has self-overlap + //NOTE: it does in JAVA2D, but not in P2D + stroke(0, 127, 127, 127); + beginShape(LINES); + vertex(0, 0); + vertex(width, height + 100); + vertex(width, 0); + vertex(0, height + 100); + endShape(); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchBasicPoly.java b/apps/fast2d/src/main/java/fast2d/SketchBasicPoly.java new file mode 100644 index 000000000..2d92ed81b --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchBasicPoly.java @@ -0,0 +1,48 @@ +package fast2d; + +import processing.core.PApplet; + +public class SketchBasicPoly extends PApplet { + float weight = 1; + + int join = MITER; + int cap = SQUARE; + int mode = OPEN; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + fill(255, 0, 63, 127); + translate(100, 200); + + strokeWeight(6 * weight * displayDensity); + stroke(0, 127, 95, 191); + beginShape(); + vertex(100, 200); + vertex(200, 100); + vertex(300, 200); + vertex(400, 100); + vertex(350, 200); + vertex(450, 100); + + vertex(300, 300); + vertex(mouseX, mouseY); + vertex(600, 200); + + vertex(550, 100); + vertex(550, 400); + vertex(750, 400); + vertex(750, 600); + vertex(100, 600); + endShape(mode); + } +} \ No newline at end of file diff --git a/apps/fast2d/src/main/java/fast2d/SketchCurveTest.java b/apps/fast2d/src/main/java/fast2d/SketchCurveTest.java new file mode 100644 index 000000000..55c1b391a --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchCurveTest.java @@ -0,0 +1,62 @@ +package fast2d; + +import processing.core.PApplet; +import processing.opengl.PGraphics2DX; + +public class SketchCurveTest extends PApplet { + int join = MITER; + int cap = SQUARE; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + //these cause errors in P4D because we haven't implemented them yet + //so they're disabled in the demo for now + if (getGraphics() instanceof PGraphics2DX) { + return; + } + + noFill(); + stroke(0); + strokeWeight(4 * displayDensity); + pushMatrix(); + scale(2); + + beginShape(); + curveVertex(84, 91); + curveVertex(84, 91); + curveVertex(68, 19); + curveVertex(21, 17); + curveVertex(32, 100); + curveVertex(32, 100); + endShape(); + + translate(100, 0); + + beginShape(); + vertex(30, 20); + bezierVertex(80, 0, 80, 75, 30, 75); + bezierVertex(50, 80, 60, 25, 30, 20); + endShape(); + + translate(100, 0); + + beginShape(); + vertex(20, 20); + quadraticVertex(80, 20, 50, 50); + quadraticVertex(20, 80, 80, 80); + vertex(80, 60); + endShape(); + + popMatrix(); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchCustomShader.java b/apps/fast2d/src/main/java/fast2d/SketchCustomShader.java new file mode 100644 index 000000000..d8036d06e --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchCustomShader.java @@ -0,0 +1,36 @@ +package fast2d; + +import processing.core.PApplet; +import processing.core.PImage; +import processing.opengl.PShader; + +public class SketchCustomShader extends PApplet { + PShader edges; + PImage img; + boolean enabled = true; + + public void settings() { +// fullScreen(P2D); + fullScreen(P2DX); + } + + public void setup() { + orientation(LANDSCAPE); + img = loadImage("leaves.jpg"); + edges = loadShader("edges.glsl"); + } + + public void draw() { + if (enabled == true) { + shader(edges); + } + image(img, 0, 0, width, height); + } + + public void mousePressed() { + enabled = !enabled; + if (!enabled) { + resetShader(); + } + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchDisplayText.java b/apps/fast2d/src/main/java/fast2d/SketchDisplayText.java new file mode 100644 index 000000000..b79c08b02 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchDisplayText.java @@ -0,0 +1,27 @@ +package fast2d; + +import processing.core.PApplet; +import processing.core.PFont; + +public class SketchDisplayText extends PApplet { + PFont font; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + font = createFont("SansSerif", displayDensity * 72); + } + + public void draw() { + background(255); + + textFont(font); + text("Now is the time for all good men to come to the aid of their country.\n" + + "If they do not the quick brown fox may never jump over the lazy sleeping dog again.\n" + + "He may, however, take up knitting as a suitable hobby for all retired quick brown foxes.\n" + + "This is test #1 of 9,876,543,210.\n" + + "Collect them all!", 0, 100); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchDuplicatedVert.java b/apps/fast2d/src/main/java/fast2d/SketchDuplicatedVert.java new file mode 100644 index 000000000..f3093f81d --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchDuplicatedVert.java @@ -0,0 +1,32 @@ +package fast2d; + +import processing.core.PApplet; + +public class SketchDuplicatedVert extends PApplet { + int join = MITER; + int cap = SQUARE; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + //NOTE: yes, this produces the wrong result in P4D + //see PGraphics4D.shapeVertex() for why + beginShape(); + vertex(500, 300); + vertex(600, 400); //dupe + vertex(700, 300); + vertex(650, 300); + vertex(600, 400); //dupe + vertex(550, 300); + endShape(CLOSE); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchFilterTest.java b/apps/fast2d/src/main/java/fast2d/SketchFilterTest.java new file mode 100644 index 000000000..3b73ccc83 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchFilterTest.java @@ -0,0 +1,27 @@ +package fast2d; + +import processing.core.PApplet; +import processing.opengl.PShader; + +public class SketchFilterTest extends PApplet { + PShader blur; + + public void settings() { +// fullScreen(P2D); + fullScreen(P2DX); + } + + public void setup() { +// orientation(LANDSCAPE); + blur = loadShader("blur.glsl"); + stroke(255, 0, 0); + rectMode(CENTER); + strokeWeight(5 * displayDensity); + } + + public void draw() { + filter(blur); + rect(mouseX, mouseY, 150, 150); + ellipse(mouseX, mouseY, 100, 100); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchLoadDisplaySVG.java b/apps/fast2d/src/main/java/fast2d/SketchLoadDisplaySVG.java new file mode 100644 index 000000000..aa146fb59 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchLoadDisplaySVG.java @@ -0,0 +1,22 @@ +package fast2d; + +import processing.core.PApplet; +import processing.core.PShape; + +public class SketchLoadDisplaySVG extends PApplet { + PShape bot; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + bot = loadShape("bot1.svg"); + } + + public void draw() { + background(102); + shape(bot, 110, 90, 100, 100); // Draw at coordinate (110, 90) at size 100 x 100 + shape(bot, 280, 40); // Draw at coordinate (280, 40) at the default size + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchMousePoly.java b/apps/fast2d/src/main/java/fast2d/SketchMousePoly.java new file mode 100644 index 000000000..39e5e9a15 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchMousePoly.java @@ -0,0 +1,52 @@ +package fast2d; + +import java.util.ArrayList; + +import processing.core.PApplet; +import processing.core.PVector; + +public class SketchMousePoly extends PApplet { + float weight = 1; + + //data for demo 2 + int[] c = new int[4096]; + ArrayList points = new ArrayList(); + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + //setup for demo 2 + for (int i = 0; i < c.length; ++i) { + c[i] = color(random(255), random(255), random(255)); + } + } + + public void draw() { + background(255); + + noStroke(); + + //NOTE: we draw each vertex with a random fill color to test how it behaves. + //in P2D, the colors are interpolated across the triangles output by the GLU tessellator. + //in JAVA2D, when endShape() is called, the currently active color is used for all vertices. + //for now P4D follows the behavior of P2D, but switching to JAVA2D's behavior + //would allow us to simplify our implementation a bit + beginShape(); + for (int i = 0; i < points.size(); ++i) { + fill(c[i]); + vertex(points.get(i).x, points.get(i).y); + } + endShape(); + } + + public void mousePressed() { + points.add(new PVector(mouseX, mouseY)); + } + + public void mouseDragged() { + points.get(points.size() - 1).x = mouseX; + points.get(points.size() - 1).y = mouseY; + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchPrimitiveTypes.java b/apps/fast2d/src/main/java/fast2d/SketchPrimitiveTypes.java new file mode 100644 index 000000000..b14593c31 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchPrimitiveTypes.java @@ -0,0 +1,182 @@ +package fast2d; + +import processing.core.PApplet; + +public class SketchPrimitiveTypes extends PApplet { + float weight = 1; + + int join = MITER; + int cap = SQUARE; + int mode = OPEN; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + //from https://processing.org/reference/beginShape_.html + + stroke(0); + strokeWeight(4 * displayDensity); + fill(255, 127, 127); + + pushMatrix(); + resetMatrix(); + scale(2); + + beginShape(); + vertex(30, 20); + vertex(85, 20); + vertex(85, 75); + vertex(30, 75); + endShape(CLOSE); + + translate(100, 0); + + beginShape(POINTS); + vertex(30, 20); + vertex(85, 20); + vertex(85, 75); + vertex(30, 75); + endShape(); + + translate(100, 0); + + beginShape(LINES); + vertex(30, 40); + vertex(85, 20); + vertex(85, 75); + vertex(30, 75); + endShape(); + + translate(100, 0); + + pushStyle(); + noFill(); + beginShape(); + vertex(30, 20); + vertex(85, 20); + vertex(85, 75); + vertex(30, 75); + endShape(); + popStyle(); + + translate(100, 0); + + pushStyle(); + noFill(); + beginShape(); + vertex(30, 20); + vertex(85, 20); + vertex(85, 75); + vertex(30, 75); + endShape(CLOSE); + popStyle(); + + translate(100, 0); + + beginShape(TRIANGLES); + vertex(30, 75); + vertex(40, 20); + vertex(50, 75); + vertex(60, 20); + vertex(70, 75); + vertex(80, 20); + endShape(); + + resetMatrix(); + scale(2); + translate(0, 100); + + beginShape(TRIANGLE_STRIP); + vertex(30, 75); + vertex(40, 20); + vertex(50, 75); + vertex(60, 20); + vertex(70, 75); + vertex(80, 20); + vertex(90, 75); + endShape(); + + translate(100, 0); + + beginShape(TRIANGLE_FAN); + vertex(57.5f, 50); + vertex(57.5f, 15); + vertex(92, 50); + vertex(57.5f, 85); + vertex(22, 50); + vertex(57.5f, 15); + endShape(); + + translate(100, 0); + + beginShape(QUADS); + vertex(30, 20); + vertex(30, 75); + vertex(50, 75); + vertex(50, 20); + vertex(65, 20); + vertex(65, 75); + vertex(85, 75); + vertex(85, 20); + endShape(); + + translate(100, 0); + + beginShape(QUAD_STRIP); + vertex(30, 20); + vertex(30, 75); + vertex(50, 20); + vertex(50, 75); + vertex(65, 20); + vertex(65, 75); + vertex(85, 20); + vertex(85, 75); + endShape(); + + translate(100, 0); + + beginShape(); + vertex(20, 20); + vertex(40, 20); + vertex(40, 40); + vertex(60, 40); + vertex(60, 60); + vertex(20, 60); + endShape(CLOSE); + + //test handling of concave and self-intersecting quads + //NOTE: JAVA2D currently draws these correctly, but P2D does not + resetMatrix(); + scale(2); + translate(0, 200); + strokeWeight(2 * displayDensity); + float t = frameCount * 0.01f; + + beginShape(QUADS); + vertex(50, 10); + vertex(90, 50); + vertex(30 + 20*sin(t), 70 + 20*cos(t)); + vertex(30 - 20*sin(t), 70 - 20*cos(t)); + endShape(CLOSE); + + translate(100, 0); + + beginShape(QUAD_STRIP); + vertex(50, 10); + vertex(90, 50); + vertex(30 + 20*sin(t), 70 + 20*cos(t)); + vertex(30 - 20*sin(t), 70 - 20*cos(t)); + endShape(CLOSE); + + popMatrix(); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchShaderNoTex.java b/apps/fast2d/src/main/java/fast2d/SketchShaderNoTex.java new file mode 100644 index 000000000..5abfb4c70 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchShaderNoTex.java @@ -0,0 +1,24 @@ +package fast2d; + +import processing.core.PApplet; +import processing.opengl.PShader; + +public class SketchShaderNoTex extends PApplet { + PShader sh; + + public void settings() { +// fullScreen(P2D); + fullScreen(P2DX); + } + + public void setup() { + orientation(LANDSCAPE); + sh = loadShader("frag.glsl", "vert.glsl"); + shader(sh); + } + + public void draw() { + translate(mouseX, mouseY); + rect(0, 0, 400, 400); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchShapeBenchmark.java b/apps/fast2d/src/main/java/fast2d/SketchShapeBenchmark.java new file mode 100644 index 000000000..8f21dae08 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchShapeBenchmark.java @@ -0,0 +1,70 @@ +package fast2d; + +import processing.core.PApplet; +import processing.opengl.PGraphics2DX; + +public class SketchShapeBenchmark extends PApplet { + 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 }; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + 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); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchTexturedPoly.java b/apps/fast2d/src/main/java/fast2d/SketchTexturedPoly.java new file mode 100644 index 000000000..97d8ff292 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchTexturedPoly.java @@ -0,0 +1,47 @@ +package fast2d; + +import processing.core.PApplet; +import processing.core.PImage; + +public class SketchTexturedPoly extends PApplet { + PImage img; + + int join = MITER; + int cap = SQUARE; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + img = loadImage("leaves.jpg"); + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + translate(100, 200); + + //test that textured shapes and tint() work correctly + float s = 4; + beginShape(); + texture(img); + vertex(10*s, 20*s, 0, 0); + tint(0, 255, 127, 127); + vertex(80*s, 5*s, 800, 0); + vertex(95*s, 90*s, 800, 800); + noTint(); + vertex(40*s, 95*s, 0, 800); + endShape(); + + //test that image() function works correctly + tint(255, 31); + rotate(-1); + image(img, -200, 100); + rotate(1); + tint(255); + image(img, 700, 100, 200, 100); + } +} diff --git a/apps/fast2d/src/main/java/fast2d/SketchUserDefinedContours.java b/apps/fast2d/src/main/java/fast2d/SketchUserDefinedContours.java new file mode 100644 index 000000000..1b4635cc3 --- /dev/null +++ b/apps/fast2d/src/main/java/fast2d/SketchUserDefinedContours.java @@ -0,0 +1,41 @@ +package fast2d; + +import processing.core.PApplet; + +public class SketchUserDefinedContours extends PApplet { + + int join = MITER; + int cap = SQUARE; + + public void settings() { + fullScreen(P2DX); + } + + public void setup() { + strokeCap(cap); + strokeJoin(join); + } + + public void draw() { + background(255); + + //from https://processing.org/reference/beginContour_.html + + fill(127, 255, 127); + stroke(255, 0, 0); + beginShape(); + // Exterior part of shape, clockwise winding + vertex(-40, -40); + vertex(40, -40); + vertex(40, 40); + vertex(-40, 40); + // Interior part of shape, counter-clockwise winding + beginContour(); + vertex(-20, -20); + vertex(-20, 20); + vertex(20, 20); + vertex(20, -20); + endContour(); + endShape(CLOSE); + } +} diff --git a/studio/apps/fast2d/src/main/res/layout/activity_main.xml b/apps/fast2d/src/main/res/layout/activity_main.xml similarity index 100% rename from studio/apps/fast2d/src/main/res/layout/activity_main.xml rename to apps/fast2d/src/main/res/layout/activity_main.xml diff --git a/studio/apps/vrcube/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/fast2d/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from studio/apps/vrcube/src/main/res/mipmap-hdpi/ic_launcher.png rename to apps/fast2d/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/studio/apps/vrcube/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/fast2d/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from studio/apps/vrcube/src/main/res/mipmap-mdpi/ic_launcher.png rename to apps/fast2d/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/studio/apps/vrcube/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/fast2d/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from studio/apps/vrcube/src/main/res/mipmap-xhdpi/ic_launcher.png rename to apps/fast2d/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/studio/apps/vrcube/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/fast2d/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/vrcube/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to apps/fast2d/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/studio/apps/vrcube/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/fast2d/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/vrcube/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to apps/fast2d/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/studio/apps/fast2d/src/main/res/values-w820dp/dimens.xml b/apps/fast2d/src/main/res/values-w820dp/dimens.xml similarity index 100% rename from studio/apps/fast2d/src/main/res/values-w820dp/dimens.xml rename to apps/fast2d/src/main/res/values-w820dp/dimens.xml diff --git a/studio/apps/fast2d/src/main/res/values/colors.xml b/apps/fast2d/src/main/res/values/colors.xml similarity index 100% rename from studio/apps/fast2d/src/main/res/values/colors.xml rename to apps/fast2d/src/main/res/values/colors.xml diff --git a/studio/apps/fast2d/src/main/res/values/dimens.xml b/apps/fast2d/src/main/res/values/dimens.xml similarity index 100% rename from studio/apps/fast2d/src/main/res/values/dimens.xml rename to apps/fast2d/src/main/res/values/dimens.xml diff --git a/studio/apps/fast2d/src/main/res/values/strings.xml b/apps/fast2d/src/main/res/values/strings.xml similarity index 100% rename from studio/apps/fast2d/src/main/res/values/strings.xml rename to apps/fast2d/src/main/res/values/strings.xml diff --git a/studio/apps/fast2d/src/main/res/values/styles.xml b/apps/fast2d/src/main/res/values/styles.xml similarity index 100% rename from studio/apps/fast2d/src/main/res/values/styles.xml rename to apps/fast2d/src/main/res/values/styles.xml diff --git a/apps/simple/build.gradle b/apps/simple/build.gradle new file mode 100644 index 000000000..6310d85d3 --- /dev/null +++ b/apps/simple/build.gradle @@ -0,0 +1,35 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'processing.tests.simple' + compileSdkVersion 33 + + defaultConfig { + applicationId "processing.tests.simple" + minSdkVersion 17 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + testImplementation 'junit:junit:4.13.2' + implementation 'androidx.appcompat:appcompat:1.6.1' + + implementation project(':libs:processing-core') +} diff --git a/apps/simple/proguard-rules.pro b/apps/simple/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/apps/simple/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/studio/apps/simple/src/main/AndroidManifest.xml b/apps/simple/src/main/AndroidManifest.xml similarity index 80% rename from studio/apps/simple/src/main/AndroidManifest.xml rename to apps/simple/src/main/AndroidManifest.xml index d132cc39d..f4ca7c1cd 100644 --- a/studio/apps/simple/src/main/AndroidManifest.xml +++ b/apps/simple/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + - + diff --git a/studio/apps/simple/src/main/assets/leaf.png b/apps/simple/src/main/assets/leaf.png similarity index 100% rename from studio/apps/simple/src/main/assets/leaf.png rename to apps/simple/src/main/assets/leaf.png diff --git a/studio/apps/simple/src/main/java/simple/MainActivity.java b/apps/simple/src/main/java/simple/MainActivity.java similarity index 84% rename from studio/apps/simple/src/main/java/simple/MainActivity.java rename to apps/simple/src/main/java/simple/MainActivity.java index abd9278f9..7328c802f 100644 --- a/studio/apps/simple/src/main/java/simple/MainActivity.java +++ b/apps/simple/src/main/java/simple/MainActivity.java @@ -4,7 +4,7 @@ import android.content.Intent; import android.view.ViewGroup; import android.widget.FrameLayout; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import processing.android.PFragment; import processing.android.CompatUtils; @@ -28,14 +28,16 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (sketch != null) { sketch.onRequestPermissionsResult( - requestCode, permissions, grantResults); + requestCode, permissions, grantResults); } } @Override public void onNewIntent(Intent intent) { + super.onNewIntent(intent); if (sketch != null) { sketch.onNewIntent(intent); } @@ -43,6 +45,7 @@ public void onNewIntent(Intent intent) { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (sketch != null) { sketch.onActivityResult(requestCode, resultCode, data); } diff --git a/studio/apps/simple/src/main/java/simple/Sketch.java b/apps/simple/src/main/java/simple/Sketch.java similarity index 100% rename from studio/apps/simple/src/main/java/simple/Sketch.java rename to apps/simple/src/main/java/simple/Sketch.java diff --git a/studio/apps/simple/src/main/res/layout/activity_main.xml b/apps/simple/src/main/res/layout/activity_main.xml similarity index 100% rename from studio/apps/simple/src/main/res/layout/activity_main.xml rename to apps/simple/src/main/res/layout/activity_main.xml diff --git a/studio/apps/wallpaper/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/simple/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from studio/apps/wallpaper/src/main/res/mipmap-hdpi/ic_launcher.png rename to apps/simple/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/studio/apps/wallpaper/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/simple/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from studio/apps/wallpaper/src/main/res/mipmap-mdpi/ic_launcher.png rename to apps/simple/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/studio/apps/wallpaper/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/simple/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from studio/apps/wallpaper/src/main/res/mipmap-xhdpi/ic_launcher.png rename to apps/simple/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/studio/apps/wallpaper/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/wallpaper/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to apps/simple/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/studio/apps/wallpaper/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/wallpaper/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to apps/simple/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/studio/apps/simple/src/main/res/values-w820dp/dimens.xml b/apps/simple/src/main/res/values-w820dp/dimens.xml similarity index 100% rename from studio/apps/simple/src/main/res/values-w820dp/dimens.xml rename to apps/simple/src/main/res/values-w820dp/dimens.xml diff --git a/studio/apps/simple/src/main/res/values/colors.xml b/apps/simple/src/main/res/values/colors.xml similarity index 100% rename from studio/apps/simple/src/main/res/values/colors.xml rename to apps/simple/src/main/res/values/colors.xml diff --git a/studio/apps/simple/src/main/res/values/dimens.xml b/apps/simple/src/main/res/values/dimens.xml similarity index 100% rename from studio/apps/simple/src/main/res/values/dimens.xml rename to apps/simple/src/main/res/values/dimens.xml diff --git a/studio/apps/simple/src/main/res/values/strings.xml b/apps/simple/src/main/res/values/strings.xml similarity index 100% rename from studio/apps/simple/src/main/res/values/strings.xml rename to apps/simple/src/main/res/values/strings.xml diff --git a/studio/apps/simple/src/main/res/values/styles.xml b/apps/simple/src/main/res/values/styles.xml similarity index 100% rename from studio/apps/simple/src/main/res/values/styles.xml rename to apps/simple/src/main/res/values/styles.xml diff --git a/studio/apps/vrcube/build.gradle b/apps/vrcube/build.gradle similarity index 57% rename from studio/apps/vrcube/build.gradle rename to apps/vrcube/build.gradle index 22109bc8c..b062a918f 100644 --- a/studio/apps/vrcube/build.gradle +++ b/apps/vrcube/build.gradle @@ -1,11 +1,12 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' +} android { - compileSdkVersion 26 defaultConfig { applicationId "processing.tests.vrcube" minSdkVersion 19 - targetSdkVersion 26 + targetSdkVersion 33 versionCode 1 versionName "1.0" } @@ -18,17 +19,18 @@ android { productFlavors { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } + namespace 'vrcube' } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' implementation project(':libs:processing-core') + implementation project(':libs:google-vr') implementation project(':libs:processing-vr') - implementation 'com.android.support:appcompat-v7:26.0.2' - implementation 'com.android.support:design:26.0.2' - implementation 'com.google.vr:sdk-base:1.150.0' + implementation 'com.google.protobuf.nano:protobuf-javanano:3.1.0' + implementation 'androidx.appcompat:appcompat:1.6.0' } diff --git a/apps/vrcube/gradle.properties b/apps/vrcube/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/apps/vrcube/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/studio/apps/vrcube/src/main/AndroidManifest.xml b/apps/vrcube/src/main/AndroidManifest.xml similarity index 86% rename from studio/apps/vrcube/src/main/AndroidManifest.xml rename to apps/vrcube/src/main/AndroidManifest.xml index ae44159fd..03513aece 100644 --- a/studio/apps/vrcube/src/main/AndroidManifest.xml +++ b/apps/vrcube/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + @@ -12,9 +12,10 @@ android:label="@string/app_name" android:theme="@style/VrActivityTheme"> + android:screenOrientation="landscape" + android:exported="true"> diff --git a/apps/vrcube/src/main/java/vrcube/MainActivity.java b/apps/vrcube/src/main/java/vrcube/MainActivity.java new file mode 100644 index 000000000..22ef6a835 --- /dev/null +++ b/apps/vrcube/src/main/java/vrcube/MainActivity.java @@ -0,0 +1,23 @@ +package vrcube; + +import android.os.Build; +import android.os.Bundle; +import android.view.WindowManager; + +import processing.vr.VRActivity; +import processing.core.PApplet; + +public class MainActivity extends VRActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + + PApplet sketch = new Sketch(); + setSketch(sketch); + } +} \ No newline at end of file diff --git a/apps/vrcube/src/main/java/vrcube/Sketch.java b/apps/vrcube/src/main/java/vrcube/Sketch.java new file mode 100644 index 000000000..5c7ee3f59 --- /dev/null +++ b/apps/vrcube/src/main/java/vrcube/Sketch.java @@ -0,0 +1,181 @@ +package vrcube; + +import processing.core.PApplet; +import processing.core.PMatrix3D; +import processing.core.PVector; +import processing.vr.*; + +public class Sketch extends PApplet { + float boxSize = 140; + VRCamera vrcam; + Selector vrsel; + + public void settings() { + fullScreen(VR); + } + + public void setup() { + vrcam = new VRCamera(this); + vrsel = new Selector(this); +// vrcam.setNear(1000); +// vrcam.setFar(1100); + } + + public void draw() { + vrsel.update(); + background(120); + translate(width/2, height/2); + lights(); + drawGrid(); + drawAim(); + } + + void drawGrid() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float x = map(i, 0, 3, -350, +350); + float y = map(j, 0, 3, -350, +350); + pushMatrix(); + translate(x, y); + rotateY(millis()/1000.0f); + if (vrsel.hit(boxSize)) { + strokeWeight(5); + stroke(0xFF2FB1EA); + if (mousePressed) { + fill(0xFF2FB1EA); + } else { + fill(0xFFE3993E); + } + } else { + noStroke(); + fill(0xFFE3993E); + } + box(boxSize); + popMatrix(); + } + } + } + + void drawAim() { + vrcam.sticky(); + stroke(47, 177, 234, 150); + strokeWeight(50); + point(0, 0, 100); + vrcam.noSticky(); + } + + class Selector { + protected PApplet parent; + + protected PVector dir = new PVector(); + protected PVector cam = new PVector(); + + protected PMatrix3D eyeMat = new PMatrix3D(); + protected PMatrix3D objMat = new PMatrix3D(); + + protected PVector front = new PVector(); + protected PVector objCam = new PVector(); + protected PVector objFront = new PVector(); + protected PVector objDir = new PVector(); + + protected PVector hit = new PVector(); + + public Selector(PApplet parent) { + this.parent = parent; + } + + public void update() { + parent.getEyeMatrix(eyeMat); + cam.set(eyeMat.m03, eyeMat.m13, eyeMat.m23); + dir.set(eyeMat.m02, eyeMat.m12, eyeMat.m22); + PVector.add(cam, dir, front); + } + + public boolean hit(PMatrix3D mat, float boxSize) { + objMat.set(mat); + return hitImpl(boxSize); + } + + public boolean hit(float boxSize) { + parent.getObjectMatrix(objMat); + return hitImpl(boxSize); + } + + protected boolean hitImpl(float boxSize) { + objMat.mult(cam, objCam); + objMat.mult(front, objFront); + PVector.sub(objFront, objCam, objDir); + PVector boxMin = new PVector(-boxSize/2, -boxSize/2, -boxSize/2); + PVector boxMax = new PVector(+boxSize/2, +boxSize/2, +boxSize/2); + return intersectsLine(objCam, objDir, boxMin, boxMax, 0, 1000, hit); + } + + protected boolean intersectsLine(PVector orig, PVector dir, + PVector minPos, PVector maxPos, float minDist, float maxDist, PVector hit) { + PVector bbox; + PVector invDir = new PVector(1/dir.x, 1/dir.y, 1/dir.z); + + boolean signDirX = invDir.x < 0; + boolean signDirY = invDir.y < 0; + boolean signDirZ = invDir.z < 0; + + bbox = signDirX ? maxPos : minPos; + float txmin = (bbox.x - orig.x) * invDir.x; + bbox = signDirX ? minPos : maxPos; + float txmax = (bbox.x - orig.x) * invDir.x; + bbox = signDirY ? maxPos : minPos; + float tymin = (bbox.y - orig.y) * invDir.y; + bbox = signDirY ? minPos : maxPos; + float tymax = (bbox.y - orig.y) * invDir.y; + + if ((txmin > tymax) || (tymin > txmax)) { + return false; + } + if (tymin > txmin) { + txmin = tymin; + } + if (tymax < txmax) { + txmax = tymax; + } + + bbox = signDirZ ? maxPos : minPos; + float tzmin = (bbox.z - orig.z) * invDir.z; + bbox = signDirZ ? minPos : maxPos; + float tzmax = (bbox.z - orig.z) * invDir.z; + + if ((txmin > tzmax) || (tzmin > txmax)) { + return false; + } + if (tzmin > txmin) { + txmin = tzmin; + } + if (tzmax < txmax) { + txmax = tzmax; + } + if ((txmin < maxDist) && (txmax > minDist)) { + hit.x = orig.x + txmin * dir.x; + hit.y = orig.y + txmin * dir.y; + hit.z = orig.z + txmin * dir.z; + return true; + } + return false; + } + } + +/* + public void settings() { + fullScreen(STEREO); + } + + public void setup() { } + + public void draw() { + background(157); + lights(); + translate(width / 2, height / 2); + rotateX(frameCount * 0.01f); + rotateY(frameCount * 0.01f); + box(350); + } +*/ +} diff --git a/apps/vrcube/src/main/res/layout/main.xml b/apps/vrcube/src/main/res/layout/main.xml new file mode 100644 index 000000000..4b602d5f6 --- /dev/null +++ b/apps/vrcube/src/main/res/layout/main.xml @@ -0,0 +1,7 @@ + diff --git a/studio/apps/watchface/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/vrcube/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from studio/apps/watchface/src/main/res/mipmap-hdpi/ic_launcher.png rename to apps/vrcube/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/studio/apps/watchface/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/vrcube/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from studio/apps/watchface/src/main/res/mipmap-mdpi/ic_launcher.png rename to apps/vrcube/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/studio/apps/watchface/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/vrcube/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from studio/apps/watchface/src/main/res/mipmap-xhdpi/ic_launcher.png rename to apps/vrcube/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/studio/apps/watchface/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/vrcube/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from studio/apps/watchface/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to apps/vrcube/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/apps/vrcube/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/vrcube/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..aee44e138 Binary files /dev/null and b/apps/vrcube/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/studio/apps/vrcube/src/main/res/values/strings.xml b/apps/vrcube/src/main/res/values/strings.xml similarity index 100% rename from studio/apps/vrcube/src/main/res/values/strings.xml rename to apps/vrcube/src/main/res/values/strings.xml diff --git a/studio/apps/vrcube/src/main/res/values/styles.xml b/apps/vrcube/src/main/res/values/styles.xml similarity index 100% rename from studio/apps/vrcube/src/main/res/values/styles.xml rename to apps/vrcube/src/main/res/values/styles.xml diff --git a/studio/apps/wallpaper/build.gradle b/apps/wallpaper/build.gradle similarity index 63% rename from studio/apps/wallpaper/build.gradle rename to apps/wallpaper/build.gradle index bdf5e0a18..aec131e52 100644 --- a/studio/apps/wallpaper/build.gradle +++ b/apps/wallpaper/build.gradle @@ -1,11 +1,12 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' +} android { - compileSdkVersion 26 defaultConfig { applicationId "processing.tests.wallpaper" minSdkVersion 17 - targetSdkVersion 26 + targetSdkVersion 33 versionCode 1 versionName "1.0" } @@ -18,14 +19,15 @@ android { productFlavors { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } + namespace 'wallpaper' } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' implementation project(':libs:processing-core') - implementation 'com.android.support:appcompat-v7:26.0.2' -} + implementation 'androidx.appcompat:appcompat:1.6.0' +} \ No newline at end of file diff --git a/apps/wallpaper/gradle.properties b/apps/wallpaper/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/apps/wallpaper/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/studio/apps/wallpaper/src/main/AndroidManifest.xml b/apps/wallpaper/src/main/AndroidManifest.xml similarity index 69% rename from studio/apps/wallpaper/src/main/AndroidManifest.xml rename to apps/wallpaper/src/main/AndroidManifest.xml index 3e10a48ed..37695c4da 100644 --- a/studio/apps/wallpaper/src/main/AndroidManifest.xml +++ b/apps/wallpaper/src/main/AndroidManifest.xml @@ -1,15 +1,19 @@ - + - + - + diff --git a/studio/apps/wallpaper/src/main/java/wallpaper/DebuggerEntryPointActivity.java b/apps/wallpaper/src/main/java/wallpaper/DebuggerEntryPointActivity.java similarity index 85% rename from studio/apps/wallpaper/src/main/java/wallpaper/DebuggerEntryPointActivity.java rename to apps/wallpaper/src/main/java/wallpaper/DebuggerEntryPointActivity.java index 3d4ef3959..ca80b0079 100644 --- a/studio/apps/wallpaper/src/main/java/wallpaper/DebuggerEntryPointActivity.java +++ b/apps/wallpaper/src/main/java/wallpaper/DebuggerEntryPointActivity.java @@ -2,7 +2,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; public class DebuggerEntryPointActivity extends Activity { @Override diff --git a/studio/apps/wallpaper/src/main/java/wallpaper/MainService.java b/apps/wallpaper/src/main/java/wallpaper/MainService.java similarity index 100% rename from studio/apps/wallpaper/src/main/java/wallpaper/MainService.java rename to apps/wallpaper/src/main/java/wallpaper/MainService.java diff --git a/studio/apps/wallpaper/src/main/java/wallpaper/Sketch.java b/apps/wallpaper/src/main/java/wallpaper/Sketch.java similarity index 100% rename from studio/apps/wallpaper/src/main/java/wallpaper/Sketch.java rename to apps/wallpaper/src/main/java/wallpaper/Sketch.java diff --git a/studio/apps/wallpaper/src/main/res/layout/main.xml b/apps/wallpaper/src/main/res/layout/main.xml similarity index 100% rename from studio/apps/wallpaper/src/main/res/layout/main.xml rename to apps/wallpaper/src/main/res/layout/main.xml diff --git a/apps/wallpaper/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/wallpaper/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..cde69bccc Binary files /dev/null and b/apps/wallpaper/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/apps/wallpaper/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/wallpaper/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..c133a0cbd Binary files /dev/null and b/apps/wallpaper/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/apps/wallpaper/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/wallpaper/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..bfa42f0e7 Binary files /dev/null and b/apps/wallpaper/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/apps/wallpaper/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/wallpaper/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..324e72cdd Binary files /dev/null and b/apps/wallpaper/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/apps/wallpaper/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/wallpaper/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..aee44e138 Binary files /dev/null and b/apps/wallpaper/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/studio/apps/wallpaper/src/main/res/values-w820dp/dimens.xml b/apps/wallpaper/src/main/res/values-w820dp/dimens.xml similarity index 78% rename from studio/apps/wallpaper/src/main/res/values-w820dp/dimens.xml rename to apps/wallpaper/src/main/res/values-w820dp/dimens.xml index 63fc81644..a2d24bc10 100644 --- a/studio/apps/wallpaper/src/main/res/values-w820dp/dimens.xml +++ b/apps/wallpaper/src/main/res/values-w820dp/dimens.xml @@ -2,5 +2,5 @@ - 64dp - + + \ No newline at end of file diff --git a/apps/wallpaper/src/main/res/values/dimens.xml b/apps/wallpaper/src/main/res/values/dimens.xml new file mode 100644 index 000000000..9cfe70043 --- /dev/null +++ b/apps/wallpaper/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + \ No newline at end of file diff --git a/studio/apps/wallpaper/src/main/res/values/strings.xml b/apps/wallpaper/src/main/res/values/strings.xml similarity index 100% rename from studio/apps/wallpaper/src/main/res/values/strings.xml rename to apps/wallpaper/src/main/res/values/strings.xml diff --git a/studio/apps/wallpaper/src/main/res/xml/wallpaper.xml b/apps/wallpaper/src/main/res/xml/wallpaper.xml similarity index 100% rename from studio/apps/wallpaper/src/main/res/xml/wallpaper.xml rename to apps/wallpaper/src/main/res/xml/wallpaper.xml diff --git a/studio/apps/watchface/build.gradle b/apps/watchface/build.gradle similarity index 50% rename from studio/apps/watchface/build.gradle rename to apps/watchface/build.gradle index 64396a315..212b286f8 100644 --- a/studio/apps/watchface/build.gradle +++ b/apps/watchface/build.gradle @@ -1,11 +1,12 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' +} android { - compileSdkVersion 26 defaultConfig { applicationId "processing.tests.watchface" minSdkVersion 25 - targetSdkVersion 26 + targetSdkVersion 33 versionCode 1 versionName "1.0" multiDexEnabled true @@ -19,20 +20,17 @@ android { productFlavors { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } + namespace 'watchface' } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' implementation project(':libs:processing-core') - implementation 'com.android.support:palette-v7:26.0.2' - implementation 'com.android.support:support-v4:26.0.2' - implementation 'com.google.android.gms:play-services-wearable:11.0.4' - implementation 'com.android.support:percent:26.0.2' - implementation 'com.android.support:recyclerview-v7:26.0.2' - implementation 'com.google.android.support:wearable:2.1.0' - compileOnly 'com.google.android.wearable:wearable:2.1.0' + implementation 'com.google.android.gms:play-services-wearable:18.0.0' + implementation 'com.google.android.support:wearable:2.9.0' + compileOnly 'com.google.android.wearable:wearable:2.9.0' } diff --git a/apps/watchface/gradle.properties b/apps/watchface/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/apps/watchface/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/studio/apps/watchface/src/main/AndroidManifest.xml b/apps/watchface/src/main/AndroidManifest.xml similarity index 85% rename from studio/apps/watchface/src/main/AndroidManifest.xml rename to apps/watchface/src/main/AndroidManifest.xml index 3ed6fa21c..e6f990184 100644 --- a/studio/apps/watchface/src/main/AndroidManifest.xml +++ b/apps/watchface/src/main/AndroidManifest.xml @@ -1,11 +1,14 @@ - + - + diff --git a/studio/apps/watchface/src/main/java/watchface/MainService.java b/apps/watchface/src/main/java/watchface/MainService.java similarity index 100% rename from studio/apps/watchface/src/main/java/watchface/MainService.java rename to apps/watchface/src/main/java/watchface/MainService.java diff --git a/studio/apps/watchface/src/main/java/watchface/Sketch.java b/apps/watchface/src/main/java/watchface/Sketch.java similarity index 100% rename from studio/apps/watchface/src/main/java/watchface/Sketch.java rename to apps/watchface/src/main/java/watchface/Sketch.java diff --git a/studio/apps/watchface/src/main/res/drawable-nodpi/bg.png b/apps/watchface/src/main/res/drawable-nodpi/bg.png similarity index 100% rename from studio/apps/watchface/src/main/res/drawable-nodpi/bg.png rename to apps/watchface/src/main/res/drawable-nodpi/bg.png diff --git a/studio/apps/watchface/src/main/res/drawable-nodpi/preview_analog.png b/apps/watchface/src/main/res/drawable-nodpi/preview_analog.png similarity index 100% rename from studio/apps/watchface/src/main/res/drawable-nodpi/preview_analog.png rename to apps/watchface/src/main/res/drawable-nodpi/preview_analog.png diff --git a/studio/apps/watchface/src/main/res/layout/main.xml b/apps/watchface/src/main/res/layout/main.xml similarity index 100% rename from studio/apps/watchface/src/main/res/layout/main.xml rename to apps/watchface/src/main/res/layout/main.xml diff --git a/apps/watchface/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/watchface/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..cde69bccc Binary files /dev/null and b/apps/watchface/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/apps/watchface/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/watchface/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..c133a0cbd Binary files /dev/null and b/apps/watchface/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/apps/watchface/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/watchface/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..bfa42f0e7 Binary files /dev/null and b/apps/watchface/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/apps/watchface/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/watchface/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..324e72cdd Binary files /dev/null and b/apps/watchface/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/studio/apps/watchface/src/main/res/values/strings.xml b/apps/watchface/src/main/res/values/strings.xml similarity index 100% rename from studio/apps/watchface/src/main/res/values/strings.xml rename to apps/watchface/src/main/res/values/strings.xml diff --git a/studio/apps/watchface/src/main/res/xml/watch_face.xml b/apps/watchface/src/main/res/xml/watch_face.xml similarity index 100% rename from studio/apps/watchface/src/main/res/xml/watch_face.xml rename to apps/watchface/src/main/res/xml/watch_face.xml diff --git a/build.gradle b/build.gradle index 8684f0725..5bb77278f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,126 +1,55 @@ -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; - -apply plugin: 'java' +// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath group: 'commons-io', name: 'commons-io', version: '2.5' - classpath group: 'org.zeroturnaround', name: 'zt-zip', version: '1.9' + classpath 'com.android.tools.build:gradle:8.0.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files } } allprojects { - apply plugin: 'java' - apply plugin: 'java-library' - - // Versions of all dependencies - ext.targetSdkVersion = '26' - ext.supportLibsVersion = '26.0.2' - ext.wearVersion = '2.1.0' - ext.gvrVersion = '1.150.0' - ext.processingVersion = '3.3.7' - ext.toolingVersion = '4.3' - ext.slf4jVersion = '1.7.10' - ext.gradlewVersion = '4.4.1' - ext.toolsLibVersion = '26.0.0-dev' + repositories { + // This was was added to address an issue in JCenter with some Android packages (https://stackoverflow.com/questions/50563338/could-not-find-runtime-jar-android-arch-lifecycleruntime1-0-0/50564224). + // JCenter is no longer used but keep it just in case. + maven { url "https://maven.google.com" } - Properties modeProperties = new Properties() - modeProperties.load(project.rootProject.file("mode/mode.properties").newDataInputStream()) - ext.modeVersion = modeProperties.getProperty("prettyVersion") + // Apparently needed by AndroidX dependencies + maven { url "https://jitpack.io" } - Properties vrProperties = new Properties() - vrProperties.load(project.rootProject.file("mode/libraries/vr/library.properties").newDataInputStream()) - ext.vrLibVersion = vrProperties.getProperty("prettyVersion") + // Needed to get google-vr dependencies + maven { url 'https://repo.gradle.org/gradle/libs-releases' } + mavenCentral() + google() + } - 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" + // Set Java compatibility for all projects + plugins.withType(JavaPlugin).configureEach { + java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) } - } 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.androidToolsLibPath = "${sdkDir}/tools/lib" - - ext.coreZipPath = "${rootDir}/mode/processing-core.zip" - - repositories { - google() - jcenter() - flatDir dirs: androidPlatformPath - flatDir dirs: androidToolsLibPath - flatDir dirs: "${rootDir}/core/dist" + // Set Android compatibility for all Android projects + plugins.withType(com.android.build.gradle.BasePlugin).configureEach { + android { + compileSdkVersion 33 + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + } } - - sourceCompatibility = 1.7 - targetCompatibility = 1.7 -} - -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/mode"), file("${root}/mode")) - FileUtils.copyDirectory(file("mode/theme"), file("${root}/theme")) - - Files.copy(file("mode/processing-core.zip").toPath(), - file("${root}/processing-core.zip").toPath(), REPLACE_EXISTING); - - Files.copy(file("mode/mode.properties").toPath(), - file("${root}/mode.properties").toPath(), REPLACE_EXISTING); - - FileUtils.copyDirectory(file("mode/tools/SDKUpdater/tool"), - file("${root}/tools/SDKUpdater/tool")) - 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/src"), - file("${root}/libraries/vr/src")) - Files.copy(file("mode/libraries/vr/library.properties").toPath(), - file("${root}/libraries/vr/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); - } -} +tasks.register('clean', Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle deleted file mode 100644 index e0c4fdc68..000000000 --- a/core/build.gradle +++ /dev/null @@ -1,162 +0,0 @@ -import com.android.build.gradle.internal.dependency.ExtractAarTransform -import com.android.build.gradle.internal.dependency.AarTransform -import com.android.build.gradle.internal.publishing.AndroidArtifacts -import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType -import com.google.common.collect.ImmutableList -import org.gradle.api.artifacts.transform.ArtifactTransform -import org.gradle.api.artifacts.type.ArtifactTypeDefinition -import java.util.regex.Pattern - -import static org.gradle.api.internal.artifacts.ArtifactAttributes.ARTIFACT_FORMAT - -import java.nio.file.Files -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - -apply plugin: 'maven' - -/** - * Custom aar configuration needed to use aar files as dependencies in a pure java - * library project, lifted from the following repo: - * https://github.com/nekocode/Gradle-Import-Aar - */ -configurations { - aar { - attributes { - attribute(ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE) - } - - // Add the aar inner jars to the compileClasspath - sourceSets.main.compileClasspath += it - - // Put our custom dependencies onto IDEA's PROVIDED scope - apply plugin: "idea" - idea.module.scopes.PROVIDED.plus += [it] - } -} - -dependencies { - // Transforamtions to extract the classes.jar in the aar package - def explodedAarType = ArtifactType.EXPLODED_AAR.getType() - registerTransform { - from.attribute(ARTIFACT_FORMAT, AndroidArtifacts.TYPE_AAR) - to.attribute(ARTIFACT_FORMAT, explodedAarType) - artifactTransform(ExtractAarTransform) - } - - registerTransform { - from.attribute(ARTIFACT_FORMAT, explodedAarType) - to.attribute(ARTIFACT_FORMAT, "classes.jar") - artifactTransform(AarTransform) { params(ArtifactType.JAR) } - } - - registerTransform { - from.attribute(ARTIFACT_FORMAT, "classes.jar") - to.attribute(ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE) - artifactTransform(ClassesJarArtifactTransform) - } - - compileOnly name: "android" - - aar "com.android.support:support-v4:${supportLibsVersion}" - aar "com.google.android.support:wearable:${wearVersion}" -} - -/** - * An ArtifactTransform for renaming the classes.jar - */ -class ClassesJarArtifactTransform extends ArtifactTransform { - @Override - List transform(File file) { - final String[] names = file.getPath().split(Pattern.quote(File.separator)) - final String aarName = names[names.length - 4].replace(".aar", "") - final File renamedJar = new File(getOutputDirectory(), aarName + ".jar") - renamedJar << file.bytes - return ImmutableList.of(renamedJar) - } -} - -task createPom { - // The compile configuration should be replaced by implementation eventually: - // https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration#new_configurations - pom { - project { - groupId "org.p5android" - artifactId "processing-core" - version "${modeVersion}" - packaging "jar" - licenses { - license { - name "GNU Lesser General Public License, version 2.1" - url "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt" - distribution "repo" - } - } - dependencies { - dependency { - groupId "com.android.support" - artifactId "support-v4" - version "${supportLibsVersion}" - scope "compile" - } - dependency { - groupId "com.google.android.support" - artifactId "wearable" - version "${wearVersion}" - scope "compile" - } - } - } - }.writeTo("dist/processing-core-${modeVersion}.pom") -} - -sourceSets { - main { - java { - srcDirs = ["src/"] - } - resources { - srcDirs = ["src/"] - } - } -} - -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = "sources" - from sourceSets.main.allSource -} - -// Does not work because of Processing-specific tags in source code, such as @webref -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = "javadoc" - from javadoc.destinationDir -} - -artifacts { -// archives javadocJar - archives sourcesJar -} - -jar.doLast { task -> - ant.checksum file: task.archivePath -} - -clean.doFirst { - delete "dist" - delete "${coreZipPath}" -} - -build.doLast { - // Copying core jar as zip inside the mode folder - Files.copy(file("${buildDir}/libs/core.jar").toPath(), - file("${coreZipPath}").toPath(), REPLACE_EXISTING); - - // Copying the files for release on JCentral - File distFolder = file("dist"); - distFolder.mkdirs(); - Files.copy(file("$buildDir/libs/core.jar").toPath(), - file("dist/processing-core-${modeVersion}.jar").toPath(), REPLACE_EXISTING); - Files.copy(file("$buildDir/libs/core-sources.jar").toPath(), - file("dist/processing-core-${modeVersion}-sources.jar").toPath(), REPLACE_EXISTING); - Files.copy(file("$buildDir/libs/core.jar.MD5").toPath(), - file("dist/processing-core-${modeVersion}.jar.md5").toPath(), REPLACE_EXISTING); -} \ No newline at end of file diff --git a/core/src/processing/data/Sort.java b/core/src/processing/data/Sort.java deleted file mode 100644 index c582735f2..000000000 --- a/core/src/processing/data/Sort.java +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2013-16 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. - - 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.data; - - -/** - * Internal sorter used by several data classes. - * Advanced users only, not official API. - */ -public abstract class Sort implements Runnable { - - public Sort() { } - - - public void run() { - int c = size(); - if (c > 1) { - sort(0, c - 1); - } - } - - - protected void sort(int i, int j) { - int pivotIndex = (i+j)/2; - swap(pivotIndex, j); - int k = partition(i-1, j); - swap(k, j); - if ((k-i) > 1) sort(i, k-1); - if ((j-k) > 1) sort(k+1, j); - } - - - protected int partition(int left, int right) { - int pivot = right; - do { - while (compare(++left, pivot) < 0) { } - while ((right != 0) && (compare(--right, pivot) > 0)) { } - swap(left, right); - } while (left < right); - swap(left, right); - return left; - } - - - abstract public int size(); - abstract public float compare(int a, int b); - abstract public void swap(int a, int b); -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..5465fec0e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 99340b4ad..41d9927a4 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2c2bbe5f9..d6e308a63 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +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 -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip diff --git a/gradlew b/gradlew index cccdd3d51..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,130 @@ -#!/usr/bin/env sh +#!/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. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# 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/HEAD/platforms/jvm/plugins-application/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 -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +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 -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$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="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +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 - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +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" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +133,120 @@ 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. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -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" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + 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 - i=$((i+1)) + # 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 - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# 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/libs/google-vr/build.gradle b/libs/google-vr/build.gradle new file mode 100644 index 000000000..9d7c8f77b --- /dev/null +++ b/libs/google-vr/build.gradle @@ -0,0 +1,7 @@ +// Dummy Gradle project to be able to import local aar files: +// https://stackoverflow.com/a/60888941 + +configurations.maybeCreate("default") +artifacts.add("default", file('sdk-base-1.180.0.aar')) +artifacts.add("default", file('sdk-common-1.180.0.aar')) +artifacts.add("default", file('sdk-audio-1.180.0.aar')) diff --git a/libs/google-vr/sdk-audio-1.180.0.aar b/libs/google-vr/sdk-audio-1.180.0.aar new file mode 100644 index 000000000..007485cf4 Binary files /dev/null and b/libs/google-vr/sdk-audio-1.180.0.aar differ diff --git a/libs/google-vr/sdk-base-1.180.0.aar b/libs/google-vr/sdk-base-1.180.0.aar new file mode 100644 index 000000000..e9047d226 Binary files /dev/null and b/libs/google-vr/sdk-base-1.180.0.aar differ diff --git a/libs/google-vr/sdk-common-1.180.0.aar b/libs/google-vr/sdk-common-1.180.0.aar new file mode 100644 index 000000000..7ba5cff92 Binary files /dev/null and b/libs/google-vr/sdk-common-1.180.0.aar differ diff --git a/libs/processing-ar/build.gradle b/libs/processing-ar/build.gradle new file mode 100755 index 000000000..f0681e6de --- /dev/null +++ b/libs/processing-ar/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'com.android.library' +} + +android { + namespace "processing.ar" + + defaultConfig { + minSdkVersion 19 + 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 { + implementation project(':libs:processing-core') + implementation 'com.google.ar:core:1.35.0' +} \ No newline at end of file diff --git a/libs/processing-ar/proguard-rules.pro b/libs/processing-ar/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/libs/processing-ar/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-ar/src/main/AndroidManifest.xml b/libs/processing-ar/src/main/AndroidManifest.xml new file mode 100755 index 000000000..97330b776 --- /dev/null +++ b/libs/processing-ar/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/processing-ar/src/main/assets/shaders/ARLightFrag.glsl b/libs/processing-ar/src/main/assets/shaders/ARLightFrag.glsl new file mode 100644 index 000000000..6ab64485c --- /dev/null +++ b/libs/processing-ar/src/main/assets/shaders/ARLightFrag.glsl @@ -0,0 +1,49 @@ +/* + Part of the Processing project - http://processing.org + + 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 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 +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform vec4 colorCorrection; + +varying vec4 vertColor; +varying vec4 backVertColor; + +// Approximate sRGB gamma parameters +const float kGamma = 0.4545454; +const float kMiddleGrayGamma = 0.466; + +void main() { + vec3 colorShift = colorCorrection.rgb; + float averagePixelIntensity = colorCorrection.a; + + vec4 color = gl_FrontFacing ? vertColor : backVertColor; + + // Apply SRGB gamma before writing the fragment color. + color.rgb = pow(color.rgb, vec3(kGamma)); + + // Apply average pixel intensity and color shift + color.rgb *= colorShift * (averagePixelIntensity / kMiddleGrayGamma); + gl_FragColor = color; +} \ No newline at end of file diff --git a/libs/processing-ar/src/main/assets/shaders/ARLightVert.glsl b/libs/processing-ar/src/main/assets/shaders/ARLightVert.glsl new file mode 100644 index 000000000..b9596af48 --- /dev/null +++ b/libs/processing-ar/src/main/assets/shaders/ARLightVert.glsl @@ -0,0 +1,156 @@ +/* + Part of the Processing project - http://processing.org + + 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 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 +*/ + +uniform mat4 modelviewMatrix; +uniform mat4 transformMatrix; +uniform mat3 normalMatrix; + +uniform int lightCount; +uniform vec4 lightPosition[8]; +uniform vec3 lightNormal[8]; +uniform vec3 lightAmbient[8]; +uniform vec3 lightDiffuse[8]; +uniform vec3 lightSpecular[8]; +uniform vec3 lightFalloff[8]; +uniform vec2 lightSpot[8]; + +attribute vec4 position; +attribute vec4 color; +attribute vec3 normal; + +attribute vec4 ambient; +attribute vec4 specular; +attribute vec4 emissive; +attribute float shininess; + +varying vec4 vertColor; +varying vec4 backVertColor; + +const float zero_float = 0.0; +const float one_float = 1.0; +const vec3 zero_vec3 = vec3(0); + +const float kInverseGamma = 2.2; + +float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) { + vec3 lpv = lightPos - vertPos; + vec3 dist = vec3(one_float); + dist.z = dot(lpv, lpv); + dist.y = sqrt(dist.z); + return one_float / dot(dist, coeff); +} + +float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) { + vec3 lpv = normalize(lightPos - vertPos); + vec3 nln = -one_float * lightNorm; + float spotCos = dot(nln, lpv); + return spotCos <= minCos ? zero_float : pow(spotCos, spotExp); +} + +float lambertFactor(vec3 lightDir, vec3 vecNormal) { + return max(zero_float, dot(lightDir, vecNormal)); +} + +float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) { + vec3 np = normalize(vertPos); + vec3 ldp = normalize(lightDir - np); + return pow(max(zero_float, dot(ldp, vecNormal)), shine); +} + +void main() { + // Vertex in clip coordinates + gl_Position = transformMatrix * position; + + // Vertex in eye coordinates + vec3 ecVertex = vec3(modelviewMatrix * position); + + // Normal vector in eye coordinates + vec3 ecNormal = normalize(normalMatrix * normal); + vec3 ecNormalInv = ecNormal * -one_float; + + // Light calculations + vec3 totalAmbient = vec3(0, 0, 0); + + vec3 totalFrontDiffuse = vec3(0, 0, 0); + vec3 totalFrontSpecular = vec3(0, 0, 0); + + vec3 totalBackDiffuse = vec3(0, 0, 0); + vec3 totalBackSpecular = vec3(0, 0, 0); + + for (int i = 0; i < 8; i++) { + if (lightCount == i) break; + + vec3 lightPos = lightPosition[i].xyz; + bool isDir = lightPosition[i].w < one_float; + float spotCos = lightSpot[i].x; + float spotExp = lightSpot[i].y; + + vec3 lightDir; + float falloff; + float spotf; + + if (isDir) { + falloff = one_float; + lightDir = -one_float * lightNormal[i]; + } else { + falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]); + lightDir = normalize(lightPos - ecVertex); + } + + spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i], + spotCos, spotExp) + : one_float; + + if (any(greaterThan(lightAmbient[i], zero_vec3))) { + totalAmbient += lightAmbient[i] * falloff; + } + + if (any(greaterThan(lightDiffuse[i], zero_vec3))) { + totalFrontDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormal); + totalBackDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormalInv); + } + + if (any(greaterThan(lightSpecular[i], zero_vec3))) { + totalFrontSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess); + totalBackSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess); + } + } + + vec4 gcolor = vec4(pow(color.rgb, vec3(kInverseGamma)), color.a); + vec4 gambient = vec4(pow(ambient.rgb, vec3(kInverseGamma)), ambient.a); + + // Calculating final color as result of all lights (plus emissive term). + // Transparency is determined exclusively by the diffuse component. + vertColor = vec4(totalAmbient, 0) * gambient + + vec4(totalFrontDiffuse, 1) * gcolor + + vec4(totalFrontSpecular, 0) * specular + + vec4(emissive.rgb, 0); + + backVertColor = vec4(totalAmbient, 0) * gambient + + vec4(totalBackDiffuse, 1) * gcolor + + vec4(totalBackSpecular, 0) * specular + + vec4(emissive.rgb, 0); +} \ No newline at end of file diff --git a/libs/processing-ar/src/main/assets/shaders/ARTexLightFrag.glsl b/libs/processing-ar/src/main/assets/shaders/ARTexLightFrag.glsl new file mode 100644 index 000000000..f06fb2160 --- /dev/null +++ b/libs/processing-ar/src/main/assets/shaders/ARTexLightFrag.glsl @@ -0,0 +1,56 @@ +/* + Part of the Processing project - http://processing.org + + 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 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 +*/ + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D texture; +uniform vec2 texOffset; +uniform vec4 colorCorrection; + +varying vec4 vertColor; +varying vec4 backVertColor; +varying vec4 vertTexCoord; + +// Approximate sRGB gamma parameters +const float kGamma = 0.4545454; +const float kInverseGamma = 2.2; +const float kMiddleGrayGamma = 0.466; + +void main() { + vec3 colorShift = colorCorrection.rgb; + float averagePixelIntensity = colorCorrection.a; + + vec4 tex = texture2D(texture, vertTexCoord.st); + vec4 gtex = vec4(pow(tex.rgb, vec3(kInverseGamma)), tex.a); + + vec4 color = gtex * (gl_FrontFacing ? vertColor : backVertColor); + + // Apply SRGB gamma before writing the fragment color. + color.rgb = pow(color.rgb, vec3(kGamma)); + + // Apply average pixel intensity and color shift + color.rgb *= colorShift * (averagePixelIntensity / kMiddleGrayGamma); + gl_FragColor = color; +} \ No newline at end of file diff --git a/libs/processing-ar/src/main/assets/shaders/ARTexLightVert.glsl b/libs/processing-ar/src/main/assets/shaders/ARTexLightVert.glsl new file mode 100644 index 000000000..5269a550e --- /dev/null +++ b/libs/processing-ar/src/main/assets/shaders/ARTexLightVert.glsl @@ -0,0 +1,162 @@ +/* + Part of the Processing project - http://processing.org + + 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 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 +*/ + +uniform mat4 modelviewMatrix; +uniform mat4 transformMatrix; +uniform mat3 normalMatrix; +uniform mat4 texMatrix; + +uniform int lightCount; +uniform vec4 lightPosition[8]; +uniform vec3 lightNormal[8]; +uniform vec3 lightAmbient[8]; +uniform vec3 lightDiffuse[8]; +uniform vec3 lightSpecular[8]; +uniform vec3 lightFalloff[8]; +uniform vec2 lightSpot[8]; + +attribute vec4 position; +attribute vec4 color; +attribute vec3 normal; +attribute vec2 texCoord; + +attribute vec4 ambient; +attribute vec4 specular; +attribute vec4 emissive; +attribute float shininess; + +varying vec4 vertColor; +varying vec4 backVertColor; +varying vec4 vertTexCoord; + +const float zero_float = 0.0; +const float one_float = 1.0; +const vec3 zero_vec3 = vec3(0); + +const float kInverseGamma = 2.2; + +float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) { + vec3 lpv = lightPos - vertPos; + vec3 dist = vec3(one_float); + dist.z = dot(lpv, lpv); + dist.y = sqrt(dist.z); + return one_float / dot(dist, coeff); +} + +float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) { + vec3 lpv = normalize(lightPos - vertPos); + vec3 nln = -one_float * lightNorm; + float spotCos = dot(nln, lpv); + return spotCos <= minCos ? zero_float : pow(spotCos, spotExp); +} + +float lambertFactor(vec3 lightDir, vec3 vecNormal) { + return max(zero_float, dot(lightDir, vecNormal)); +} + +float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) { + vec3 np = normalize(vertPos); + vec3 ldp = normalize(lightDir - np); + return pow(max(zero_float, dot(ldp, vecNormal)), shine); +} + +void main() { + // Vertex in clip coordinates + gl_Position = transformMatrix * position; + + // Vertex in eye coordinates + vec3 ecVertex = vec3(modelviewMatrix * position); + + // Normal vector in eye coordinates + vec3 ecNormal = normalize(normalMatrix * normal); + vec3 ecNormalInv = ecNormal * -one_float; + + // Light calculations + vec3 totalAmbient = vec3(0, 0, 0); + + vec3 totalFrontDiffuse = vec3(0, 0, 0); + vec3 totalFrontSpecular = vec3(0, 0, 0); + + vec3 totalBackDiffuse = vec3(0, 0, 0); + vec3 totalBackSpecular = vec3(0, 0, 0); + + for (int i = 0; i < 8; i++) { + if (lightCount == i) break; + + vec3 lightPos = lightPosition[i].xyz; + bool isDir = lightPosition[i].w < one_float; + float spotCos = lightSpot[i].x; + float spotExp = lightSpot[i].y; + + vec3 lightDir; + float falloff; + float spotf; + + if (isDir) { + falloff = one_float; + lightDir = -one_float * lightNormal[i]; + } else { + falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]); + lightDir = normalize(lightPos - ecVertex); + } + + spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i], + spotCos, spotExp) + : one_float; + + if (any(greaterThan(lightAmbient[i], zero_vec3))) { + totalAmbient += lightAmbient[i] * falloff; + } + + if (any(greaterThan(lightDiffuse[i], zero_vec3))) { + totalFrontDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormal); + totalBackDiffuse += lightDiffuse[i] * falloff * spotf * + lambertFactor(lightDir, ecNormalInv); + } + + if (any(greaterThan(lightSpecular[i], zero_vec3))) { + totalFrontSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess); + totalBackSpecular += lightSpecular[i] * falloff * spotf * + blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess); + } + } + + vec4 gcolor = vec4(pow(color.rgb, vec3(kInverseGamma)), color.a); + vec4 gambient = vec4(pow(ambient.rgb, vec3(kInverseGamma)), ambient.a); + + // Calculating final color as result of all lights (plus emissive term). + // Transparency is determined exclusively by the diffuse component. + vertColor = vec4(totalAmbient, 0) * gambient + + vec4(totalFrontDiffuse, 1) * gcolor + + vec4(totalFrontSpecular, 0) * specular + + vec4(emissive.rgb, 0); + + backVertColor = vec4(totalAmbient, 0) * gambient + + vec4(totalBackDiffuse, 1) * gcolor + + vec4(totalBackSpecular, 0) * specular + + vec4(emissive.rgb, 0); + + // Calculating texture coordinates, with r and q set both to one + vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0); +} diff --git a/libs/processing-ar/src/main/assets/shaders/BackgroundFrag.glsl b/libs/processing-ar/src/main/assets/shaders/BackgroundFrag.glsl new file mode 100644 index 000000000..d2fc66d60 --- /dev/null +++ b/libs/processing-ar/src/main/assets/shaders/BackgroundFrag.glsl @@ -0,0 +1,9 @@ +#extension GL_OES_EGL_image_external : require + +precision mediump float; +varying vec2 v_TexCoord; +uniform samplerExternalOES sTexture; + +void main() { + gl_FragColor = texture2D(sTexture, v_TexCoord); +} diff --git a/libs/processing-ar/src/main/assets/shaders/BackgroundVert.glsl b/libs/processing-ar/src/main/assets/shaders/BackgroundVert.glsl new file mode 100644 index 000000000..25a4da170 --- /dev/null +++ b/libs/processing-ar/src/main/assets/shaders/BackgroundVert.glsl @@ -0,0 +1,9 @@ +attribute vec4 a_Position; +attribute vec2 a_TexCoord; + +varying vec2 v_TexCoord; + +void main() { + gl_Position = a_Position; + v_TexCoord = a_TexCoord; +} diff --git a/libs/processing-ar/src/main/java/processing/ar/ARAnchor.java b/libs/processing-ar/src/main/java/processing/ar/ARAnchor.java new file mode 100644 index 000000000..c3cd930c4 --- /dev/null +++ b/libs/processing-ar/src/main/java/processing/ar/ARAnchor.java @@ -0,0 +1,87 @@ +/* -*- 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.ar; + +import processing.core.PMatrix3D; + +public class ARAnchor { + protected ARGraphics g; + private boolean disposed = false; + + private int id; + private PMatrix3D m; + + public ARAnchor(ARTrackable trackable, float x, float y, float z) { + this.g = trackable.g; + + int idx = g.trackableIndex(Integer.parseInt(trackable.id())); + id = g.createAnchor(idx, x, y, z); + } + + public ARAnchor(ARTrackable trackable) { + this.g = trackable.g; + id = g.createAnchor(trackable.hit); + trackable.hit = null; + } + + public void dispose() { + if (!disposed) { + g.deleteAnchor(id); + disposed = true; + } + } + + public String id() { + return String.valueOf(id); + } + + public PMatrix3D matrix() { + m = g.getTrackableMatrix(id, m); + return m; + } + + public void attach() { + g.pushMatrix(); + g.anchor(id); + } + + public void detach() { + g.popMatrix(); + } + + public boolean isTracking() { + return g.anchorStatus(id) == ARGraphics.TRACKING; + } + + public boolean isPaused() { + return g.anchorStatus(id) == ARGraphics.PAUSED; + } + + public boolean isStopped() { + return g.anchorStatus(id) == ARGraphics.STOPPED; + } + + public boolean isDisposed() { + return disposed; + } +} diff --git a/libs/processing-ar/src/main/java/processing/ar/ARGraphics.java b/libs/processing-ar/src/main/java/processing/ar/ARGraphics.java new file mode 100644 index 000000000..3cdfba0b9 --- /dev/null +++ b/libs/processing-ar/src/main/java/processing/ar/ARGraphics.java @@ -0,0 +1,687 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2019-23 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.ar; + +import android.view.SurfaceHolder; + +import com.google.ar.core.Anchor; +import com.google.ar.core.AugmentedImage; +import com.google.ar.core.HitResult; +import com.google.ar.core.Plane; +import com.google.ar.core.Pose; +import com.google.ar.core.Trackable; +import com.google.ar.core.TrackingState; +import com.google.ar.core.Config; +import com.google.ar.core.Session; + +import java.net.URL; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +import processing.android.AppComponent; +import processing.core.PGraphics; +import processing.core.PMatrix3D; +import processing.core.PSurface; +import processing.opengl.PGL; +import processing.opengl.PGLES; +import processing.opengl.PGraphics3D; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PShader; + +public class ARGraphics extends PGraphics3D { + static protected final int UNKNOWN = -1; + + static protected final int PLANE_FLOOR = 0; + static protected final int PLANE_CEILING = 1; + static protected final int PLANE_WALL = 2; + static protected final int POINT = 3; + static protected final int IMAGE = 4; + + static protected final int TRACKING = 0; + static protected final int PAUSED = 1; + static protected final int STOPPED = 2; + + // Convenience reference to the AR surface. It is the same object one gets from PApplet.getSurface(). + protected ARSurface surfar; + + protected BackgroundRenderer backgroundRenderer; + + protected float[] projMatrix = new float[16]; + protected float[] viewMatrix = new float[16]; + protected float[] anchorMatrix = new float[16]; + protected float[] colorCorrection = new float[4]; + + protected ArrayList trackers = new ArrayList(); + protected ArrayList trackObjects = new ArrayList(); + protected HashMap trackMatrices = new HashMap(); + protected HashMap trackIds = new HashMap(); + protected HashMap trackIdx = new HashMap(); + + protected ArrayList newObjects = new ArrayList(); + protected ArrayList delAnchors = new ArrayList(); + + protected HashMap anchors = new HashMap(); + + protected float[] pointIn = new float[3]; + protected float[] pointOut = new float[3]; + + protected int lastTrackableId = 0; + protected int lastAnchorId = 0; + + static protected URL arLightShaderVertURL = + PGraphicsOpenGL.class.getResource("/assets/shaders/ARLightVert.glsl"); + static protected URL arTexlightShaderVertURL = + PGraphicsOpenGL.class.getResource("/assets/shaders/ARTexLightVert.glsl"); + static protected URL arLightShaderFragURL = + PGraphicsOpenGL.class.getResource("/assets/shaders/ARLightFrag.glsl"); + static protected URL arTexlightShaderFragURL = + PGraphicsOpenGL.class.getResource("/assets/shaders/ARTexLightFrag.glsl"); + + protected PShader arLightShader; + protected PShader arTexlightShader; + + + public ARGraphics() { + } + + + static ARTrackable[] getTrackables() { + return null; + } + + @Override + public PSurface createSurface(AppComponent appComponent, SurfaceHolder surfaceHolder, boolean reset) { + if (reset) pgl.resetFBOLayer(); + surfar = new ARSurface(this, appComponent, surfaceHolder); + return surfar; + } + + + @Override + protected PGL createPGL(PGraphicsOpenGL pGraphicsOpenGL) { + return new PGLES(pGraphicsOpenGL); + } + + + @Override + public void eye() { + super.ortho(0, width, -height, 0, -1, +1); + + eyeDist = 1; + resetMatrix(); + } + + @Override + public void beginDraw() { + super.beginDraw(); + updateView(); + + // Always clear the screen and draw the background + background(0); + backgroundRenderer.draw(surfar.frame); + } + + public void endDraw() { + cleanup(); + super.endDraw(); + } + + + @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 AR"); + } + + + @Override + public void perspective(float fov, float aspect, float zNear, float zFar) { + PGraphics.showWarning("Perspective cannot be set in AR"); + } + + + @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() { + if (projMatrix != null && viewMatrix != null) { + + // Fist, set all matrices to identity + resetProjection(); + resetMatrix(); + + // Apply the projection matrix + applyProjection(projMatrix[0], projMatrix[4], projMatrix[8], projMatrix[12], + projMatrix[1], projMatrix[5], projMatrix[9], projMatrix[13], + projMatrix[2], projMatrix[6], projMatrix[10], projMatrix[14], + projMatrix[3], projMatrix[7], projMatrix[11], projMatrix[15]); + + // make modelview = view + applyMatrix(viewMatrix[0], viewMatrix[4], viewMatrix[8], viewMatrix[12], + viewMatrix[1], viewMatrix[5], viewMatrix[9], viewMatrix[13], + viewMatrix[2], viewMatrix[6], viewMatrix[10], viewMatrix[14], + viewMatrix[3], viewMatrix[7], viewMatrix[11], viewMatrix[15]); + } + } + + + public void addTracker(ARTracker tracker) { + trackers.add(tracker); + } + + + public void removeTracker(ARTracker tracker) { + trackers.remove(tracker); + } + + + public int trackableCount() { + return trackObjects.size(); + } + + + public int trackableId(int i) { + return trackIds.get(trackObjects.get(i)); + } + + + public int trackableIndex(int id) { + return trackIdx.get(id); + } + + + public String trackableName(int i) { + Trackable track = trackObjects.get(i); + if (track instanceof AugmentedImage) { + AugmentedImage img = ((AugmentedImage)track); + return img.getName(); + } + return null; + } + + + public int trackableType(int i) { + Trackable track = trackObjects.get(i); + if (track instanceof Plane) { + Plane plane = (Plane)track; + if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING) { + return PLANE_FLOOR; + } else if (plane.getType() == Plane.Type.HORIZONTAL_DOWNWARD_FACING) { + return PLANE_CEILING; + } else if (plane.getType() == Plane.Type.VERTICAL) { + return PLANE_WALL; + } + } else if (track instanceof AugmentedImage) { + return IMAGE; + } + return UNKNOWN; + } + + + public int trackableStatus(int i) { + Trackable track = trackObjects.get(i); + if (track.getTrackingState() == TrackingState.PAUSED) { + return PAUSED; + } else if (track.getTrackingState() == TrackingState.TRACKING) { + return TRACKING; + } else if (track.getTrackingState() == TrackingState.STOPPED) { + return STOPPED; + } + return UNKNOWN; + } + + + public boolean trackableNew(int i) { + Trackable track = trackObjects.get(i); + return newObjects.contains(track); + } + + + public boolean trackableSelected(int i, int mx, int my) { + Trackable tracki = trackObjects.get(i); + for (HitResult hit : surfar.frame.hitTest(mx, my)) { + Trackable trackable = hit.getTrackable(); + Pose hitPose = hit.getHitPose(); + if (trackable instanceof Plane) { + Plane plane = (Plane)trackable; + if (tracki.equals(plane) && plane.isPoseInPolygon(hitPose)) { + return true; + } + } else if (trackable instanceof AugmentedImage) { + AugmentedImage image = (AugmentedImage)trackable; + Pose anchorPose = image.getCenterPose(); + Pose localHitPose = anchorPose.compose(hitPose); + if (tracki.equals(image) && isPoseInsideAugmentedImage(localHitPose, image)) { + return true; + } + } + } + return false; + } + + + protected HitResult getHitResult(int mx, int my) { + for (HitResult hit : surfar.frame.hitTest(mx, my)) { + Trackable trackable = hit.getTrackable(); + Pose hitPose = hit.getHitPose(); + if (trackable instanceof Plane) { + Plane plane = (Plane)trackable; + if (trackObjects.contains(plane) && plane.isPoseInPolygon(hitPose)) { + return hit; + } + } else if (trackable instanceof AugmentedImage) { + AugmentedImage image = (AugmentedImage)trackable; + Pose anchorPose = image.getCenterPose(); + Pose localHitPose = anchorPose.compose(hitPose); + if (trackObjects.contains(image) && isPoseInsideAugmentedImage(localHitPose, image)) { + return hit; + } + } + } + return null; + } + + + private boolean isPoseInsideAugmentedImage(Pose pose, AugmentedImage image) { + // Get the four corners of the AugmentedImage's defining rectangle + float[] corners = new float[16]; + image.getCenterPose().toMatrix(corners, 0); + + // Define the vertices of the rectangle in 2D (assuming the image is flat on the XZ plane) + float imageMinX = Float.POSITIVE_INFINITY; + float imageMaxX = Float.NEGATIVE_INFINITY; + float imageMinZ = Float.POSITIVE_INFINITY; + float imageMaxZ = Float.NEGATIVE_INFINITY; + + // Extract the X and Z coordinates of the corners + for (int i = 0; i < 8; i += 2) { + float cornerX = corners[i]; + float cornerZ = corners[i + 2]; + + if (cornerX < imageMinX) { + imageMinX = cornerX; + } + if (cornerX > imageMaxX) { + imageMaxX = cornerX; + } + if (cornerZ < imageMinZ) { + imageMinZ = cornerZ; + } + if (cornerZ > imageMaxZ) { + imageMaxZ = cornerZ; + } + } + + // Check if the Pose's position (X, Z) is within the bounds of the AugmentedImage's rectangle + float poseX = pose.tx(); + float poseZ = pose.tz(); + return (imageMinX <= poseX && poseX <= imageMaxX && imageMinZ <= poseZ && poseZ <= imageMaxZ); + } + + + protected int getTrackable(HitResult hit) { + Trackable track = hit.getTrackable(); + return trackObjects.indexOf(track); + } + + + public float[] getTrackablePolygon(int i) { + return getTrackablePolygon(i, null); + } + + + public float[] getTrackablePolygon(int i, float[] points) { + Trackable track = trackObjects.get(i); + if (track instanceof Plane) { + Plane plane = (Plane)track; + FloatBuffer buffer = plane.getPolygon(); + buffer.rewind(); + if (points == null || points.length < buffer.capacity()) { + points = new float[buffer.capacity()]; + } + buffer.get(points, 0, buffer.capacity()); + } else if (track instanceof AugmentedImage) { + AugmentedImage image = (AugmentedImage)track; + points = new float[8]; + image.getCenterPose().toMatrix(points, 0); + } + return points; + } + + + public float getTrackableExtentX(int i) { + Trackable track = trackObjects.get(i); + if (track instanceof Plane) { + return ((Plane)track).getExtentX(); + } else if (track instanceof AugmentedImage) { + return ((AugmentedImage)track).getExtentX(); + } + return -1; + } + + + public float getTrackableExtentZ(int i) { + Trackable track = trackObjects.get(i); + if (track instanceof Plane) { + return ((Plane)track).getExtentZ(); + } else if (track instanceof AugmentedImage) { + return ((AugmentedImage)track).getExtentZ(); + } + return -1; + } + + + public PMatrix3D getTrackableMatrix(int i) { + return getTrackableMatrix(i, null); + } + + + public PMatrix3D getTrackableMatrix(int i, PMatrix3D target) { + if (target == null) { + target = new PMatrix3D(); + } + + Plane plane = (Plane)trackObjects.get(i); + float[] mat = trackMatrices.get(plane); + target.set(mat[0], mat[4], mat[8], mat[12], + mat[1], mat[5], mat[9], mat[13], + mat[2], mat[6], mat[10], mat[14], + mat[3], mat[7], mat[11], mat[15]); + + return target; + } + + + public int createAnchor(int i, float x, float y, float z) { + Trackable track = trackObjects.get(i); + Pose centerPose = null; + if (track instanceof Plane) { + Plane plane = (Plane)track; + centerPose = plane.getCenterPose(); + } else if (track instanceof AugmentedImage) { + AugmentedImage img = (AugmentedImage)track; + centerPose = img.getCenterPose(); + } + if (centerPose != null) { + pointIn[0] = x; + pointIn[1] = y; + pointIn[2] = z; + centerPose.transformPoint(pointIn, 0, pointOut, 0); + Pose anchorPose = Pose.makeTranslation(pointOut); + Anchor anchor = track.createAnchor(anchorPose); + anchors.put(++lastAnchorId, anchor); + return lastAnchorId; + } + return -1; + } + + + public int createAnchor(int mx, int my) { + for (HitResult hit : surfar.frame.hitTest(mx, my)) { + Trackable trackable = hit.getTrackable(); + Pose hitPose = hit.getHitPose(); + if (trackable instanceof Plane) { + Plane plane = (Plane)trackable; + if (trackObjects.contains(plane) && plane.isPoseInPolygon(hitPose)) { + return createAnchor(hit); + } + } else if (trackable instanceof AugmentedImage) { + AugmentedImage image = (AugmentedImage)trackable; + Pose anchorPose = image.getCenterPose(); + Pose localHitPose = anchorPose.compose(hitPose); + if (trackObjects.contains(image) && isPoseInsideAugmentedImage(localHitPose, image)) { + return createAnchor(hit); + } + } + } + return 0; + } + + + protected int createAnchor(HitResult hit) { + Anchor anchor = hit.createAnchor(); + anchors.put(++lastAnchorId, anchor); + return lastAnchorId; + } + + + public void deleteAnchor(int id) { + delAnchors.add(id); + } + + + public int anchorCount() { + return anchors.size(); + } + + + public int anchorStatus(int id) { + Anchor anchor = anchors.get(id); + if (anchor.getTrackingState() == TrackingState.PAUSED) { + return PAUSED; + } else if (anchor.getTrackingState() == TrackingState.TRACKING) { + return TRACKING; + } else if (anchor.getTrackingState() == TrackingState.STOPPED) { + return STOPPED; + } + return UNKNOWN; + } + + + public PMatrix3D getAnchorMatrix(int id) { + return getAnchorMatrix(id, null); + } + + + public PMatrix3D getAnchorMatrix(int id, PMatrix3D target) { + if (target == null) { + target = new PMatrix3D(); + } + Anchor anchor = anchors.get(id); + anchor.getPose().toMatrix(anchorMatrix, 0); + target.set(anchorMatrix[0], anchorMatrix[4], anchorMatrix[8], anchorMatrix[12], + anchorMatrix[1], anchorMatrix[5], anchorMatrix[9], anchorMatrix[13], + anchorMatrix[2], anchorMatrix[6], anchorMatrix[10], anchorMatrix[14], + anchorMatrix[3], anchorMatrix[7], anchorMatrix[11], anchorMatrix[15]); + return target; + } + + + public void anchor(int id) { + Anchor anchor = anchors.get(id); + anchor.getPose().toMatrix(anchorMatrix, 0); + + // now, modelview = view * anchor + applyMatrix(anchorMatrix[0], anchorMatrix[4], anchorMatrix[8], anchorMatrix[12], + anchorMatrix[1], anchorMatrix[5], anchorMatrix[9], anchorMatrix[13], + anchorMatrix[2], anchorMatrix[6], anchorMatrix[10], anchorMatrix[14], + anchorMatrix[3], anchorMatrix[7], anchorMatrix[11], anchorMatrix[15]); + } + + + protected void createBackgroundRenderer() { + backgroundRenderer = new BackgroundRenderer(surfar.getActivity()); + } + + + protected void setCameraTexture() { + surfar.session.setCameraTextureName(backgroundRenderer.getTextureId()); + } + + + protected void updateMatrices() { + surfar.camera.getProjectionMatrix(projMatrix, 0, 0.1f, 100.0f); + surfar.camera.getViewMatrix(viewMatrix, 0); + surfar.frame.getLightEstimate().getColorCorrection(colorCorrection, 0); + } + + + protected void updateTrackables() { + Collection planes = surfar.frame.getUpdatedTrackables(Plane.class); + for (Plane plane: planes) { + addNewPlane(plane); + } + + Collection images = surfar.frame.getUpdatedTrackables(AugmentedImage.class); + for (AugmentedImage image: images) { + addNewImage(image); + } + + // Remove stopped and subsummed trackables + for (int i = trackObjects.size() - 1; i >= 0; i--) { + Trackable track = trackObjects.get(i); + if (track instanceof Plane) { + Plane plane = (Plane)track; + if (plane.getTrackingState() == TrackingState.STOPPED || plane.getSubsumedBy() != null) { + trackObjects.remove(i); + trackMatrices.remove(plane); + int pid = trackIds.remove(plane); + trackIdx.remove(pid); + for (ARTracker t: trackers) t.remove(pid); + } + } + } + + // Update indices + for (int i = 0; i < trackObjects.size(); i++) { + Trackable track = trackObjects.get(i); + int pid = trackIds.get(track); + trackIdx.put(pid, i); + if (newObjects.contains(track)) { + for (ARTracker t: trackers) t.create(i); + } + } + } + + + protected void addNewPlane(Plane plane) { + if (plane.getSubsumedBy() != null) return; + float[] mat = addNewMatrix(plane); + Pose pose = plane.getCenterPose(); + if (pose != null) pose.toMatrix(mat, 0); + } + + + protected void addNewImage(AugmentedImage image) { + float[] mat = addNewMatrix(image); + Pose pose = image.getCenterPose(); + if (pose != null) pose.toMatrix(mat, 0); + } + + + protected float[] addNewMatrix(Trackable obj) { + float[] mat; + if (trackMatrices.containsKey(obj)) { + mat = trackMatrices.get(obj); + } else { + mat = new float[16]; + trackMatrices.put(obj, mat); + trackObjects.add(obj); + trackIds.put(obj, ++lastTrackableId); + newObjects.add(obj); + } + return mat; + } + + + protected void cleanup() { + newObjects.clear(); + for (int id: delAnchors) { + Anchor anchor = anchors.remove(id); + anchor.detach(); + } + delAnchors.clear(); + } + + + @Override + protected PShader getPolyShader(boolean lit, boolean tex) { + if (getPrimaryPG() != this) { + // An offscreen surface will use the default shaders from the parent OpenGL renderer + return super.getPolyShader(lit, tex); + } + + PShader shader; + boolean useDefault = polyShader == null; + if (lit) { + if (tex) { + if (useDefault || !isPolyShaderTexLight(polyShader)) { + if (arTexlightShader == null) { + arTexlightShader = loadShaderFromURL(arTexlightShaderFragURL, arTexlightShaderVertURL); + } + shader = arTexlightShader; + } else { + shader = polyShader; + } + } else { + if (useDefault || !isPolyShaderLight(polyShader)) { + if (arLightShader == null) { + arLightShader = loadShaderFromURL(arLightShaderFragURL, arLightShaderVertURL); + } + shader = arLightShader; + } else { + shader = polyShader; + } + } + updateShader(shader); + return shader; + } else { + // Non-lit shaders use the default shaders from the parent OpenGL renderer + return super.getPolyShader(lit, tex); + } + } + + + @Override + protected void updateShader(PShader shader) { + super.updateShader(shader); + shader.set("colorCorrection", colorCorrection, 4); + } +} diff --git a/libs/processing-ar/src/main/java/processing/ar/ARSurface.java b/libs/processing-ar/src/main/java/processing/ar/ARSurface.java new file mode 100644 index 000000000..e1a4ba08f --- /dev/null +++ b/libs/processing-ar/src/main/java/processing/ar/ARSurface.java @@ -0,0 +1,332 @@ +/* -*- 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.ar; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ConfigurationInfo; +import android.content.res.AssetManager; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.view.*; + +import com.google.ar.core.*; +import com.google.ar.core.exceptions.*; + +import processing.android.AppComponent; +import processing.core.PGraphics; +import processing.opengl.PGLES; +import processing.opengl.PGraphicsOpenGL; +import processing.opengl.PSurfaceGLES; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import java.io.File; +import java.io.InputStream; + +public class ARSurface extends PSurfaceGLES { + private static String T_ALERT_MESSAGE = "ALERT"; + private static String C_NOT_SUPPORTED = "ARCore SDK required to run this app type"; + private static String T_PROMPT_MESSAGE = "PROMPT"; + private static String C_SUPPORTED = "ARCore SDK is installed"; + private static String C_EXCEPT_INSTALL = "Please install ARCore"; + private static String C_EXCEPT_UPDATE_SDK = "Please update ARCore"; + private static String C_EXCEPT_UPDATE_APP = "Please update this app"; + private static String C_DEVICE = "This device does not support AR"; + + // Made these public so they can be accessed from the sketch + public Session session; + public Frame frame; + public Camera camera; + + protected GLSurfaceView surfaceView; + protected AndroidARRenderer renderer; + protected ARGraphics par; + + protected RotationHandler displayRotationHelper; + + public ARSurface(PGraphics graphics, AppComponent appComponent, SurfaceHolder surfaceHolder) { + super(graphics, appComponent, surfaceHolder); + this.sketch = graphics.parent; + this.graphics = graphics; + this.component = appComponent; + this.pgl = (PGLES) ((PGraphicsOpenGL) graphics).pgl; + + par = (ARGraphics) graphics; + + displayRotationHelper = new RotationHandler(activity); + surfaceView = new SurfaceViewAR(activity); + } + + @Override + public Context getContext() { + return activity; + } + + @Override + public void finish() { + sketch.getActivity().finish(); + } + + @Override + public AssetManager getAssets() { + return sketch.getContext().getAssets(); + } + + @Override + public void startActivity(Intent intent) { + sketch.getContext().startActivity(intent); + } + + @Override + public void initView(int sketchWidth, int sketchHeight) { + Window window = sketch.getActivity().getWindow(); + + window.getDecorView() + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + window.setContentView(surfaceView); + } + + @Override + public String getName() { + return sketch.getActivity().getComponentName().getPackageName(); + } + + @Override + public void setOrientation(int which) { + } + + @Override + public File getFilesDir() { + return sketch.getActivity().getFilesDir(); + } + + @Override + public InputStream openFileInput(String filename) { + return null; + } + + @Override + public File getFileStreamPath(String path) { + return sketch.getActivity().getFileStreamPath(path); + } + + @Override + public void dispose() { + } + + + public class SurfaceViewAR extends GLSurfaceView { + public SurfaceViewAR(Context context) { + super(context); + + 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(); + + setPreserveEGLContextOnPause(true); + setEGLContextClientVersion(2); + setEGLConfigChooser(8, 8, 8, 8, 16, 0); + setRenderer(getARRenderer()); + setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + } + + + @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); + } + } + + public AndroidARRenderer getARRenderer() { + renderer = new AndroidARRenderer(); + return renderer; + } + + protected class AndroidARRenderer implements GLSurfaceView.Renderer { + public AndroidARRenderer() { + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + pgl.getGL(null); + par.createBackgroundRenderer(); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + displayRotationHelper.onSurfaceChanged(width, height); + GLES20.glViewport(0, 0, width, height); + + sketch.surfaceChanged(); + graphics.surfaceChanged(); + + sketch.setSize(width, height); + graphics.setSize(sketch.sketchWidth(), sketch.sketchHeight()); + } + + @Override + public void onDrawFrame(GL10 gl) { + if (session == null) return; + + displayRotationHelper.updateSessionIfNeeded(session); + try { + + par.setCameraTexture(); + frame = session.update(); + camera = frame.getCamera(); + + if (camera.getTrackingState() == TrackingState.TRACKING) par.updateTrackables(); + par.updateMatrices(); + + sketch.calculate(); + sketch.handleDraw(); + + + } catch (Throwable tr) { + PGraphics.showWarning("An error occurred in ARCORE: " + tr.getMessage()); + } + } + } + + + @Override + public void startThread() { + } + + @Override + public void pauseThread() { + if (session != null) { + displayRotationHelper.onPause(); + surfaceView.onPause(); + session.pause(); + } + } + + @Override + public void resumeThread() { + if (!sketch.hasPermission("android.permission.CAMERA")) return; + + if (session == null) { + String message = null; + String exception = null; + try { + // Perhaps this should be done in the MainActivity? + // https://github.com/google-ar/arcore-android-sdk/blob/master/samples/hello_ar_java/app/src/main/java/com/google/ar/core/examples/java/helloar/HelloArActivity.java + switch (ArCoreApk.getInstance().requestInstall(sketch.getActivity(), true)) { + case INSTALL_REQUESTED: + message(T_ALERT_MESSAGE, C_NOT_SUPPORTED); + return; + case INSTALLED: + break; + } + + session = new Session(activity); + } catch (UnavailableArcoreNotInstalledException + | UnavailableUserDeclinedInstallationException e) { + message = C_EXCEPT_INSTALL; + exception = e.toString(); + } catch (UnavailableApkTooOldException e) { + message = C_EXCEPT_UPDATE_SDK; + exception = e.toString(); + } catch (UnavailableSdkTooOldException e) { + message = C_EXCEPT_UPDATE_APP; + exception = e.toString(); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("That's that"); + } + + if (message != null) { + message(T_ALERT_MESSAGE, message + " -- " + exception); + } + + Config config = new Config(session); + if (!session.isSupported(config)) { + message(T_PROMPT_MESSAGE, C_DEVICE); + } + session.configure(config); + } + try { + session.resume(); + } catch (CameraNotAvailableException e) { + } + surfaceView.onResume(); + displayRotationHelper.onResume(); + } + + public void message(String _title, String _message) { + final Activity parent = activity; + final String message = _message; + final String title = _title; + + parent.runOnUiThread(new Runnable() { + public void run() { + new AlertDialog.Builder(parent) + .setTitle(title) + .setMessage(message) + .setPositiveButton("OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + } + }).show(); + } + }); + } +} diff --git a/libs/processing-ar/src/main/java/processing/ar/ARTrackable.java b/libs/processing-ar/src/main/java/processing/ar/ARTrackable.java new file mode 100644 index 000000000..c939b95d8 --- /dev/null +++ b/libs/processing-ar/src/main/java/processing/ar/ARTrackable.java @@ -0,0 +1,134 @@ +/* -*- 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.ar; + +import processing.core.PMatrix3D; + +import com.google.ar.core.HitResult; + +public class ARTrackable { + protected ARGraphics g; + protected HitResult hit; + + private String name; + private int id; + private PMatrix3D m; + private float[] points; + + public ARTrackable(ARGraphics g, int id, String name) { + this.g = g; + this.id = id; + this.name = name; + } + + public String id() { + return String.valueOf(id); + } + + public PMatrix3D matrix() { + int idx = g.trackableIndex(id); + m = g.getTrackableMatrix(idx, m); + return m; + } + + public void transform() { + g.applyMatrix(matrix()); + } + + public float[] getPolygon() { + int idx = g.trackableIndex(id); + points = g.getTrackablePolygon(idx, points); + return points; + } + + public float lengthX() { + int idx = g.trackableIndex(id); + return g.getTrackableExtentX(idx); + } + + public float lengthY() { + return 0; + } + + public float lengthZ() { + int idx = g.trackableIndex(id); + return g.getTrackableExtentZ(idx); + } + + public String getName() { + return name; + } + + public boolean isSelected(int mx, int my) { + int idx = g.trackableIndex(id); + return g.trackableSelected(idx, mx, my); + } + + public boolean isNew() { + int idx = g.trackableIndex(id); + return g.trackableNew(idx); + } + + public boolean isTracking() { + int idx = g.trackableIndex(id); + return g.trackableStatus(idx) == ARGraphics.TRACKING; + } + + public boolean isPaused() { + int idx = g.trackableIndex(id); + return g.trackableStatus(idx) == ARGraphics.PAUSED; + } + + public boolean isStopped() { + int idx = g.trackableIndex(id); + return g.trackableStatus(idx) == ARGraphics.STOPPED; + } + + public boolean isPlane() { + return true; + } + + public boolean isPointCloud() { + return false; + } + + public boolean isFloorPlane() { + int idx = g.trackableIndex(id); + return g.trackableType(idx) == ARGraphics.PLANE_FLOOR; + } + + public boolean isImage(){ + int idx = g.trackableIndex(id); + return g.trackableType(idx)== ARGraphics.IMAGE; + } + + public boolean isCeilingPlane() { + int idx = g.trackableIndex(id); + return g.trackableType(idx) == ARGraphics.PLANE_CEILING; + } + + public boolean isWallPlane() { + int idx = g.trackableIndex(id); + return g.trackableType(idx) == ARGraphics.PLANE_WALL; + } +} diff --git a/libs/processing-ar/src/main/java/processing/ar/ARTracker.java b/libs/processing-ar/src/main/java/processing/ar/ARTracker.java new file mode 100644 index 000000000..9beb70bd7 --- /dev/null +++ b/libs/processing-ar/src/main/java/processing/ar/ARTracker.java @@ -0,0 +1,183 @@ +/* -*- 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.ar; + +import android.graphics.Bitmap; + +import com.google.ar.core.HitResult; +import com.google.ar.core.AugmentedImageDatabase; +import com.google.ar.core.Config; +import com.google.ar.core.Session; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Set; + +import processing.core.PApplet; +import processing.core.PImage; + +public class ARTracker { + protected PApplet p; + protected ARGraphics g; + protected AugmentedImageDatabase db; + + private HashMap trackables = new HashMap(); + private ArrayList toRemove = new ArrayList(); + private Method trackableEventMethod; + + public ARTracker(PApplet parent) { + this.p = parent; + this.g = (ARGraphics)p.g; + setEventHandler(); + } + + public void addImage(String name, PImage img) { + addImageImp(name, img, null); + } + + public void addImage(String name, PImage img, float size) { + addImageImp(name, img, size); + } + + private void addImageImp(String name, PImage img, Float size) { + if (db == null) { + // Creating a new database of augmented images. + db = new AugmentedImageDatabase(g.surfar.session); + } + + Bitmap bitmap = (Bitmap)img.getNative(); + if (size != null) { + db.addImage(name, bitmap, size); + } else { + db.addImage(name, bitmap); + } + + // Reset the session config with the updated image database + Config config = new Config(g.surfar.session); + config.setAugmentedImageDatabase(db); + g.surfar.session.configure(config); + } + + public void start() { + cleanup(); + g.addTracker(this); + } + + public void stop() { + g.removeTracker(this); + } + + public int count() { + return g.trackableCount(); + } + + public ARTrackable get(int idx) { + int id = g.trackableId(idx); + String name = g.trackableName(idx); + String sid = String.valueOf(id); + if (!trackables.containsKey(sid)) { + ARTrackable t = new ARTrackable(g, id, name); + trackables.put(sid, t); + } + return get(sid); + } + + public ARTrackable get(String id) { + return trackables.get(id); + } + + public ARTrackable get(int mx, int my) { + HitResult hit = g.getHitResult(mx, my); + if (hit != null) { + int idx = g.getTrackable(hit); + ARTrackable t = get(idx); + t.hit = hit; + return t; + } else { + return null; + } + } + + protected void create(int idx) { + if (trackableEventMethod != null) { + try { + ARTrackable t = get(idx); + trackableEventMethod.invoke(p, t); + } catch (Exception e) { + System.err.println("error, disabling trackableEventMethod() for AR tracker"); + e.printStackTrace(); + trackableEventMethod = null; + } + } + } + + public void clearAnchors(Collection anchors) { + for (ARAnchor anchor : anchors) { + if (anchor.isStopped() || anchor.isDisposed()) { + anchor.dispose(); + toRemove.add(anchor); + } + } + anchors.removeAll(toRemove); + toRemove.clear(); + } + + protected void cleanup() { + // Remove any inactive trackables left over in the tracker. + Set 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/assets/shaders/ColorFrag.glsl b/libs/processing-core/src/main/assets/shaders/ColorFrag.glsl similarity index 100% rename from core/src/assets/shaders/ColorFrag.glsl rename to libs/processing-core/src/main/assets/shaders/ColorFrag.glsl diff --git a/core/src/assets/shaders/ColorVert.glsl b/libs/processing-core/src/main/assets/shaders/ColorVert.glsl similarity index 100% rename from core/src/assets/shaders/ColorVert.glsl rename to libs/processing-core/src/main/assets/shaders/ColorVert.glsl diff --git a/core/src/assets/shaders/LightFrag.glsl b/libs/processing-core/src/main/assets/shaders/LightFrag.glsl similarity index 100% rename from core/src/assets/shaders/LightFrag.glsl rename to libs/processing-core/src/main/assets/shaders/LightFrag.glsl diff --git a/core/src/assets/shaders/LightVert.glsl b/libs/processing-core/src/main/assets/shaders/LightVert.glsl similarity index 100% rename from core/src/assets/shaders/LightVert.glsl rename to libs/processing-core/src/main/assets/shaders/LightVert.glsl diff --git a/core/src/assets/shaders/LineFrag.glsl b/libs/processing-core/src/main/assets/shaders/LineFrag.glsl similarity index 100% rename from core/src/assets/shaders/LineFrag.glsl rename to libs/processing-core/src/main/assets/shaders/LineFrag.glsl diff --git a/core/src/assets/shaders/LineVert.glsl b/libs/processing-core/src/main/assets/shaders/LineVert.glsl similarity index 100% rename from core/src/assets/shaders/LineVert.glsl rename to libs/processing-core/src/main/assets/shaders/LineVert.glsl diff --git a/core/src/assets/shaders/MaskFrag.glsl b/libs/processing-core/src/main/assets/shaders/MaskFrag.glsl similarity index 100% rename from core/src/assets/shaders/MaskFrag.glsl rename to libs/processing-core/src/main/assets/shaders/MaskFrag.glsl diff --git a/core/src/assets/shaders/P2DFrag.glsl b/libs/processing-core/src/main/assets/shaders/P2DFrag.glsl similarity index 100% rename from core/src/assets/shaders/P2DFrag.glsl rename to libs/processing-core/src/main/assets/shaders/P2DFrag.glsl diff --git a/core/src/assets/shaders/P2DVert.glsl b/libs/processing-core/src/main/assets/shaders/P2DVert.glsl similarity index 100% rename from core/src/assets/shaders/P2DVert.glsl rename to libs/processing-core/src/main/assets/shaders/P2DVert.glsl diff --git a/core/src/assets/shaders/PointFrag.glsl b/libs/processing-core/src/main/assets/shaders/PointFrag.glsl similarity index 100% rename from core/src/assets/shaders/PointFrag.glsl rename to libs/processing-core/src/main/assets/shaders/PointFrag.glsl diff --git a/core/src/assets/shaders/PointVert.glsl b/libs/processing-core/src/main/assets/shaders/PointVert.glsl similarity index 100% rename from core/src/assets/shaders/PointVert.glsl rename to libs/processing-core/src/main/assets/shaders/PointVert.glsl diff --git a/core/src/assets/shaders/TexFrag.glsl b/libs/processing-core/src/main/assets/shaders/TexFrag.glsl similarity index 100% rename from core/src/assets/shaders/TexFrag.glsl rename to libs/processing-core/src/main/assets/shaders/TexFrag.glsl diff --git a/core/src/assets/shaders/TexLightFrag.glsl b/libs/processing-core/src/main/assets/shaders/TexLightFrag.glsl similarity index 100% rename from core/src/assets/shaders/TexLightFrag.glsl rename to libs/processing-core/src/main/assets/shaders/TexLightFrag.glsl diff --git a/core/src/assets/shaders/TexLightVert.glsl b/libs/processing-core/src/main/assets/shaders/TexLightVert.glsl similarity index 100% rename from core/src/assets/shaders/TexLightVert.glsl rename to libs/processing-core/src/main/assets/shaders/TexLightVert.glsl diff --git a/core/src/assets/shaders/TexVert.glsl b/libs/processing-core/src/main/assets/shaders/TexVert.glsl similarity index 100% rename from core/src/assets/shaders/TexVert.glsl rename to libs/processing-core/src/main/assets/shaders/TexVert.glsl diff --git a/core/src/processing/a2d/PGraphicsAndroid2D.java b/libs/processing-core/src/main/java/processing/a2d/PGraphicsAndroid2D.java similarity index 99% rename from core/src/processing/a2d/PGraphicsAndroid2D.java rename to libs/processing-core/src/main/java/processing/a2d/PGraphicsAndroid2D.java index ecf771b4d..b82ced644 100644 --- a/core/src/processing/a2d/PGraphicsAndroid2D.java +++ b/libs/processing-core/src/main/java/processing/a2d/PGraphicsAndroid2D.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + 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 @@ -23,6 +23,19 @@ package processing.a2d; +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; @@ -45,21 +58,6 @@ import processing.core.PSurface; 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; - - /** * Subclass for PGraphics that implements the graphics API using * the Android 2D graphics model. Similar tradeoffs to JAVA2D mode @@ -455,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)"); @@ -2330,8 +2334,8 @@ public void copy(int sx, int sy, int sw, int sh, // 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/core/src/processing/a2d/PShapeAndroid2D.java b/libs/processing-core/src/main/java/processing/a2d/PShapeAndroid2D.java similarity index 80% rename from core/src/processing/a2d/PShapeAndroid2D.java rename to libs/processing-core/src/main/java/processing/a2d/PShapeAndroid2D.java index e5ace2266..b9bb2c019 100644 --- a/core/src/processing/a2d/PShapeAndroid2D.java +++ b/libs/processing-core/src/main/java/processing/a2d/PShapeAndroid2D.java @@ -1,6 +1,29 @@ +/* -*- 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; diff --git a/core/src/processing/a2d/PSurfaceAndroid2D.java b/libs/processing-core/src/main/java/processing/a2d/PSurfaceAndroid2D.java similarity index 93% rename from core/src/processing/a2d/PSurfaceAndroid2D.java rename to libs/processing-core/src/main/java/processing/a2d/PSurfaceAndroid2D.java index 8c13844ee..eb7635b87 100644 --- a/core/src/processing/a2d/PSurfaceAndroid2D.java +++ b/libs/processing-core/src/main/java/processing/a2d/PSurfaceAndroid2D.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -23,12 +23,14 @@ 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; @@ -84,6 +86,11 @@ public SurfaceViewAndroid2D(Context context, SurfaceHolder holder) { // 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 diff --git a/core/src/processing/android/ActivityAPI.java b/libs/processing-core/src/main/java/processing/android/ActivityAPI.java similarity index 97% rename from core/src/processing/android/ActivityAPI.java rename to libs/processing-core/src/main/java/processing/android/ActivityAPI.java index e668ea63d..751ec2048 100644 --- a/core/src/processing/android/ActivityAPI.java +++ b/libs/processing-core/src/main/java/processing/android/ActivityAPI.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016-17 The Processing Foundation + 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 diff --git a/core/src/processing/android/AppComponent.java b/libs/processing-core/src/main/java/processing/android/AppComponent.java similarity index 96% rename from core/src/processing/android/AppComponent.java rename to libs/processing-core/src/main/java/processing/android/AppComponent.java index 1574061ee..396e01126 100644 --- a/core/src/processing/android/AppComponent.java +++ b/libs/processing-core/src/main/java/processing/android/AppComponent.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016-17 The Processing Foundation + 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 @@ -23,6 +23,7 @@ package processing.android; import android.content.Intent; + import processing.core.PApplet; import processing.core.PConstants; diff --git a/core/src/processing/android/CompatUtils.java b/libs/processing-core/src/main/java/processing/android/CompatUtils.java similarity index 98% rename from core/src/processing/android/CompatUtils.java rename to libs/processing-core/src/main/java/processing/android/CompatUtils.java index b4f8f55bd..e218e860b 100644 --- a/core/src/processing/android/CompatUtils.java +++ b/libs/processing-core/src/main/java/processing/android/CompatUtils.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2017 The Processing Foundation + 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 @@ -22,9 +22,6 @@ package processing.android; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicInteger; import android.annotation.SuppressLint; import android.os.Build; import android.util.DisplayMetrics; @@ -32,6 +29,10 @@ 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 diff --git a/core/src/processing/android/PFragment.java b/libs/processing-core/src/main/java/processing/android/PFragment.java similarity index 94% rename from core/src/processing/android/PFragment.java rename to libs/processing-core/src/main/java/processing/android/PFragment.java index 1a2132ed3..8fe80d72c 100644 --- a/core/src/processing/android/PFragment.java +++ b/libs/processing-core/src/main/java/processing/android/PFragment.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016-17 The Processing Foundation + 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 @@ -22,12 +22,6 @@ package processing.android; -import android.support.annotation.IdRes; -import android.support.annotation.LayoutRes; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; import android.util.DisplayMetrics; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -44,6 +38,14 @@ 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 { @@ -217,7 +219,7 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { } @Override - public boolean onOptionsItemSelected(MenuItem item){ + public boolean onOptionsItemSelected(MenuItem item) { if (sketch != null) return sketch.onOptionsItemSelected(item); return super.onOptionsItemSelected(item); } diff --git a/core/src/processing/android/PWallpaper.java b/libs/processing-core/src/main/java/processing/android/PWallpaper.java similarity index 99% rename from core/src/processing/android/PWallpaper.java rename to libs/processing-core/src/main/java/processing/android/PWallpaper.java index a285fc90d..850339100 100644 --- a/core/src/processing/android/PWallpaper.java +++ b/libs/processing-core/src/main/java/processing/android/PWallpaper.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016-17 The Processing Foundation + 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 @@ -26,12 +26,12 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.WindowManager; -import processing.core.PApplet; 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; diff --git a/core/src/processing/android/PWatchFaceCanvas.java b/libs/processing-core/src/main/java/processing/android/PWatchFaceCanvas.java similarity index 99% rename from core/src/processing/android/PWatchFaceCanvas.java rename to libs/processing-core/src/main/java/processing/android/PWatchFaceCanvas.java index 227022cfe..ee4222dfc 100644 --- a/core/src/processing/android/PWatchFaceCanvas.java +++ b/libs/processing-core/src/main/java/processing/android/PWatchFaceCanvas.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016-17 The Processing Foundation + 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 @@ -38,6 +38,7 @@ import android.view.SurfaceHolder; import android.view.WindowInsets; import android.view.WindowManager; + import processing.a2d.PGraphicsAndroid2D; import processing.core.PApplet; diff --git a/core/src/processing/android/PWatchFaceGLES.java b/libs/processing-core/src/main/java/processing/android/PWatchFaceGLES.java similarity index 97% rename from core/src/processing/android/PWatchFaceGLES.java rename to libs/processing-core/src/main/java/processing/android/PWatchFaceGLES.java index 8d385d286..e62ab7eb4 100644 --- a/core/src/processing/android/PWatchFaceGLES.java +++ b/libs/processing-core/src/main/java/processing/android/PWatchFaceGLES.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016-17 The Processing Foundation + 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 @@ -37,10 +37,11 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.WindowManager; -import processing.core.PApplet; +import android.graphics.Rect; + import java.lang.reflect.Method; -import android.graphics.Rect; +import processing.core.PApplet; @TargetApi(21) public class PWatchFaceGLES extends Gles2WatchFaceService implements AppComponent { @@ -172,9 +173,9 @@ public void onCreate(SurfaceHolder surfaceHolder) { 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)) { + if (!EGL14.eglChooseConfig(eglDisplay, CONFIG_ATTRIB_LIST, 0, eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { throw new RuntimeException("eglChooseConfig failed"); - } else if(numEglConfigs[0] == 0) { + } else if (numEglConfigs[0] == 0) { throw new RuntimeException("no matching EGL configs"); } else { return eglConfigs[0]; diff --git a/core/src/processing/android/PermissionRequestor.java b/libs/processing-core/src/main/java/processing/android/PermissionRequestor.java similarity index 92% rename from core/src/processing/android/PermissionRequestor.java rename to libs/processing-core/src/main/java/processing/android/PermissionRequestor.java index 93c939238..deac0d852 100644 --- a/core/src/processing/android/PermissionRequestor.java +++ b/libs/processing-core/src/main/java/processing/android/PermissionRequestor.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2017 The Processing Foundation + 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 @@ -24,8 +24,10 @@ import android.app.Activity; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; + 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 { @@ -48,6 +50,7 @@ protected void onStart() { } @Override + @SuppressWarnings("RestrictedApi") public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { Bundle resultData = new Bundle(); resultData.putStringArray(KEY_PERMISSIONS, permissions); diff --git a/core/src/processing/android/ServiceEngine.java b/libs/processing-core/src/main/java/processing/android/ServiceEngine.java similarity index 96% rename from core/src/processing/android/ServiceEngine.java rename to libs/processing-core/src/main/java/processing/android/ServiceEngine.java index 4ca043075..11b1164f0 100644 --- a/core/src/processing/android/ServiceEngine.java +++ b/libs/processing-core/src/main/java/processing/android/ServiceEngine.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2017 The Processing Foundation + 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 @@ -23,6 +23,7 @@ package processing.android; import android.graphics.Rect; + import processing.core.PConstants; public interface ServiceEngine extends PConstants { diff --git a/core/src/processing/core/PApplet.java b/libs/processing-core/src/main/java/processing/core/PApplet.java similarity index 66% rename from core/src/processing/core/PApplet.java rename to libs/processing-core/src/main/java/processing/core/PApplet.java index 0b1607db1..2b55cf67c 100644 --- a/core/src/processing/core/PApplet.java +++ b/libs/processing-core/src/main/java/processing/core/PApplet.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -24,29 +24,23 @@ 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.FragmentManager; -import android.view.Window; -import android.view.inputmethod.InputMethodManager; import android.app.Activity; -import android.content.*; +import android.app.FragmentManager; +import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.AssetManager; -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.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.support.annotation.LayoutRes; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -55,14 +49,63 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; -import android.view.ContextMenu.ContextMenuInfo; +import android.view.Window; +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.*; -import processing.event.*; -import processing.opengl.*; +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 { @@ -178,6 +221,7 @@ public class PApplet extends Object implements ActivityAPI, PConstants { */ public int pmouseX, pmouseY; + public int mouseButton; public boolean mousePressed; @@ -872,7 +916,7 @@ 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) { @@ -1248,10 +1292,6 @@ public void setup() { } - public void calculate() { - } - - public void draw() { // if no draw method, then shut things down //System.out.println("no draw method, goodbye"); @@ -2093,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. @@ -2243,47 +2283,56 @@ protected void nativeMotionEvent(MotionEvent motionEvent) { protected void enqueueTouchEvents(MotionEvent event, int button, int modifiers) { - int action = event.getAction(); - int actionMasked = action & MotionEvent.ACTION_MASK; - int paction = 0; + int actionMasked = event.getActionMasked(); + int pAction = 0; + int pointerUp = 0; + int pointerUpIdx = -1; switch (actionMasked) { case MotionEvent.ACTION_DOWN: - paction = TouchEvent.START; + pAction = TouchEvent.START; break; case MotionEvent.ACTION_POINTER_DOWN: - paction = TouchEvent.START; + pAction = TouchEvent.START; break; case MotionEvent.ACTION_MOVE: - paction = TouchEvent.MOVE; + pAction = TouchEvent.MOVE; break; case MotionEvent.ACTION_UP: - paction = TouchEvent.END; + pAction = TouchEvent.END; break; case MotionEvent.ACTION_POINTER_UP: - paction = TouchEvent.END; + 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; + pAction = TouchEvent.CANCEL; break; } - if (paction == TouchEvent.START || paction == TouchEvent.END) { + if (pAction == TouchEvent.START || pAction == TouchEvent.END || pAction == TouchEvent.CANCEL) { touchPointerId = event.getPointerId(0); } - int pointerCount = event.getPointerCount(); + // 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; 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(pointerCount); - for (int p = 0; p < pointerCount; p++) { - touchEvent.setPointer(p, event.getPointerId(p), event.getHistoricalX(p, h), event.getHistoricalY(p, h), - event.getHistoricalSize(p, h), event.getHistoricalPressure(p, 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); } @@ -2291,16 +2340,18 @@ protected void enqueueTouchEvents(MotionEvent event, int button, int modifiers) // Current event TouchEvent touchEvent = new TouchEvent(event, event.getEventTime(), - paction, modifiers, button); - if (actionMasked == MotionEvent.ACTION_UP) { + 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(pointerCount); - for (int p = 0; p < event.getPointerCount(); p++) { - touchEvent.setPointer(p, event.getPointerId(p), event.getX(p), event.getY(p), - event.getSize(p), event.getPressure(p)); + 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); @@ -2308,12 +2359,12 @@ protected void enqueueTouchEvents(MotionEvent event, int button, int modifiers) protected void enqueueMouseEvents(MotionEvent event, int button, int modifiers) { - int action = event.getAction(); + int actionMasked = event.getActionMasked(); int clickCount = 1; // not really set... (i.e. not catching double taps) int index; - switch (action & MotionEvent.ACTION_MASK) { + switch (actionMasked) { case MotionEvent.ACTION_DOWN: mousePointerId = event.getPointerId(0); postEvent(new MouseEvent(event, event.getEventTime(), @@ -2436,66 +2487,6 @@ public void touchCancelled(TouchEvent event) { ////////////////////////////////////////////////////////////// - // Wallpaper and wear API - - - public boolean wallpaperPreview() { - return surface.getEngine().isPreview(); - } - - - public float wallpaperOffset() { - return surface.getEngine().getXOffset(); - } - - - 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(); - } - - - ////////////////////////////////////////////////////////////// - // KeyEvent[] keyEventQueue = new KeyEvent[10]; // int keyEventCount; @@ -4006,13 +3997,6 @@ public void noiseSeed(long seed) { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . -// protected String[] loadImageFormats; - - -// public PImage loadImage(String filename) { -// return loadImage(filename, null); -// } - public PImage loadImage(String filename) { //, Object params) { // return loadImage(filename, null); @@ -4044,86 +4028,9 @@ public PImage loadImage(String filename) { //, Object params) { } - /* 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) { @@ -4276,12 +4183,28 @@ public JSONObject parseJSONObject(String input) { * @see PApplet#saveJSONArray(JSONArray, String) */ public JSONObject loadJSONObject(String filename) { - return new JSONObject(createReader(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) { - return new JSONObject(createReader(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; } @@ -4327,12 +4250,28 @@ public JSONArray parseJSONArray(String input) { * @see PApplet#saveJSONArray(JSONArray, String) */ public JSONArray loadJSONArray(String filename) { - return new JSONArray(createReader(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) { - return new JSONArray(createReader(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; } @@ -5862,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); } @@ -5884,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); @@ -6178,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); @@ -6189,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); @@ -6632,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); } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -6690,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; } @@ -6979,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; @@ -7029,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; } @@ -7518,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 @@ -8081,175 +8053,374 @@ public void updatePixels(int x1, int y1, int x2, int y2) { ////////////////////////////////////////////////////////////// - // everything below this line is automatically generated. no touch. - // public functions for processing.core + // ANDROID-SPECIFIC API - /** - * 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 - */ - public void setCache(PImage image, Object storage) { - g.setCache(image, storage); + // Wallpaper and wear API + + + public boolean wallpaperPreview() { + return surface.getEngine().isPreview(); } - /** - * 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 - */ - public Object getCache(PImage image) { - return g.getCache(image); + public float wallpaperOffset() { + return surface.getEngine().getXOffset(); } - /** - * Remove information associated with this renderer from the cache, if any. - * @param renderer The PGraphics renderer whose cache data should be removed - */ - public void removeCache(PImage image) { - g.removeCache(image); + public int wallpaperHomeCount() { + float step = surface.getEngine().getXOffsetStep(); + if (0 < step) { + return (int)(1 + 1 / step); + } else { + return 1; + } } - public void flush() { - g.flush(); + public boolean wearAmbient() { + return surface.getEngine().isInAmbientMode(); } - public PGL beginPGL() { - return g.beginPGL(); + public boolean wearInteractive() { + return !surface.getEngine().isInAmbientMode(); } - public void endPGL() { - g.endPGL(); + public boolean wearRound() { + return surface.getEngine().isRound(); } - public void hint(int which) { - g.hint(which); + public boolean wearSquare() { + return !surface.getEngine().isRound(); } - /** - * Start a new shape of type POLYGON - */ - public void beginShape() { - g.beginShape(); + public Rect wearInsets() { + return surface.getEngine().getInsets(); } - /** - * 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. - */ - public void beginShape(int kind) { - g.beginShape(kind); + public boolean wearLowBit() { + return surface.getEngine().useLowBitAmbient(); } - /** - * Sets whether the upcoming vertex is part of an edge. - * Equivalent to glEdgeFlag(), for people familiar with OpenGL. - */ - public void edge(boolean edge) { - g.edge(edge); + public boolean wearBurnIn() { + return surface.getEngine().requireBurnInProtection(); } - /** - * 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(). - */ - public void normal(float nx, float ny, float nz) { - g.normal(nx, ny, nz); + // Ray casting API + + + public PVector[] getRayFromScreen(float screenX, float screenY, PVector[] ray) { + return g.getRayFromScreen(screenX, screenY, ray); } - public void attribPosition(String name, float x, float y, float z) { - g.attribPosition(name, x, y, z); + public void getRayFromScreen(float screenX, float screenY, PVector origin, PVector direction) { + g.getRayFromScreen(screenX, screenY, origin, direction); } - public void attribNormal(String name, float nx, float ny, float nz) { - g.attribNormal(name, nx, ny, nz); + public boolean intersectsSphere(float r, float screenX, float screenY) { + return g.intersectsSphere(r, screenX, screenY); } - public void attribColor(String name, int color) { - g.attribColor(name, color); + public boolean intersectsSphere(float r, PVector origin, PVector direction) { + return g.intersectsSphere(r, origin, direction); } - public void attrib(String name, float... values) { - g.attrib(name, values); + public boolean intersectsBox(float w, float screenX, float screenY) { + return g.intersectsBox(w, screenX, screenY); } - public void attrib(String name, int... values) { - g.attrib(name, values); + public boolean intersectsBox(float w, float h, float d, float screenX, float screenY) { + return g.intersectsBox(w, h, d, screenX, screenY); } - public void attrib(String name, boolean... values) { - g.attrib(name, values); + public boolean intersectsBox(float size, PVector origin, PVector direction) { + return g.intersectsBox(size, origin, direction); } - /** - * Set texture mode to either to use coordinates based on the IMAGE - * (more intuitive for new users) or NORMALIZED (better for advanced chaps) - */ - public void textureMode(int mode) { - g.textureMode(mode); + public boolean intersectsBox(float w, float h, float d, PVector origin, PVector direction) { + return g.intersectsBox(w, h, d, origin, direction); } - public void textureWrap(int wrap) { - g.textureWrap(wrap); + public PVector intersectsPlane(float screenX, float screenY) { + return g.intersectsPlane(screenX, screenY); } - /** - * Set texture image for current shape. - * Needs to be called between @see beginShape and @see endShape - * + public PVector intersectsPlane(PVector origin, PVector direction) { + return g.intersectsPlane(origin, direction); + } + + + public void eye() { + g.eye(); + } + + + public void calculate() { + } + + + /** + * Sets the coordinate system in 3D centered at (width/2, height/2) + * and with the Y axis pointing up. + */ + + public void cameraUp() { + g.cameraUp(); + } + + + /** + * Returns a copy of the current object matrix. + * Pass in null to create a new matrix. + */ + public PMatrix3D getObjectMatrix() { + return g.getObjectMatrix(); + } + + + /** + * Copy the current object matrix into the specified target. + * Pass in null to create a new matrix. + */ + public PMatrix3D getObjectMatrix(PMatrix3D target) { + return g.getObjectMatrix(target); + } + + + /** + * 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(); + } + + + public void endPGL() { + g.endPGL(); + } + + + public void flush() { + g.flush(); + } + + + public void hint(int which) { + g.hint(which); + } + + + /** + * Start a new shape of type POLYGON + */ + public void beginShape() { + g.beginShape(); + } + + + /** + * ( 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); + } + + + /** + * Sets whether the upcoming vertex is part of an edge. + * Equivalent to glEdgeFlag(), for people familiar with OpenGL. + */ + public void edge(boolean edge) { + g.edge(edge); + } + + + /** + * ( 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); + } + + + /** + * ( 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); + } + + + /** + * ( 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); @@ -8258,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() { @@ -8291,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(); } @@ -8317,28 +8520,43 @@ 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 loadShape(String filename) { - return g.loadShape(filename); + /** + * @nowebref + */ + public PShape loadShape(String filename, String options) { + return g.loadShape(filename, options); } @@ -8367,43 +8585,128 @@ public PShape createShape(int kind, float... 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); } + /** + * ( 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); } @@ -8414,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) { @@ -8421,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) { @@ -8522,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); @@ -8556,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: *

@@ -8576,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); @@ -8583,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);
@@ -8608,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); @@ -8615,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. @@ -8650,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, @@ -8668,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); @@ -8678,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:

@@ -8711,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, @@ -8720,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, @@ -8729,22 +9569,83 @@ public void curve(float x1, float y1, float z1, /** - * The mode can only be set to CORNERS, CORNER, and CENTER. - *

- * Support for CENTER was added in release 0146. + * ( 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 imageMode(int mode) { g.imageMode(mode); } - public void image(PImage image, float x, float y) { - g.image(image, x, y); + /** + * ( 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 image(PImage img, float a, float b) { + g.image(img, a, b); } - public void image(PImage image, float x, float y, float c, float d) { - g.image(image, x, y, c, d); + /** + * @param c width to display the image by default + * @param d height to display the image by default + */ + public void image(PImage img, float a, float b, float c, float d) { + g.image(img, a, b, c, d); } @@ -8752,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); @@ -8775,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) { @@ -8782,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); @@ -8807,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(); @@ -8817,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(); @@ -8827,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); @@ -8837,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); @@ -8845,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); @@ -8855,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); @@ -8867,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); @@ -8889,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); @@ -8899,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); @@ -8907,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 @@ -8917,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. */ @@ -8925,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). @@ -8937,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); @@ -8959,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); @@ -8971,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(); @@ -8979,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(); @@ -8987,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 ) * - * 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. + * 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(). + * + * ( 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); @@ -9017,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); @@ -9025,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); @@ -9033,12 +10393,32 @@ public void rotateY(float angle) { /** - * Rotate around the Z axis. + * ( begin auto-generated from rotateZ.xml ) * - * 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. + * 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. + * + * ( 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); @@ -9046,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); @@ -9062,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); @@ -9081,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); @@ -9089,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); @@ -9097,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); } @@ -9115,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) { @@ -9129,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, @@ -9162,42 +10670,6 @@ public PMatrix3D getMatrix(PMatrix3D target) { } - /** - * Returns a copy of the current object matrix. - * Pass in null to create a new matrix. - */ - public PMatrix3D getObjectMatrix() { - return g.getObjectMatrix(); - } - - - /** - * Copy the current object matrix into the specified target. - * Pass in null to create a new matrix. - */ - public PMatrix3D getObjectMatrix(PMatrix3D target) { - return g.getObjectMatrix(target); - } - - - /** - * 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); - } - - /** * Set the current transformation matrix to the contents of another. */ @@ -9223,33 +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(); } - public void cameraUp() { - g.cameraUp(); - } - - + /** + * ( 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) { @@ -9257,27 +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(); } - public void eye() { - g.eye(); - } - - + /** + * ( 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) { @@ -9285,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) { @@ -9302,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); @@ -9318,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); @@ -9328,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); @@ -9340,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); @@ -9352,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); @@ -9368,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); @@ -9382,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); @@ -9390,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(); } @@ -9412,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); } @@ -9456,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); } @@ -9494,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); } @@ -9532,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); @@ -9660,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); @@ -9668,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(); } @@ -9717,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. */ @@ -9861,40 +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(); - } - - - public void setNative(Object nativeObject) { - g.setNative(nativeObject); - } - - - /** + * ( 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). @@ -9911,16 +12367,21 @@ public void setNative(Object nativeObject) { * 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 @@ -9932,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); @@ -9947,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); @@ -9957,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 @@ -9966,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. *

*

    @@ -9999,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); @@ -10027,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) { @@ -10037,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, @@ -10046,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); @@ -10057,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 97% rename from core/src/processing/core/PConstants.java rename to libs/processing-core/src/main/java/processing/core/PConstants.java index 05519d2a9..14753a133 100644 --- a/core/src/processing/core/PConstants.java +++ b/libs/processing-core/src/main/java/processing/core/PConstants.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -46,10 +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.PGraphicsVRStereo"; - static final String MONO = "processing.vr.PGraphicsVRMono"; + 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. diff --git a/core/src/processing/core/PFont.java b/libs/processing-core/src/main/java/processing/core/PFont.java similarity index 99% rename from core/src/processing/core/PFont.java rename to libs/processing-core/src/main/java/processing/core/PFont.java index 6d30c539c..48a01b3cc 100644 --- a/core/src/processing/core/PFont.java +++ b/libs/processing-core/src/main/java/processing/core/PFont.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/core/PGraphics.java b/libs/processing-core/src/main/java/processing/core/PGraphics.java similarity index 97% rename from core/src/processing/core/PGraphics.java rename to libs/processing-core/src/main/java/processing/core/PGraphics.java index e8598a711..dc1657cb2 100644 --- a/core/src/processing/core/PGraphics.java +++ b/libs/processing-core/src/main/java/processing/core/PGraphics.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -760,10 +760,10 @@ public PSurface createSurface(AppComponent component, SurfaceHolder holder, bool * 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); } @@ -773,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 @@ -1584,6 +1582,12 @@ public PShape loadShape(String filename) { } + public PShape loadShape(String filename, String options) { + showMissingWarning("loadShape"); + return null; + } + + ////////////////////////////////////////////////////////////// // SHAPE CREATION @@ -2206,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); + } + ////////////////////////////////////////////////////////////// @@ -2325,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); + } + ////////////////////////////////////////////////////////////// @@ -3382,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. @@ -3452,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) @@ -3517,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. */ @@ -3535,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 @@ -3861,6 +3927,22 @@ protected void textCharModelImpl(PImage glyph, } + ////////////////////////////////////////////////////////////// + + // PARITY WITH P5.JS + + + public void push() { + pushStyle(); + pushMatrix(); + } + + + public void pop() { + popStyle(); + popMatrix(); + } + ////////////////////////////////////////////////////////////// @@ -4352,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; + } + ////////////////////////////////////////////////////////////// @@ -5557,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; @@ -5574,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) { @@ -5588,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); @@ -5620,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 diff --git a/core/src/processing/core/PImage.java b/libs/processing-core/src/main/java/processing/core/PImage.java similarity index 99% rename from core/src/processing/core/PImage.java rename to libs/processing-core/src/main/java/processing/core/PImage.java index b491cc814..8eceaf2c4 100644 --- a/core/src/processing/core/PImage.java +++ b/libs/processing-core/src/main/java/processing/core/PImage.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -563,6 +563,11 @@ public PImage get() { } + public PImage copy() { + return get(0, 0, pixelWidth, pixelHeight); + } + + /** * Set a single pixel to the specified color. */ diff --git a/core/src/processing/core/PMatrix.java b/libs/processing-core/src/main/java/processing/core/PMatrix.java similarity index 98% rename from core/src/processing/core/PMatrix.java rename to libs/processing-core/src/main/java/processing/core/PMatrix.java index 52e14a823..1c6fabc3f 100644 --- a/core/src/processing/core/PMatrix.java +++ b/libs/processing-core/src/main/java/processing/core/PMatrix.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + 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 diff --git a/core/src/processing/core/PMatrix2D.java b/libs/processing-core/src/main/java/processing/core/PMatrix2D.java similarity index 99% rename from core/src/processing/core/PMatrix2D.java rename to libs/processing-core/src/main/java/processing/core/PMatrix2D.java index 31226e78a..8d13979b7 100644 --- a/core/src/processing/core/PMatrix2D.java +++ b/libs/processing-core/src/main/java/processing/core/PMatrix2D.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + 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 diff --git a/core/src/processing/core/PMatrix3D.java b/libs/processing-core/src/main/java/processing/core/PMatrix3D.java similarity index 99% rename from core/src/processing/core/PMatrix3D.java rename to libs/processing-core/src/main/java/processing/core/PMatrix3D.java index fbd662821..9167af1f0 100644 --- a/core/src/processing/core/PMatrix3D.java +++ b/libs/processing-core/src/main/java/processing/core/PMatrix3D.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + 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 diff --git a/core/src/processing/core/PShape.java b/libs/processing-core/src/main/java/processing/core/PShape.java similarity index 90% rename from core/src/processing/core/PShape.java rename to libs/processing-core/src/main/java/processing/core/PShape.java index 68267d35b..a609a9e4e 100644 --- a/core/src/processing/core/PShape.java +++ b/libs/processing-core/src/main/java/processing/core/PShape.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + 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 @@ -23,11 +23,12 @@ package processing.core; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + import java.util.HashMap; import java.util.Map; -import processing.core.PApplet; - /** * ( begin auto-generated from PShape.xml ) @@ -85,12 +86,16 @@ public class PShape implements PConstants { // /** Generic, only draws its child objects. */ // static public final int GROUP = 0; // GROUP now inherited from PConstants, and is still zero + + // These constants were updated in 3.0b6 so that they could be distinguished + // from others in PConstants and improve how some typos were handled. + // https://github.com/processing/processing/issues/3776 /** A line, ellipse, arc, image, etc. */ - static public final int PRIMITIVE = 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; @@ -103,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()"; @@ -119,11 +125,9 @@ public class PShape implements PConstants { public static final String NOT_A_SIMPLE_VERTEX = "%1$s can not be called on quadratic or bezier vertices"; - // boundary box of this shape - //protected float x; - //protected float y; - //protected float width; - //protected float height; + static public final String PER_VERTEX_UNSUPPORTED = + "This renderer does not support %1$s for individual vertices"; + /** * ( begin auto-generated from PShape_width.xml ) * @@ -338,10 +342,8 @@ public PShape(PGraphics g, 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 = g.rectMode; + ellipseMode = g.ellipseMode; // normalX = normalY = 0; // normalZ = 1; @@ -710,8 +712,7 @@ public void vertex(float x, float y, float u, float v) { public void vertex(float x, float y, float z) { - // why not? - vertex(x, y); + vertex(x, y); // maybe? maybe not? } @@ -1550,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; @@ -1604,9 +1608,9 @@ protected void drawImpl(PGraphics g) { } else if (family == PRIMITIVE) { drawPrimitive(g); } else if (family == GEOMETRY) { - // same as path - drawPath(g); -// drawGeometry(g); + // Not same as path: `kind` matters. +// drawPath(g); + drawGeometry(g); } else if (family == PATH) { drawPath(g); } @@ -1645,26 +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 { - if(params.length != 5){ - g.rectMode(CORNER); - } - else{ - g.rectMode((int) params[4]); + 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.rect(params[0], params[1], params[2], params[3]); + 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) { @@ -1856,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; + } + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -2016,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); } @@ -2348,7 +2464,9 @@ public int getFill(int index) { } } - + /** + * @nowebref + */ public void setFill(boolean fill) { if (openShape) { PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()"); @@ -2359,6 +2477,24 @@ 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()"); @@ -2367,28 +2503,33 @@ public void setFill(int fill) { this.fillColor = fill; - if (vertices != null) { - for (int i = 0; i < vertices.length; i++) { + 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) { + 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; @@ -2400,8 +2541,7 @@ 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getTint()"); return this.tintColor; } @@ -2482,6 +2622,9 @@ public int getStroke(int index) { } + /** + * @nowebref + */ public void setStroke(boolean stroke) { if (openShape) { PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()"); @@ -2492,6 +2635,24 @@ 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()"); @@ -2499,23 +2660,31 @@ public void setStroke(int stroke) { } strokeColor = stroke; - if (vertices != null) { - for (int i = 0; i < vertices.length; i++) { + + 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setStroke()"); return; } @@ -2529,8 +2698,7 @@ 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getStrokeWeight()"); return strokeWeight; } @@ -2548,8 +2716,8 @@ public void setStrokeWeight(float weight) { strokeWeight = weight; - if (vertices != null) { - for (int i = 0; i < vertices.length; i++) { + if (vertices != null && perVertexStyles) { + for (int i = 0; i < vertexCount; i++) { setStrokeWeight(i, weight); } } @@ -2562,9 +2730,13 @@ 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setStrokeWeight()"); return; } @@ -2596,8 +2768,7 @@ 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getAmbient()"); return ambientColor; } @@ -2632,8 +2803,7 @@ public void setAmbient(int index, int ambient) { } // make sure we allocated the vertices array and that vertex exists - if (vertices == null || - index >= vertices.length) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setAmbient()"); return; } @@ -2646,8 +2816,7 @@ 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getSpecular()"); return specularColor; } @@ -2682,8 +2851,7 @@ public void setSpecular(int index, int specular) { } // make sure we allocated the vertices array and that vertex exists - if (vertices == null || - index >= vertices.length) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setSpecular()"); return; } @@ -2696,8 +2864,7 @@ 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) { + if (vertices == null || index >= vertices.length) { PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getEmissive()"); return emissiveColor; } @@ -2822,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]) + @@ -2837,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."); } } @@ -2871,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() @@ -2883,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); @@ -3373,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 75% rename from core/src/processing/core/PShapeOBJ.java rename to libs/processing-core/src/main/java/processing/core/PShapeOBJ.java index ca4d953f5..470f3820a 100644 --- a/core/src/processing/core/PShapeOBJ.java +++ b/libs/processing-core/src/main/java/processing/core/PShapeOBJ.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 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 @@ -23,9 +23,10 @@ package processing.core; import java.io.BufferedReader; +import java.io.File; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import java.util.Hashtable; + /** * This class is not part of the Processing API and should not be used @@ -45,18 +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; @@ -95,10 +99,10 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl, vertexCount = face.vertIdx.size(); vertices = new float[vertexCount][12]; for (int j = 0; j < face.vertIdx.size(); j++){ - int vertIdx, normIdx, texIdx; - PVector vert, norms, tex; + int vertIdx, normIdx; + PVector vert, norms; - vert = norms = tex = null; + vert = norms = null; vertIdx = face.vertIdx.get(j).intValue() - 1; vert = coords.get(vertIdx); @@ -110,13 +114,6 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl, } } - if (j < face.texIdx.size()) { - texIdx = face.texIdx.get(j).intValue() - 1; - if (-1 < texIdx) { - tex = texcoords.get(texIdx); - } - } - vertices[j][X] = vert.x; vertices[j][Y] = vert.y; vertices[j][Z] = vert.z; @@ -132,13 +129,23 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl, vertices[j][PGraphics.NZ] = norms.z; } - if (tex != null) { - vertices[j][PGraphics.U] = tex.x; - vertices[j][PGraphics.V] = tex.y; - } - if (mtl != null && mtl.kdMap != null) { + // This face is textured. + int texIdx; + PVector tex = null; + + if (j < face.texIdx.size()) { + texIdx = face.texIdx.get(j).intValue() - 1; + if (-1 < texIdx) { + tex = texcoords.get(texIdx); + } + } + image = mtl.kdMap; + if (tex != null) { + vertices[j][PGraphics.U] = tex.x; + vertices[j][PGraphics.V] = tex.y; + } } } } @@ -168,14 +175,14 @@ 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, ArrayList coords, ArrayList normals, ArrayList texcoords) { - Map mtlTable = new HashMap(); + Hashtable mtlTable = new Hashtable(); int mtlIdxCur = -1; boolean readv, readvn, readvt; try { @@ -184,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 @@ -231,20 +238,22 @@ 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) { 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, fn, mreader, materials, mtlTable); - mreader.close(); + parseMTL(parent, path, + mreader, materials, mtlTable); } } } else if (parts[0].equals("g")) { @@ -332,10 +341,10 @@ static protected void parseOBJ(PApplet parent, } - static protected void parseMTL(PApplet parent, String mtlfn, + static protected void parseMTL(PApplet parent, String path, BufferedReader reader, ArrayList materials, - Map materialsHash) { + Hashtable materialsHash) { try { String line; OBJMaterial currentMtl = null; @@ -348,46 +357,39 @@ static protected void parseMTL(PApplet parent, String mtlfn, if (parts[0].equals("newmtl")) { // Starting new material. String mtlname = parts[1]; - currentMtl = addMaterial(mtlname, materials, materialsHash); - } else { - if (currentMtl == null) { - currentMtl = addMaterial("material" + materials.size(), - materials, materialsHash); - } - if (parts[0].equals("map_Kd") && parts.length > 1) { - // Loading texture map. - String texname = parts[1]; - currentMtl.kdMap = parent.loadImage(texname); - if (currentMtl.kdMap == null) { - System.err.println("The texture map \"" + texname + "\" " + - "in the materials definition file \"" + mtlfn + "\" " + - "is missing or inaccessible, make sure " + - "the URL is valid or that the file has been " + - "added to your sketch and is readable."); - } - } else if (parts[0].equals("Ka") && parts.length > 3) { - // The ambient color of the material - currentMtl.ka.x = Float.valueOf(parts[1]).floatValue(); - currentMtl.ka.y = Float.valueOf(parts[2]).floatValue(); - currentMtl.ka.z = Float.valueOf(parts[3]).floatValue(); - } else if (parts[0].equals("Kd") && parts.length > 3) { - // The diffuse color of the material - currentMtl.kd.x = Float.valueOf(parts[1]).floatValue(); - currentMtl.kd.y = Float.valueOf(parts[2]).floatValue(); - currentMtl.kd.z = Float.valueOf(parts[3]).floatValue(); - } else if (parts[0].equals("Ks") && parts.length > 3) { - // The specular color weighted by the specular coefficient - currentMtl.ks.x = Float.valueOf(parts[1]).floatValue(); - 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) { - // Reading the alpha transparency. - currentMtl.d = Float.valueOf(parts[1]).floatValue(); - } else if (parts[0].equals("Ns") && parts.length > 1) { - // The specular component of the Phong shading model - currentMtl.ns = Float.valueOf(parts[1]).floatValue(); + currentMtl = new OBJMaterial(mtlname); + 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 + currentMtl.ka.x = Float.valueOf(parts[1]).floatValue(); + currentMtl.ka.y = Float.valueOf(parts[2]).floatValue(); + currentMtl.ka.z = Float.valueOf(parts[3]).floatValue(); + } else if (parts[0].equals("Kd") && parts.length > 3) { + // The diffuse color of the material + currentMtl.kd.x = Float.valueOf(parts[1]).floatValue(); + currentMtl.kd.y = Float.valueOf(parts[2]).floatValue(); + currentMtl.kd.z = Float.valueOf(parts[3]).floatValue(); + } else if (parts[0].equals("Ks") && parts.length > 3) { + // The specular color weighted by the specular coefficient + currentMtl.ks.x = Float.valueOf(parts[1]).floatValue(); + 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) { + // Reading the alpha transparency. + currentMtl.d = Float.valueOf(parts[1]).floatValue(); + } else if (parts[0].equals("Ns") && parts.length > 1) { + // The specular component of the Phong shading model + currentMtl.ns = Float.valueOf(parts[1]).floatValue(); } } } @@ -396,27 +398,19 @@ static protected void parseMTL(PApplet parent, String mtlfn, } } - protected static OBJMaterial addMaterial(String mtlname, - ArrayList materials, - Map materialsHash) { - OBJMaterial currentMtl = new OBJMaterial(mtlname); - materialsHash.put(mtlname, Integer.valueOf(materials.size())); - materials.add(currentMtl); - return currentMtl; - } 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); } @@ -438,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; @@ -462,4 +464,5 @@ static protected class OBJMaterial { kdMap = null; } } -} \ No newline at end of file +} + diff --git a/core/src/processing/core/PShapeSVG.java b/libs/processing-core/src/main/java/processing/core/PShapeSVG.java similarity index 70% rename from core/src/processing/core/PShapeSVG.java rename to libs/processing-core/src/main/java/processing/core/PShapeSVG.java index bcbf15f63..33bd932b8 100644 --- a/core/src/processing/core/PShapeSVG.java +++ b/libs/processing-core/src/main/java/processing/core/PShapeSVG.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2006-12 Ben Fry and Casey Reas Copyright (c) 2004-06 Michael Chang @@ -24,11 +24,21 @@ package processing.core; - +//import static java.awt.Font.BOLD; +//import static java.awt.Font.ITALIC; +//import static java.awt.Font.PLAIN; import processing.data.*; -import java.util.HashMap; + +// TODO replace these with PMatrix2D +import android.graphics.Matrix; +//import java.awt.geom.AffineTransform; +//import java.awt.geom.Point2D; + import java.util.Map; -import android.graphics.*; +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 @@ -156,14 +166,14 @@ protected PShapeSVG(PShapeSVG parent, XML properties, boolean parseKids) { // Negative size is illegal. if (width < 0 || height < 0) throw new RuntimeException(": width (" + width + - ") and height (" + height + ") must not be negative."); + ") 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."); + "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. @@ -326,6 +336,10 @@ protected PShape parseChild(XML elem) { 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); @@ -354,8 +368,11 @@ protected PShape parseChild(XML elem) { // return new FontGlyph(this, elem); } else if (name.equals("text")) { // || name.equals("font")) { - PGraphics.showWarning("Text and fonts in SVG files are " + - "not currently supported, convert text to outlines instead."); + 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."); @@ -390,10 +407,10 @@ 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) + getFloatWithUnit(element, "x1", svgWidth), + getFloatWithUnit(element, "y1", svgHeight), + getFloatWithUnit(element, "x2", svgWidth), + getFloatWithUnit(element, "y2", svgHeight) }; } @@ -429,14 +446,29 @@ 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) + 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 @@ -448,14 +480,27 @@ protected void parsePoly(boolean close) { String pointsAttr = element.getString("points"); if (pointsAttr != null) { - String[] pointsBuffer = PApplet.splitTokens(pointsAttr); - vertexCount = pointsBuffer.length; + 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++) { - String pb[] = PApplet.splitTokens(pointsBuffer[i], ", \t\r\n"); - vertices[i][X] = Float.parseFloat(pb[0]); - vertices[i][Y] = Float.parseFloat(pb[1]); + 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]); +// } } } @@ -514,7 +559,7 @@ protected void parsePath() { // use whitespace constant to get rid of extra spaces and CR or LF String[] pathTokens = - PApplet.splitTokens(pathBuffer.toString(), "|" + WHITESPACE); + PApplet.splitTokens(pathBuffer.toString(), "|" + WHITESPACE); vertices = new float[pathTokens.length][2]; vertexCodes = new int[pathTokens.length]; @@ -540,302 +585,302 @@ protected void parsePath() { } 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 (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; - 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; + // horizontal lineto absolute + case 'H': + cx = PApplet.parseFloat(pathTokens[i + 1]); + parsePathLineto(cx, cy); + i += 2; + break; - case 'L': - cx = PApplet.parseFloat(pathTokens[i + 1]); - cy = PApplet.parseFloat(pathTokens[i + 2]); - parsePathLineto(cx, cy); - i += 3; - 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; - case 'l': - cx = cx + PApplet.parseFloat(pathTokens[i + 1]); - cy = cy + PApplet.parseFloat(pathTokens[i + 2]); - parsePathLineto(cx, cy); - i += 3; + // 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; - // horizontal lineto absolute - case 'H': - cx = PApplet.parseFloat(pathTokens[i + 1]); - parsePathLineto(cx, cy); - i += 2; + // 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; - // horizontal lineto relative - case 'h': - cx = cx + PApplet.parseFloat(pathTokens[i + 1]); - parsePathLineto(cx, cy); - i += 2; + // 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; - case 'V': - cy = PApplet.parseFloat(pathTokens[i + 1]); - parsePathLineto(cx, cy); - i += 2; + // 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; - case 'v': - cy = cy + PApplet.parseFloat(pathTokens[i + 1]); - parsePathLineto(cx, cy); - i += 2; + // 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; - // 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); + // 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; } - 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; + 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); + // 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; } - 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); + 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; } - 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++; + // 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; - 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]); + // 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; } @@ -952,7 +997,7 @@ private void parsePathArcto(float x1, float y1, 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); + 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; } @@ -976,7 +1021,7 @@ private void parsePathArcto(float x1, float y1, float inc = phiDelta / segmentCount; float a = PApplet.sin(inc) * - (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3; + (PApplet.sqrt(4 + 3 * PApplet.sq(PApplet.tan(inc / 2))) - 1) / 3; float sinPhi1 = PApplet.sin(phi1), cosPhi1 = PApplet.cos(phi1); @@ -1297,23 +1342,23 @@ static protected int parseSimpleColor(String colorText) { * 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 } + { "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 } }); /* @@ -1483,7 +1528,7 @@ public Gradient(PShapeSVG parent, XML properties) { if (opacityStr == null) opacityStr = "1"; } int tupacity = PApplet.constrain( - (int)(PApplet.parseFloat(opacityStr) * 255), 0, 255); + (int)(PApplet.parseFloat(opacityStr) * 255), 0, 255); color[count] = (tupacity << 24) | parseSimpleColor(colorStr); count++; } @@ -1506,16 +1551,16 @@ public LinearGradient(PShapeSVG parent, XML properties) { this.y2 = getFloatWithUnit(properties, "y2", svgHeight); String transformStr = - properties.getString("gradientTransform"); + 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 + 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); @@ -1549,16 +1594,16 @@ public RadialGradient(PShapeSVG parent, XML properties) { this.r = getFloatWithUnit(properties, "r", svgSizeXY); String transformStr = - properties.getString("gradientTransform"); + 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 + 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); @@ -1582,7 +1627,183 @@ public RadialGradient(PShapeSVG parent, XML properties) { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - public static class Font extends PShapeSVG { +// 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; @@ -1603,8 +1824,8 @@ public Font(PShapeSVG parent, XML properties) { horizAdvX = properties.getInt("horiz-adv-x", 0); - namedGlyphs = new HashMap(); - unicodeGlyphs = new HashMap(); + namedGlyphs = new HashMap<>(); + unicodeGlyphs = new HashMap<>(); glyphCount = 0; glyphs = new FontGlyph[elements.length]; @@ -1699,7 +1920,7 @@ public float textWidth(String str, float size) { static class FontFace extends PShapeSVG { int horizOriginX; // dflt 0 int horizOriginY; // dflt 0 -// int horizAdvX; // no dflt? + // int horizAdvX; // no dflt? int vertOriginX; // dflt horizAdvX/2 int vertOriginY; // dflt ascent int vertAdvY; // dflt 1em (unitsPerEm value) @@ -1733,7 +1954,7 @@ protected void drawShape() { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - public static class FontGlyph extends PShapeSVG { // extends Path + static public class FontGlyph extends PShapeSVG { // extends Path public String name; char unicode; int horizAdvX; @@ -1751,7 +1972,7 @@ public FontGlyph(PShapeSVG parent, XML properties, Font font) { //System.out.println("unicode for " + name + " is " + u); } else { System.err.println("unicode for " + name + - " is more than one char: " + u); + " is more than one char: " + u); } } if (properties.hasAttribute("horiz-adv-x")) { diff --git a/core/src/processing/core/PStyle.java b/libs/processing-core/src/main/java/processing/core/PStyle.java similarity index 92% rename from core/src/processing/core/PStyle.java rename to libs/processing-core/src/main/java/processing/core/PStyle.java index e52ddc3f2..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 diff --git a/core/src/processing/core/PSurface.java b/libs/processing-core/src/main/java/processing/core/PSurface.java similarity index 98% rename from core/src/processing/core/PSurface.java rename to libs/processing-core/src/main/java/processing/core/PSurface.java index 4cc0dc74f..4a2ec763b 100644 --- a/core/src/processing/core/PSurface.java +++ b/libs/processing-core/src/main/java/processing/core/PSurface.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -22,9 +22,6 @@ package processing.core; -import java.io.File; -import java.io.InputStream; - import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -36,6 +33,10 @@ 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; diff --git a/core/src/processing/core/PSurfaceNone.java b/libs/processing-core/src/main/java/processing/core/PSurfaceNone.java similarity index 97% rename from core/src/processing/core/PSurfaceNone.java rename to libs/processing-core/src/main/java/processing/core/PSurfaceNone.java index cd350b873..5858ed1f7 100644 --- a/core/src/processing/core/PSurfaceNone.java +++ b/libs/processing-core/src/main/java/processing/core/PSurfaceNone.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -32,10 +32,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.service.wallpaper.WallpaperService; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.support.wearable.watchface.WatchFaceService; import android.view.LayoutInflater; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -44,7 +40,13 @@ import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; import android.widget.RelativeLayout; -import android.support.v4.os.ResultReceiver; +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; @@ -75,7 +77,7 @@ public class PSurfaceNone implements PSurface, PConstants { protected boolean requestedThreadStart = false; protected Thread thread; protected boolean paused; - protected Object pauseObject = new Object(); + protected final Object pauseObject = new Object(); protected float frameRateTarget = 60; protected long frameRatePeriod = 1000000000L / 60L; @@ -330,8 +332,7 @@ public InputStream openFileInput(String filename) { try { return activity.openFileInput(filename); } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + System.err.println("Cannot open file " + filename); } } return null; @@ -529,7 +530,9 @@ public void run() { // not good to make this synchronized, locks things up try { Thread.sleep(sleepTime / 1000000L, (int) (sleepTime % 1000000L)); noDelays = 0; // Got some sleep, not delaying anymore - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + System.err.println("Cannot properly set the timing for the draw animation."); + } overSleepTime = (System.nanoTime() - afterTime) - sleepTime; diff --git a/core/src/processing/core/PVector.java b/libs/processing-core/src/main/java/processing/core/PVector.java similarity index 99% rename from core/src/processing/core/PVector.java rename to libs/processing-core/src/main/java/processing/core/PVector.java index 7e652127c..d1c074f92 100644 --- a/core/src/processing/core/PVector.java +++ b/libs/processing-core/src/main/java/processing/core/PVector.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2008-12 Ben Fry and Casey Reas Copyright (c) 2008 Dan Shiffman 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 95% rename from core/src/processing/data/FloatDict.java rename to libs/processing-core/src/main/java/processing/data/FloatDict.java index f7c7406aa..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; @@ -447,7 +448,7 @@ public void div(String key, float amount) { private void checkMinMax(String functionName) { if (count == 0) { String msg = - String.format("Cannot use %s() on an empty %s.", + String.format("Cannot use %s() on an empty %s.", functionName, getClass().getSimpleName()); throw new RuntimeException(msg); } @@ -607,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]; @@ -631,7 +633,7 @@ public String removeIndex(int index) { count--; keys[count] = null; values[count] = 0; - return key; + return value; } @@ -734,7 +736,7 @@ public int size() { } @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]); @@ -747,7 +749,13 @@ public float compare(int a, int b) { 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 @@ -798,6 +806,16 @@ public void print() { } + /** + * 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 @@ -826,4 +844,4 @@ public String toJSON() { public String toString() { return getClass().getSimpleName() + " size=" + size() + " " + toJSON(); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/FloatList.java b/libs/processing-core/src/main/java/processing/data/FloatList.java similarity index 95% rename from core/src/processing/data/FloatList.java rename to libs/processing-core/src/main/java/processing/data/FloatList.java index 35aec44ad..863b05658 100644 --- a/core/src/processing/data/FloatList.java +++ b/libs/processing-core/src/main/java/processing/data/FloatList.java @@ -1,27 +1,7 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2013-16 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. - - 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.data; +import java.io.File; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Iterator; import java.util.Random; @@ -649,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; } @@ -702,8 +694,9 @@ public int size() { } @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 @@ -907,6 +900,27 @@ public void print() { } + /** + * 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. */ diff --git a/core/src/processing/data/IntDict.java b/libs/processing-core/src/main/java/processing/data/IntDict.java similarity index 96% rename from core/src/processing/data/IntDict.java rename to libs/processing-core/src/main/java/processing/data/IntDict.java index 41b8502c9..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; @@ -471,7 +472,7 @@ public void div(String key, int amount) { private void checkMinMax(String functionName) { if (count == 0) { String msg = - String.format("Cannot use %s() on an empty %s.", + String.format("Cannot use %s() on an empty %s.", functionName, getClass().getSimpleName()); throw new RuntimeException(msg); } @@ -593,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]; @@ -615,7 +617,7 @@ public String removeIndex(int index) { count--; keys[count] = null; values[count] = 0; - return key; + return value; } @@ -701,7 +703,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]); @@ -766,8 +768,17 @@ public void print() { /** - * 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++) { @@ -793,4 +804,4 @@ public String toJSON() { public String toString() { return getClass().getSimpleName() + " size=" + size() + " " + toJSON(); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/IntList.java b/libs/processing-core/src/main/java/processing/data/IntList.java similarity index 94% rename from core/src/processing/data/IntList.java rename to libs/processing-core/src/main/java/processing/data/IntList.java index 0d5f8956c..dc2c89916 100644 --- a/core/src/processing/data/IntList.java +++ b/libs/processing-core/src/main/java/processing/data/IntList.java @@ -1,27 +1,7 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2013-16 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. - - 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.data; +import java.io.File; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Iterator; import java.util.Random; @@ -618,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; } @@ -651,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]; } @@ -862,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); } @@ -895,6 +900,27 @@ public void print() { } + /** + * 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. */ diff --git a/core/src/processing/data/JSONArray.java b/libs/processing-core/src/main/java/processing/data/JSONArray.java similarity index 97% rename from core/src/processing/data/JSONArray.java rename to libs/processing-core/src/main/java/processing/data/JSONArray.java index b43c6fe0a..ea8276bd8 100644 --- a/core/src/processing/data/JSONArray.java +++ b/libs/processing-core/src/main/java/processing/data/JSONArray.java @@ -144,17 +144,17 @@ protected JSONArray(JSONTokener x) { myArrayList.add(x.nextValue()); } switch (x.nextClean()) { - case ';': - case ',': - if (x.nextClean() == ']') { - return; - } - x.back(); - break; - case ']': + case ';': + case ',': + if (x.nextClean() == ']') { return; - default: - throw new RuntimeException("Expected a ',' or ']'"); + } + x.back(); + break; + case ']': + return; + default: + throw new RuntimeException("Expected a ',' or ']'"); } } } @@ -323,8 +323,8 @@ public int getInt(int index) { Object object = this.get(index); try { return object instanceof Number - ? ((Number)object).intValue() - : Integer.parseInt((String)object); + ? ((Number)object).intValue() + : Integer.parseInt((String)object); } catch (Exception e) { throw new RuntimeException("JSONArray[" + index + "] is not a number."); } @@ -360,8 +360,8 @@ public long getLong(int index) { Object object = this.get(index); try { return object instanceof Number - ? ((Number)object).longValue() - : Long.parseLong((String)object); + ? ((Number)object).longValue() + : Long.parseLong((String)object); } catch (Exception e) { throw new RuntimeException("JSONArray[" + index + "] is not a number."); } @@ -422,8 +422,8 @@ public double getDouble(int index) { Object object = this.get(index); try { return object instanceof Number - ? ((Number)object).doubleValue() - : Double.parseDouble((String)object); + ? ((Number)object).doubleValue() + : Double.parseDouble((String)object); } catch (Exception e) { throw new RuntimeException("JSONArray[" + index + "] is not a number."); } @@ -465,12 +465,12 @@ public double getDouble(int index, double defaultValue) { public boolean getBoolean(int index) { Object object = this.get(index); if (object.equals(Boolean.FALSE) || - (object instanceof String && - ((String)object).equalsIgnoreCase("false"))) { + (object instanceof String && + ((String)object).equalsIgnoreCase("false"))) { return false; } else if (object.equals(Boolean.TRUE) || - (object instanceof String && - ((String)object).equalsIgnoreCase("true"))) { + (object instanceof String && + ((String)object).equalsIgnoreCase("true"))) { return true; } throw new RuntimeException("JSONArray[" + index + "] is not a boolean."); @@ -1205,7 +1205,7 @@ protected Writer writeInternal(Writer writer, int indentFactor, int indent) { if (length == 1) { JSONObject.writeValue(writer, this.myArrayList.get(0), - indentFactor, indent); + indentFactor, indent); // thisFactor, indent); } else if (length != 0) { final int newIndent = indent + thisFactor; @@ -1221,7 +1221,7 @@ protected Writer writeInternal(Writer writer, int indentFactor, int indent) { // JSONObject.writeValue(writer, this.myArrayList.get(i), // thisFactor, newIndent); JSONObject.writeValue(writer, this.myArrayList.get(i), - indentFactor, newIndent); + indentFactor, newIndent); commanate = true; } if (indentFactor != -1) { @@ -1257,4 +1257,4 @@ public String join(String separator) { } return sb.toString(); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/JSONObject.java b/libs/processing-core/src/main/java/processing/data/JSONObject.java similarity index 93% rename from core/src/processing/data/JSONObject.java rename to libs/processing-core/src/main/java/processing/data/JSONObject.java index 627737ef4..cc7a22de0 100644 --- a/core/src/processing/data/JSONObject.java +++ b/libs/processing-core/src/main/java/processing/data/JSONObject.java @@ -123,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); // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -244,13 +244,13 @@ protected JSONObject(JSONTokener x) { for (;;) { c = x.nextClean(); switch (c) { - case 0: - throw new RuntimeException("A JSONObject text must end with '}'"); - case '}': - return; - default: - x.back(); - key = x.nextValue().toString(); + case 0: + throw new RuntimeException("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); } // The key is followed by ':'. We will also tolerate '=' or '=>'. @@ -268,17 +268,17 @@ protected JSONObject(JSONTokener x) { // Pairs are separated by ','. We will also tolerate ';'. switch (x.nextClean()) { - case ';': - case ',': - if (x.nextClean() == '}') { - return; - } - x.back(); - break; - case '}': + case ';': + case ',': + if (x.nextClean() == '}') { return; - default: - throw new RuntimeException("Expected a ',' or '}'"); + } + x.back(); + break; + case '}': + return; + default: + throw new RuntimeException("Expected a ',' or '}'"); } } } @@ -518,7 +518,7 @@ static protected String doubleToString(double d) { String string = Double.toString(d); if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && - string.indexOf('E') < 0) { + string.indexOf('E') < 0) { while (string.endsWith("0")) { string = string.substring(0, string.length() - 1); } @@ -612,7 +612,7 @@ public int getInt(String key) { } try { return object instanceof Number ? - ((Number)object).intValue() : Integer.parseInt((String)object); + ((Number)object).intValue() : Integer.parseInt((String)object); } catch (Exception e) { throw new RuntimeException("JSONObject[" + quote(key) + "] is not an int."); } @@ -650,8 +650,8 @@ public long getLong(String key) { Object object = this.get(key); try { return object instanceof Number - ? ((Number)object).longValue() - : Long.parseLong((String)object); + ? ((Number)object).longValue() + : Long.parseLong((String)object); } catch (Exception e) { throw new RuntimeException("JSONObject[" + quote(key) + "] is not a long.", e); } @@ -710,8 +710,8 @@ public double getDouble(String key) { Object object = this.get(key); try { return object instanceof Number - ? ((Number)object).doubleValue() - : Double.parseDouble((String)object); + ? ((Number)object).doubleValue() + : Double.parseDouble((String)object); } catch (Exception e) { throw new RuntimeException("JSONObject[" + quote(key) + "] is not a number."); } @@ -752,12 +752,12 @@ public double getDouble(String key, double defaultValue) { public boolean getBoolean(String key) { Object object = this.get(key); if (object.equals(Boolean.FALSE) || - (object instanceof String && - ((String)object).equalsIgnoreCase("false"))) { + (object instanceof String && + ((String)object).equalsIgnoreCase("false"))) { return false; } else if (object.equals(Boolean.TRUE) || - (object instanceof String && - ((String)object).equalsIgnoreCase("true"))) { + (object instanceof String && + ((String)object).equalsIgnoreCase("true"))) { return true; } throw new RuntimeException("JSONObject[" + quote(key) + "] is not a Boolean."); @@ -989,7 +989,7 @@ private static String numberToString(Number number) { String string = number.toString(); if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && - string.indexOf('E') < 0) { + string.indexOf('E') < 0) { while (string.endsWith("0")) { string = string.substring(0, string.length() - 1); } @@ -1115,43 +1115,43 @@ private void populateMap(Object bean) { boolean includeSuperClass = klass.getClassLoader() != null; Method[] methods = includeSuperClass - ? klass.getMethods() - : klass.getDeclaredMethods(); - for (int i = 0; i < methods.length; i += 1) { - try { - Method method = methods[i]; - if (Modifier.isPublic(method.getModifiers())) { - String name = method.getName(); - String key = ""; - if (name.startsWith("get")) { - if ("getClass".equals(name) || - "getDeclaringClass".equals(name)) { - key = ""; - } else { - key = name.substring(3); + ? klass.getMethods() + : klass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i += 1) { + try { + Method method = methods[i]; + if (Modifier.isPublic(method.getModifiers())) { + String name = method.getName(); + String key = ""; + if (name.startsWith("get")) { + if ("getClass".equals(name) || + "getDeclaringClass".equals(name)) { + key = ""; + } else { + key = name.substring(3); + } + } else if (name.startsWith("is")) { + key = name.substring(2); } - } else if (name.startsWith("is")) { - key = name.substring(2); - } - if (key.length() > 0 && - Character.isUpperCase(key.charAt(0)) && - method.getParameterTypes().length == 0) { - if (key.length() == 1) { - key = key.toLowerCase(); - } else if (!Character.isUpperCase(key.charAt(1))) { - key = key.substring(0, 1).toLowerCase() + - key.substring(1); - } - - Object result = method.invoke(bean, (Object[])null); - if (result != null) { - this.map.put(key, wrap(result)); + if (key.length() > 0 && + Character.isUpperCase(key.charAt(0)) && + method.getParameterTypes().length == 0) { + if (key.length() == 1) { + key = key.toLowerCase(); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase() + + key.substring(1); + } + + Object result = method.invoke(bean, (Object[])null); + if (result != null) { + this.map.put(key, wrap(result)); + } } } + } catch (Exception ignore) { } - } catch (Exception ignore) { } - } } @@ -1387,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 { @@ -1399,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; @@ -1416,42 +1416,42 @@ static protected Writer quote(String string, Writer w) throws IOException { b = c; c = string.charAt(i); switch (c) { - case '\\': - case '"': + case '\\': + case '"': + w.write('\\'); + w.write(c); + break; + case '/': + if (b == '<') { w.write('\\'); + } + w.write(c); + break; + case '\b': + w.write("\\b"); + break; + case '\t': + w.write("\\t"); + break; + case '\n': + w.write("\\n"); + break; + case '\f': + w.write("\\f"); + break; + case '\r': + w.write("\\r"); + break; + default: + if (c < ' ' || (c >= '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + w.write("\\u"); + hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { w.write(c); - break; - case '/': - if (b == '<') { - w.write('\\'); - } - w.write(c); - break; - case '\b': - w.write("\\b"); - break; - case '\t': - w.write("\\t"); - break; - case '\n': - w.write("\\n"); - break; - case '\f': - w.write("\\f"); - break; - case '\r': - w.write("\\r"); - break; - default: - if (c < ' ' || (c >= '\u0080' && c < '\u00a0') - || (c >= '\u2000' && c < '\u2100')) { - w.write("\\u"); - hhhh = Integer.toHexString(c); - w.write("0000", 0, 4 - hhhh.length()); - w.write(hhhh); - } else { - w.write(c); - } + } } } w.write('"'); @@ -1503,7 +1503,7 @@ static protected Object stringToValue(String string) { if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') { try { if (string.indexOf('.') > -1 || - string.indexOf('e') > -1 || string.indexOf('E') > -1) { + string.indexOf('e') > -1 || string.indexOf('E') > -1) { d = Double.valueOf(string); if (!d.isInfinite() && !d.isNaN()) { return d; @@ -1534,12 +1534,12 @@ static protected void testValidity(Object o) { if (o instanceof Double) { if (((Double)o).isInfinite() || ((Double)o).isNaN()) { throw new RuntimeException( - "JSON does not allow non-finite numbers."); + "JSON does not allow non-finite numbers."); } } else if (o instanceof Float) { if (((Float)o).isInfinite() || ((Float)o).isNaN()) { throw new RuntimeException( - "JSON does not allow non-finite numbers."); + "JSON does not allow non-finite numbers."); } } } @@ -1682,7 +1682,7 @@ static protected String valueToString(Object value) { return numberToString((Number) value); } if (value instanceof Boolean || value instanceof JSONObject || - value instanceof JSONArray) { + value instanceof JSONArray) { return value.toString(); } if (value instanceof Map) { @@ -1715,12 +1715,12 @@ static protected Object wrap(Object object) { return NULL; } if (object instanceof JSONObject || object instanceof JSONArray || - NULL.equals(object) || /*object instanceof JSONString ||*/ - object instanceof Byte || object instanceof Character || - object instanceof Short || object instanceof Integer || - object instanceof Long || object instanceof Boolean || - object instanceof Float || object instanceof Double || - object instanceof String) { + NULL.equals(object) || /*object instanceof JSONString ||*/ + object instanceof Byte || object instanceof Character || + object instanceof Short || object instanceof Integer || + object instanceof Long || object instanceof Boolean || + object instanceof Float || object instanceof Double || + object instanceof String) { return object; } @@ -1735,16 +1735,16 @@ static protected Object wrap(Object object) { } Package objectPackage = object.getClass().getPackage(); String objectPackageName = objectPackage != null - ? objectPackage.getName() - : ""; - if ( - objectPackageName.startsWith("java.") || - objectPackageName.startsWith("javax.") || - object.getClass().getClassLoader() == null - ) { - return object.toString(); - } - return new JSONObject(object); + ? objectPackage.getName() + : ""; + if ( + objectPackageName.startsWith("java.") || + objectPackageName.startsWith("javax.") || + object.getClass().getClassLoader() == null + ) { + return object.toString(); + } + return new JSONObject(object); } catch(Exception exception) { return null; } @@ -1777,7 +1777,7 @@ static final Writer writeValue(Writer writer, Object value, new JSONObject(value).writeInternal(writer, indentFactor, indent); } else if (value instanceof Collection) { new JSONArray(value).writeInternal(writer, indentFactor, - indent); + indent); } else if (value.getClass().isArray()) { new JSONArray(value).writeInternal(writer, indentFactor, indent); } else if (value instanceof Number) { @@ -2279,4 +2279,4 @@ protected Writer writeInternal(Writer writer, int indentFactor, int indent) { // this.line + "]"; // } // } -} \ No newline at end of file +} diff --git a/core/src/processing/data/JSONTokener.java b/libs/processing-core/src/main/java/processing/data/JSONTokener.java similarity index 100% rename from core/src/processing/data/JSONTokener.java rename to libs/processing-core/src/main/java/processing/data/JSONTokener.java 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/libs/processing-core/src/main/java/processing/data/Sort.java b/libs/processing-core/src/main/java/processing/data/Sort.java new file mode 100644 index 000000000..a83fea551 --- /dev/null +++ b/libs/processing-core/src/main/java/processing/data/Sort.java @@ -0,0 +1,46 @@ +package processing.data; + + +/** + * Internal sorter used by several data classes. + * Advanced users only, not official API. + */ +public abstract class Sort implements Runnable { + + public Sort() { } + + + public void run() { + int c = size(); + if (c > 1) { + sort(0, c - 1); + } + } + + + protected void sort(int i, int j) { + int pivotIndex = (i+j)/2; + swap(pivotIndex, j); + int k = partition(i-1, j); + swap(k, j); + if ((k-i) > 1) sort(i, k-1); + if ((j-k) > 1) sort(k+1, j); + } + + + protected int partition(int left, int right) { + int pivot = right; + do { + while (compare(++left, pivot) < 0) { } + while ((right != 0) && (compare(--right, pivot) > 0)) { } + swap(left, right); + } while (left < right); + swap(left, right); + return left; + } + + + abstract public int size(); + 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 95% rename from core/src/processing/data/StringDict.java rename to libs/processing-core/src/main/java/processing/data/StringDict.java index 9718f9bd0..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; @@ -433,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; } @@ -446,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]; @@ -457,10 +459,11 @@ public String removeIndex(int index) { count--; keys[count] = null; values[count] = null; - return key; + return value; } + public void swap(int a, int b) { String tkey = keys[a]; String tvalue = values[a]; @@ -522,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]); @@ -571,8 +574,17 @@ public void print() { /** - * 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++) { @@ -598,4 +610,4 @@ public String toJSON() { public String toString() { return getClass().getSimpleName() + " size=" + size() + " " + toJSON(); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/StringList.java b/libs/processing-core/src/main/java/processing/data/StringList.java similarity index 94% rename from core/src/processing/data/StringList.java rename to libs/processing-core/src/main/java/processing/data/StringList.java index 49925c1e9..c4de6d33c 100644 --- a/core/src/processing/data/StringList.java +++ b/libs/processing-core/src/main/java/processing/data/StringList.java @@ -1,27 +1,7 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2013-16 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. - - 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.data; +import java.io.File; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Iterator; import java.util.Random; @@ -536,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; } @@ -778,6 +758,27 @@ public void print() { } + /** + * 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. */ diff --git a/core/src/processing/data/Table.java b/libs/processing-core/src/main/java/processing/data/Table.java similarity index 93% rename from core/src/processing/data/Table.java rename to libs/processing-core/src/main/java/processing/data/Table.java index 5845c57f8..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 @@ -124,7 +125,7 @@ public Table(File file, String options) throws IOException { // uses createInput() to handle .gz (and eventually .bz2) files init(); parse(PApplet.createInput(file), - extensionOptions(true, file.getName(), options)); + extensionOptions(true, file.getName(), options)); } /** @@ -210,22 +211,22 @@ public Table(ResultSet rs) { int type = rsmd.getColumnType(col + 1); switch (type) { // TODO these aren't tested. nor are they complete. - case Types.INTEGER: - case Types.TINYINT: - case Types.SMALLINT: - setColumnType(col, INT); - break; - case Types.BIGINT: - setColumnType(col, LONG); - break; - case Types.FLOAT: - setColumnType(col, FLOAT); - break; - case Types.DECIMAL: - case Types.DOUBLE: - case Types.REAL: - setColumnType(col, DOUBLE); - break; + case Types.INTEGER: + case Types.TINYINT: + case Types.SMALLINT: + setColumnType(col, INT); + break; + case Types.BIGINT: + setColumnType(col, LONG); + break; + case Types.FLOAT: + setColumnType(col, FLOAT); + break; + case Types.DECIMAL: + case Types.DOUBLE: + case Types.REAL: + setColumnType(col, DOUBLE); + break; } } @@ -233,12 +234,12 @@ public Table(ResultSet rs) { while (rs.next()) { for (int col = 0; col < columnCount; col++) { switch (columnTypes[col]) { - case STRING: setString(row, col, rs.getString(col+1)); break; - case INT: setInt(row, col, rs.getInt(col+1)); break; - case LONG: setLong(row, col, rs.getLong(col+1)); break; - case FLOAT: setFloat(row, col, rs.getFloat(col+1)); break; - case DOUBLE: setDouble(row, col, rs.getDouble(col+1)); break; - default: throw new IllegalArgumentException("column type " + columnTypes[col] + " not supported."); + case STRING: setString(row, col, rs.getString(col+1)); break; + case INT: setInt(row, col, rs.getInt(col+1)); break; + case LONG: setLong(row, col, rs.getLong(col+1)); break; + case FLOAT: setFloat(row, col, rs.getFloat(col+1)); break; + case DOUBLE: setDouble(row, col, rs.getDouble(col+1)); break; + default: throw new IllegalArgumentException("column type " + columnTypes[col] + " not supported."); } } row++; @@ -562,7 +563,7 @@ static class CommaSeparatedLine { String[] pieces; int pieceCount; - // int offset; +// int offset; int start; //, stop; String[] handle(String line, BufferedReader reader) throws IOException { @@ -851,7 +852,7 @@ protected void odsParse(InputStream input, String worksheet, boolean header) { // // XML[] sheets = - xml.getChildren("office:body/office:spreadsheet/table:table"); + xml.getChildren("office:body/office:spreadsheet/table:table"); boolean found = false; for (XML sheet : sheets) { @@ -869,7 +870,7 @@ protected void odsParse(InputStream input, String worksheet, boolean header) { throw new RuntimeException("No worksheets found in the ODS file."); } else { throw new RuntimeException("No worksheet named " + worksheet + - " found in the ODS file."); + " found in the ODS file."); } } } catch (UnsupportedEncodingException e) { @@ -1127,7 +1128,7 @@ public void parseInto(Object enclosingObject, String fieldName) { // Only bother setting if it's true, // otherwise false by default anyway. if (content.toLowerCase().equals("true") || - content.equals("1")) { + content.equals("1")) { field.setBoolean(item, true); } } @@ -1173,7 +1174,7 @@ public void parseInto(Object enclosingObject, String fieldName) { public boolean save(File file, String options) throws IOException { return save(PApplet.createOutput(file), - Table.extensionOptions(false, file.getName(), options)); + Table.extensionOptions(false, file.getName(), options)); } @@ -1309,8 +1310,8 @@ protected void writeEntryCSV(PrintWriter writer, String entry) { // add quotes if commas or CR/LF are in the entry } else if (entry.indexOf(',') != -1 || - entry.indexOf('\n') != -1 || - entry.indexOf('\r') != -1) { + entry.indexOf('\n') != -1 || + entry.indexOf('\r') != -1) { writer.print('\"'); writer.print(entry); writer.print('\"'); @@ -1318,8 +1319,8 @@ protected void writeEntryCSV(PrintWriter writer, String entry) { // add quotes if leading or trailing space } else if ((entry.length() > 0) && - (entry.charAt(0) == ' ' || - entry.charAt(entry.length() - 1) == ' ')) { + (entry.charAt(0) == ' ' || + entry.charAt(entry.length() - 1) == ' ')) { writer.print('\"'); writer.print(entry); writer.print('\"'); @@ -1414,14 +1415,14 @@ protected void saveODS(OutputStream os) throws IOException { ZipEntry entry = new ZipEntry("META-INF/manifest.xml"); String[] lines = new String[] { - xmlHeader, - "", - " ", - " ", - " ", - " ", - " ", - "" + xmlHeader, + "", + " ", + " ", + " ", + " ", + " ", + "" }; zos.putNextEntry(entry); zos.write(PApplet.join(lines, "\n").getBytes()); @@ -1476,12 +1477,12 @@ protected void saveODS(OutputStream os) throws IOException { */ final String[] dummyFiles = new String[] { - "meta.xml", "settings.xml", "styles.xml" + "meta.xml", "settings.xml", "styles.xml" }; lines = new String[] { - xmlHeader, - "" + xmlHeader, + "" }; byte[] dummyBytes = PApplet.join(lines, "\n").getBytes(); for (String filename : dummyFiles) { @@ -1504,15 +1505,15 @@ protected void saveODS(OutputStream os) throws IOException { zos.putNextEntry(entry); //lines = new String[] { writeUTF(zos, new String[] { - xmlHeader, - "", - " ", - " ", - " " + xmlHeader, + "", + " ", + " ", + " " }); //zos.write(PApplet.join(lines, "\n").getBytes()); @@ -1541,10 +1542,10 @@ protected void saveODS(OutputStream os) throws IOException { //lines = new String[] { writeUTF(zos, new String[] { - " ", - " ", - " ", - "" + " ", + " ", + " ", + "" }); //zos.write(PApplet.join(lines, "\n").getBytes()); zos.closeEntry(); @@ -1581,17 +1582,17 @@ void saveStringODS(OutputStream output, String text) throws IOException { } writeUTF(output, - " ", - " " + sanitized + "", - " "); + " ", + " " + sanitized + "", + " "); } void saveNumberODS(OutputStream output, String text) throws IOException { writeUTF(output, - " ", - " " + text + "", - " "); + " ", + " " + text + "", + " "); } @@ -1646,35 +1647,35 @@ protected void saveBinary(OutputStream os) throws IOException { for (TableRow row : rows()) { for (int col = 0; col < getColumnCount(); col++) { switch (columnTypes[col]) { - case STRING: - String str = row.getString(col); - if (str == null) { - output.writeBoolean(false); - } else { - output.writeBoolean(true); - output.writeUTF(str); - } - break; - case INT: - output.writeInt(row.getInt(col)); - break; - case LONG: - output.writeLong(row.getLong(col)); - break; - case FLOAT: - output.writeFloat(row.getFloat(col)); - break; - case DOUBLE: - output.writeDouble(row.getDouble(col)); - break; - case CATEGORY: - String peace = row.getString(col); - if (peace.equals(missingString)) { - output.writeInt(missingCategory); - } else { - output.writeInt(columnCategories[col].index(peace)); - } - break; + case STRING: + String str = row.getString(col); + if (str == null) { + output.writeBoolean(false); + } else { + output.writeBoolean(true); + output.writeUTF(str); + } + break; + case INT: + output.writeInt(row.getInt(col)); + break; + case LONG: + output.writeLong(row.getLong(col)); + break; + case FLOAT: + output.writeFloat(row.getFloat(col)); + break; + case DOUBLE: + output.writeDouble(row.getDouble(col)); + break; + case CATEGORY: + String peace = row.getString(col); + if (peace.equals(missingString)) { + output.writeInt(missingCategory); + } else { + output.writeInt(columnCategories[col].index(peace)); + } + break; } } } @@ -1708,26 +1709,26 @@ protected void loadBinary(InputStream is) throws IOException { int newType = input.readInt(); columnTypes[column] = newType; switch (newType) { - case INT: - columns[column] = new int[rowCount]; - break; - case LONG: - columns[column] = new long[rowCount];; - break; - case FLOAT: - columns[column] = new float[rowCount];; - break; - case DOUBLE: - columns[column] = new double[rowCount];; - break; - case STRING: - columns[column] = new String[rowCount];; - break; - case CATEGORY: - columns[column] = new int[rowCount];; - break; - default: - throw new IllegalArgumentException(newType + " is not a valid column type."); + case INT: + columns[column] = new int[rowCount]; + break; + case LONG: + columns[column] = new long[rowCount];; + break; + case FLOAT: + columns[column] = new float[rowCount];; + break; + case DOUBLE: + columns[column] = new double[rowCount];; + break; + case STRING: + columns[column] = new String[rowCount];; + break; + case CATEGORY: + columns[column] = new int[rowCount];; + break; + default: + throw new IllegalArgumentException(newType + " is not a valid column type."); } } @@ -1751,30 +1752,30 @@ protected void loadBinary(InputStream is) throws IOException { for (int row = 0; row < rowCount; row++) { for (int col = 0; col < columnCount; col++) { switch (columnTypes[col]) { - case STRING: - String str = null; - if (input.readBoolean()) { - str = input.readUTF(); - } - setString(row, col, str); - break; - case INT: - setInt(row, col, input.readInt()); - break; - case LONG: - setLong(row, col, input.readLong()); - break; - case FLOAT: - setFloat(row, col, input.readFloat()); - break; - case DOUBLE: - setDouble(row, col, input.readDouble()); - break; - case CATEGORY: - int index = input.readInt(); - //String name = columnCategories[col].key(index); - setInt(row, col, index); - break; + case STRING: + String str = null; + if (input.readBoolean()) { + str = input.readUTF(); + } + setString(row, col, str); + break; + case INT: + setInt(row, col, input.readInt()); + break; + case LONG: + setLong(row, col, input.readLong()); + break; + case FLOAT: + setFloat(row, col, input.readFloat()); + break; + case DOUBLE: + setDouble(row, col, input.readDouble()); + break; + case CATEGORY: + int index = input.readInt(); + //String name = columnCategories[col].key(index); + setInt(row, col, index); + break; } } } @@ -1860,7 +1861,7 @@ public void insertColumn(int index, String title, int type) { } } - /** + /** * @webref table:method * @brief Removes a column from a table * @param columnName the title of the column to be removed @@ -1870,7 +1871,7 @@ public void removeColumn(String columnName) { removeColumn(getColumnIndex(columnName)); } - /** + /** * @param column the index number of the column to be removed */ public void removeColumn(int column) { @@ -1935,7 +1936,7 @@ public void setColumnCount(int newCount) { } columnTypes = PApplet.expand(columnTypes, newCount); columnCategories = (HashMapBlows[]) - PApplet.expand(columnCategories, newCount); + PApplet.expand(columnCategories, newCount); } } @@ -2311,7 +2312,7 @@ public void setRowCount(int newCount) { } - /** + /** * @webref table:method * @brief Adds a row to a table * @see Table#removeRow(int) @@ -2324,7 +2325,7 @@ public TableRow addRow() { } - /** + /** * @param source a reference to the original row to be duplicated */ public TableRow addRow(TableRow source) { @@ -2338,38 +2339,38 @@ public TableRow setRow(int row, TableRow source) { for (int col = 0; col < Math.min(source.getColumnCount(), columns.length); col++) { switch (columnTypes[col]) { - case INT: - setInt(row, col, source.getInt(col)); - break; - case LONG: - setLong(row, col, source.getLong(col)); - break; - case FLOAT: - setFloat(row, col, source.getFloat(col)); - break; - case DOUBLE: - setDouble(row, col, source.getDouble(col)); - break; - 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; + case INT: + setInt(row, col, source.getInt(col)); + break; + case LONG: + setLong(row, col, source.getLong(col)); + break; + case FLOAT: + setFloat(row, col, source.getFloat(col)); + break; + case DOUBLE: + setDouble(row, col, source.getDouble(col)); + break; + 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"); + default: + throw new RuntimeException("no types"); } } return new RowPointer(this, row); } - /** + /** * @nowebref */ public TableRow addRow(Object[] columnData) { @@ -2428,10 +2429,13 @@ public void insertRow(int insert, Object[] columnData) { } } } + // 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 @@ -3060,13 +3064,13 @@ public void remove() { public int getInt(int row, int column) { checkBounds(row, column); if (columnTypes[column] == INT || - columnTypes[column] == CATEGORY) { + columnTypes[column] == CATEGORY) { int[] intData = (int[]) columns[column]; return intData[row]; } String str = getString(row, column); return (str == null || str.equals(missingString)) ? - missingInt : PApplet.parseInt(str, missingInt); + missingInt : PApplet.parseInt(str, missingInt); } /** @@ -3102,7 +3106,7 @@ public void setInt(int row, int column, int value) { } else { ensureBounds(row, column); if (columnTypes[column] != INT && - columnTypes[column] != CATEGORY) { + columnTypes[column] != CATEGORY) { throw new IllegalArgumentException("Column " + column + " is not an int column."); } int[] intData = (int[]) columns[column]; @@ -3767,7 +3771,7 @@ public int matchRowIndex(String regexp, int column) { String[] stringData = (String[]) columns[column]; for (int row = 0; row < rowCount; row++) { if (stringData[row] != null && - PApplet.match(stringData[row], regexp) != null) { + PApplet.match(stringData[row], regexp) != null) { return row; } } @@ -3775,7 +3779,7 @@ public int matchRowIndex(String regexp, int column) { for (int row = 0; row < rowCount; row++) { String str = getString(row, column); if (str != null && - PApplet.match(str, regexp) != null) { + PApplet.match(str, regexp) != null) { return row; } } @@ -3809,7 +3813,7 @@ public int[] matchRowIndices(String regexp, int column) { String[] stringData = (String[]) columns[column]; for (int row = 0; row < rowCount; row++) { if (stringData[row] != null && - PApplet.match(stringData[row], regexp) != null) { + PApplet.match(stringData[row], regexp) != null) { outgoing[count++] = row; } } @@ -3817,7 +3821,7 @@ public int[] matchRowIndices(String regexp, int column) { for (int row = 0; row < rowCount; row++) { String str = getString(row, column); if (str != null && - PApplet.match(str, regexp) != null) { + PApplet.match(str, regexp) != null) { outgoing[count++] = row; } } @@ -4330,25 +4334,36 @@ 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]; switch (getColumnType(column)) { - case INT: - return getInt(a, column) - getInt(b, column); - case LONG: - return getLong(a, column) - getLong(b, column); - case FLOAT: - return getFloat(a, column) - getFloat(b, column); - case DOUBLE: - return (float) (getDouble(a, column) - getDouble(b, column)); - case STRING: - return getString(a, column).compareToIgnoreCase(getString(b, column)); - case CATEGORY: - return getInt(a, column) - getInt(b, column); - default: - throw new IllegalArgumentException("Invalid column type: " + getColumnType(column)); + case INT: + return getInt(a, column) - getInt(b, column); + case LONG: + long diffl = getLong(a, column) - getLong(b, column); + return diffl == 0 ? 0 : (diffl < 0 ? -1 : 1); + case FLOAT: + float difff = getFloat(a, column) - getFloat(b, column); + return difff == 0 ? 0 : (difff < 0 ? -1 : 1); + case DOUBLE: + double diffd = getDouble(a, column) - getDouble(b, column); + return diffd == 0 ? 0 : (diffd < 0 ? -1 : 1); + case STRING: + 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: + throw new IllegalArgumentException("Invalid column type: " + getColumnType(column)); } } @@ -4365,47 +4380,47 @@ public void swap(int a, int b) { //Object[] newColumns = new Object[getColumnCount()]; for (int col = 0; col < getColumnCount(); col++) { switch (getColumnType(col)) { - case INT: - case CATEGORY: - int[] oldInt = (int[]) columns[col]; - int[] newInt = new int[rowCount]; - for (int row = 0; row < getRowCount(); row++) { - newInt[row] = oldInt[order[row]]; - } - columns[col] = newInt; - break; - case LONG: - long[] oldLong = (long[]) columns[col]; - long[] newLong = new long[rowCount]; - for (int row = 0; row < getRowCount(); row++) { - newLong[row] = oldLong[order[row]]; - } - columns[col] = newLong; - break; - case FLOAT: - float[] oldFloat = (float[]) columns[col]; - float[] newFloat = new float[rowCount]; - for (int row = 0; row < getRowCount(); row++) { - newFloat[row] = oldFloat[order[row]]; - } - columns[col] = newFloat; - break; - case DOUBLE: - double[] oldDouble = (double[]) columns[col]; - double[] newDouble = new double[rowCount]; - for (int row = 0; row < getRowCount(); row++) { - newDouble[row] = oldDouble[order[row]]; - } - columns[col] = newDouble; - break; - case STRING: - String[] oldString = (String[]) columns[col]; - String[] newString = new String[rowCount]; - for (int row = 0; row < getRowCount(); row++) { - newString[row] = oldString[order[row]]; - } - columns[col] = newString; - break; + case INT: + case CATEGORY: + int[] oldInt = (int[]) columns[col]; + int[] newInt = new int[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newInt[row] = oldInt[order[row]]; + } + columns[col] = newInt; + break; + case LONG: + long[] oldLong = (long[]) columns[col]; + long[] newLong = new long[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newLong[row] = oldLong[order[row]]; + } + columns[col] = newLong; + break; + case FLOAT: + float[] oldFloat = (float[]) columns[col]; + float[] newFloat = new float[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newFloat[row] = oldFloat[order[row]]; + } + columns[col] = newFloat; + break; + case DOUBLE: + double[] oldDouble = (double[]) columns[col]; + double[] newDouble = new double[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newDouble[row] = oldDouble[order[row]]; + } + columns[col] = newDouble; + break; + case STRING: + String[] oldString = (String[]) columns[col]; + String[] newString = new String[rowCount]; + for (int row = 0; row < getRowCount(); row++) { + newString[row] = oldString[order[row]]; + } + columns[col] = newString; + break; } } } @@ -4485,37 +4500,37 @@ public StringList getStringList(int column) { public IntDict getIntDict(String keyColumnName, String valueColumnName) { return new IntDict(getStringColumn(keyColumnName), - getIntColumn(valueColumnName)); + getIntColumn(valueColumnName)); } public IntDict getIntDict(int keyColumn, int valueColumn) { return new IntDict(getStringColumn(keyColumn), - getIntColumn(valueColumn)); + getIntColumn(valueColumn)); } public FloatDict getFloatDict(String keyColumnName, String valueColumnName) { return new FloatDict(getStringColumn(keyColumnName), - getFloatColumn(valueColumnName)); + getFloatColumn(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)); } public StringDict getStringDict(int keyColumn, int valueColumn) { return new StringDict(getStringColumn(keyColumn), - getStringColumn(valueColumn)); + getStringColumn(valueColumn)); } @@ -4614,7 +4629,7 @@ public Map getRowMap(int column) { // } - // public StringIntPairs getColumnRowLookup(int col) { + // public StringIntPairs getColumnRowLookup(int col) { // StringIntPairs sc = new StringIntPairs(); // String[] column = getStringColumn(col); // for (int i = 0; i < column.length; i++) { @@ -4801,64 +4816,64 @@ protected void convertBasic(BufferedReader reader, boolean tsv, protected void convertRow(DataOutputStream output, String[] pieces) throws IOException { if (pieces.length > getColumnCount()) { throw new IllegalArgumentException("Row with too many columns: " + - PApplet.join(pieces, ",")); + PApplet.join(pieces, ",")); } // pieces.length may be less than columns.length, so loop over pieces for (int col = 0; col < pieces.length; col++) { switch (columnTypes[col]) { - case STRING: - output.writeUTF(pieces[col]); - break; - case INT: - output.writeInt(PApplet.parseInt(pieces[col], missingInt)); - break; - case LONG: - try { - output.writeLong(Long.parseLong(pieces[col])); - } catch (NumberFormatException nfe) { - output.writeLong(missingLong); - } - break; - case FLOAT: - output.writeFloat(PApplet.parseFloat(pieces[col], missingFloat)); - break; - case DOUBLE: - try { - output.writeDouble(Double.parseDouble(pieces[col])); - } catch (NumberFormatException nfe) { - output.writeDouble(missingDouble); - } - break; - case CATEGORY: - String peace = pieces[col]; - if (peace.equals(missingString)) { - output.writeInt(missingCategory); - } else { - output.writeInt(columnCategories[col].index(peace)); - } - break; + case STRING: + output.writeUTF(pieces[col]); + break; + case INT: + output.writeInt(PApplet.parseInt(pieces[col], missingInt)); + break; + case LONG: + try { + output.writeLong(Long.parseLong(pieces[col])); + } catch (NumberFormatException nfe) { + output.writeLong(missingLong); + } + break; + case FLOAT: + output.writeFloat(PApplet.parseFloat(pieces[col], missingFloat)); + break; + case DOUBLE: + try { + output.writeDouble(Double.parseDouble(pieces[col])); + } catch (NumberFormatException nfe) { + output.writeDouble(missingDouble); + } + break; + case CATEGORY: + String peace = pieces[col]; + if (peace.equals(missingString)) { + output.writeInt(missingCategory); + } else { + output.writeInt(columnCategories[col].index(peace)); + } + break; } } for (int col = pieces.length; col < getColumnCount(); col++) { switch (columnTypes[col]) { - case STRING: - output.writeUTF(""); - break; - case INT: - output.writeInt(missingInt); - break; - case LONG: - output.writeLong(missingLong); - break; - case FLOAT: - output.writeFloat(missingFloat); - break; - case DOUBLE: - output.writeDouble(missingDouble); - break; - case CATEGORY: - output.writeInt(missingCategory); - break; + case STRING: + output.writeUTF(""); + break; + case INT: + output.writeInt(missingInt); + break; + case LONG: + output.writeLong(missingLong); + break; + case FLOAT: + output.writeFloat(missingFloat); + break; + case DOUBLE: + output.writeDouble(missingDouble); + break; + case CATEGORY: + output.writeInt(missingCategory); + break; } } @@ -4917,4 +4932,4 @@ public void write(PrintWriter writer) { public void print() { writeTSV(new PrintWriter(System.out)); } -} \ No newline at end of file +} diff --git a/core/src/processing/data/TableRow.java b/libs/processing-core/src/main/java/processing/data/TableRow.java similarity index 85% rename from core/src/processing/data/TableRow.java rename to libs/processing-core/src/main/java/processing/data/TableRow.java index fe041fd78..3ac59fe4c 100644 --- a/core/src/processing/data/TableRow.java +++ b/libs/processing-core/src/main/java/processing/data/TableRow.java @@ -1,25 +1,3 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2013-16 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.data; import java.io.PrintWriter; @@ -90,7 +68,7 @@ public interface TableRow { * @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 @@ -98,7 +76,7 @@ public interface TableRow { * @see TableRow#getString(int) */ public double getDouble(int column); - + /** * @param columnName title of the column to reference */ @@ -132,7 +110,7 @@ public interface TableRow { * @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 @@ -141,7 +119,7 @@ public interface TableRow { * @see TableRow#setString(int, String) */ public void setLong(int column, long value); - + /** * @param columnName title of the target column */ @@ -156,7 +134,7 @@ public interface TableRow { * @see TableRow#setString(int, String) */ public void setFloat(int column, float value); - + /** * @param columnName title of the target column */ @@ -170,7 +148,7 @@ public interface TableRow { * @see TableRow#setString(int, String) */ public void setDouble(int column, double value); - + /** * @param columnName title of the target column */ @@ -182,19 +160,19 @@ public interface TableRow { * @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 diff --git a/core/src/processing/data/XML.java b/libs/processing-core/src/main/java/processing/data/XML.java similarity index 99% rename from core/src/processing/data/XML.java rename to libs/processing-core/src/main/java/processing/data/XML.java index bfe81012e..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-16 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 @@ -594,7 +594,14 @@ 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(); 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 aa5b8544f..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-16 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 97% rename from core/src/processing/event/KeyEvent.java rename to libs/processing-core/src/main/java/processing/event/KeyEvent.java index 0778db483..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-16 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/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 64f422427..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-16 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/core/src/processing/event/TouchEvent.java b/libs/processing-core/src/main/java/processing/event/TouchEvent.java similarity index 98% rename from core/src/processing/event/TouchEvent.java rename to libs/processing-core/src/main/java/processing/event/TouchEvent.java index 2add7c140..440a7824e 100644 --- a/core/src/processing/event/TouchEvent.java +++ b/libs/processing-core/src/main/java/processing/event/TouchEvent.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 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/opengl/FontTexture.java b/libs/processing-core/src/main/java/processing/opengl/FontTexture.java similarity index 99% rename from core/src/processing/opengl/FontTexture.java rename to libs/processing-core/src/main/java/processing/opengl/FontTexture.java index 52ec62f2a..cc67259bb 100644 --- a/core/src/processing/opengl/FontTexture.java +++ b/libs/processing-core/src/main/java/processing/opengl/FontTexture.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/opengl/FrameBuffer.java b/libs/processing-core/src/main/java/processing/opengl/FrameBuffer.java similarity index 99% rename from core/src/processing/opengl/FrameBuffer.java rename to libs/processing-core/src/main/java/processing/opengl/FrameBuffer.java index 239054784..a5426c52d 100644 --- a/core/src/processing/opengl/FrameBuffer.java +++ b/libs/processing-core/src/main/java/processing/opengl/FrameBuffer.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology 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 99% rename from core/src/processing/opengl/PGL.java rename to libs/processing-core/src/main/java/processing/opengl/PGL.java index 01b5f305e..18e4d8bb8 100644 --- a/core/src/processing/opengl/PGL.java +++ b/libs/processing-core/src/main/java/processing/opengl/PGL.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/opengl/PGLES.java b/libs/processing-core/src/main/java/processing/opengl/PGLES.java similarity index 99% rename from core/src/processing/opengl/PGLES.java rename to libs/processing-core/src/main/java/processing/opengl/PGLES.java index b86628048..e31799170 100644 --- a/core/src/processing/opengl/PGLES.java +++ b/libs/processing-core/src/main/java/processing/opengl/PGLES.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -37,6 +37,7 @@ import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.view.SurfaceView; + import processing.opengl.tess.PGLU; import processing.opengl.tess.PGLUtessellator; import processing.opengl.tess.PGLUtessellatorCallbackAdapter; @@ -58,6 +59,10 @@ public class PGLES extends PGL { /** The current surface view */ public GLSurfaceView glview; + + /** Requested major version of the OpenGL ES context */ + static public int version = 2; + // ........................................................ // Static initialization for some parameters that need to be different for diff --git a/core/src/processing/opengl/PGraphics2D.java b/libs/processing-core/src/main/java/processing/opengl/PGraphics2D.java similarity index 99% rename from core/src/processing/opengl/PGraphics2D.java rename to libs/processing-core/src/main/java/processing/opengl/PGraphics2D.java index 9422d08d4..2fdd47543 100644 --- a/core/src/processing/opengl/PGraphics2D.java +++ b/libs/processing-core/src/main/java/processing/opengl/PGraphics2D.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/opengl/PGraphics2DX.java b/libs/processing-core/src/main/java/processing/opengl/PGraphics2DX.java similarity index 77% rename from core/src/processing/opengl/PGraphics2DX.java rename to libs/processing-core/src/main/java/processing/opengl/PGraphics2DX.java index a812a69a2..60fee8e16 100755 --- a/core/src/processing/opengl/PGraphics2DX.java +++ b/libs/processing-core/src/main/java/processing/opengl/PGraphics2DX.java @@ -1,3 +1,25 @@ +/* -*- 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; @@ -8,23 +30,33 @@ import processing.core.PApplet; import processing.core.PGraphics; import processing.core.PImage; -import processing.core.PMatrix2D; import processing.core.PMatrix3D; import processing.core.PShape; import processing.core.PShapeSVG; -// Super fast OpenGL 2D renderer by Miles Fogle: -// https://github.com/hazmatsuitor - -//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) +/** + * 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 { @@ -36,13 +68,6 @@ public final class PGraphics2DX extends PGraphicsOpenGL { static protected final int SHADER2D = 7; - // Enables/disables matrix pre-multiplication - // https://github.com/processing/processing/wiki/Advanced-OpenGL#vertex-coordinates-are-in-model-space - // https://github.com/processing/processing/issues/2904 - // see above URLs for some discussion on premultiplying matrix vs. flushing buffer on matrix change. - // rather than committing to one or the other, this implementation supports both - public static boolean premultiplyMatrices = true; - // Uses the implementations in the parent PGraphicsOpenGL class, which is needed to to draw obj files // and apply shader filters. protected boolean useParentImpl = false; @@ -361,8 +386,6 @@ public void texture(PImage image) { return; } - init(); - Texture t = currentPG.getTexture(image); texWidth = t.width; texHeight = t.height; @@ -818,9 +841,6 @@ public void ellipseImpl(float a, float b, float c, float d) { return; } - //TODO: optimize this function, it is still pretty slow - //TODO: try using a lookup table and see if we can make it faster than real trig - beginShape(POLYGON); //convert corner/diameter to center/radius @@ -835,10 +855,15 @@ public void ellipseImpl(float a, float b, float c, float d) { int segments = circleDetail(PApplet.max(rx, ry) + (stroke? strokeWeight : 0), TWO_PI); float step = TWO_PI / segments; - float angle = 0; + float cos = PApplet.cos(step); + float sin = PApplet.sin(step); + float dx = 0, dy = 1; for (int i = 0; i < segments; ++i) { - angle += step; - shapeVertex(x + PApplet.sin(angle) * rx, y + PApplet.cos(angle) * ry, 0, 0, fillColor, 0); + 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; @@ -903,17 +928,20 @@ protected void arcImpl(float x, float y, float w, float h, float start, float st 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) { - float s = PApplet.cos(start) * w; - float c = PApplet.sin(start) * h; - - vertex(x + s, y + c); - - start += step; + 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 we still want to tessellate as if it is + //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); @@ -1065,200 +1093,6 @@ protected void textCharModelImpl(FontTexture.TextureInfo info, } - ////////////////////////////////////////////////////////////// - - // MATRIX OPS - - - /* - * Monkey-patch all methods that modify matrices to optionally flush the vertex buffer. - * If you see a method that isn't here but should be, or is here but shouldn't, - * feel free to add/remove it. - */ - - - @Override - public void applyMatrix(float n00, float n01, float n02, float n10, float n11, float n12) { - preMatrixChanged(); - super.applyMatrix(n00, n01, n02, n10, n11, n12); - postMatrixChanged(); - } - - - @Override - public void applyMatrix(PMatrix2D source) { - preMatrixChanged(); - super.applyMatrix(source); - postMatrixChanged(); - } - - - @Override - public void applyProjection(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) { - preMatrixChanged(); - super.applyProjection(n00, n01, n02, n03, - n10, n11, n12, n13, - n20, n21, n22, n23, - n30, n31, n32, n33); - postMatrixChanged(); - } - - - @Override - public void applyProjection(PMatrix3D mat) { - preMatrixChanged(); - super.applyProjection(mat); - postMatrixChanged(); - } - - - @Override - public void popMatrix() { - preMatrixChanged(); - super.popMatrix(); - postMatrixChanged(); - } - - - @Override - public void popProjection() { - preMatrixChanged(); - super.popProjection(); - postMatrixChanged(); - } - - - @Override - public void pushMatrix() { - preMatrixChanged(); - super.pushMatrix(); - postMatrixChanged(); - } - - - @Override - public void pushProjection() { - preMatrixChanged(); - super.pushProjection(); - postMatrixChanged(); - } - - - @Override - public void resetMatrix() { - preMatrixChanged(); - super.resetMatrix(); - postMatrixChanged(); - } - - - @Override - public void resetProjection() { - preMatrixChanged(); - super.resetProjection(); - postMatrixChanged(); - } - - - @Override - public void rotate(float angle) { - preMatrixChanged(); - super.rotate(angle); - postMatrixChanged(); - } - - - @Override - public void scale(float s) { - preMatrixChanged(); - super.scale(s); - postMatrixChanged(); - } - - - @Override - public void scale(float sx, float sy) { - preMatrixChanged(); - super.scale(sx, sy); - postMatrixChanged(); - } - - - @Override - public void setMatrix(PMatrix2D source) { - preMatrixChanged(); - super.setMatrix(source); - postMatrixChanged(); - } - - - @Override - public void setProjection(PMatrix3D mat) { - preMatrixChanged(); - super.setProjection(mat); - postMatrixChanged(); - } - - - @Override - public void shearX(float angle) { - preMatrixChanged(); - super.shearX(angle); - postMatrixChanged(); - } - - - @Override - public void shearY(float angle) { - preMatrixChanged(); - super.shearY(angle); - postMatrixChanged(); - } - - - @Override - public void translate(float tx, float ty) { - preMatrixChanged(); - super.translate(tx, ty); - postMatrixChanged(); - } - - - @Override - public void updateProjmodelview() { - preMatrixChanged(); - super.updateProjmodelview(); - postMatrixChanged(); - } - - - @Override - public void updateGLModelview() { - preMatrixChanged(); - super.updateGLModelview(); - postMatrixChanged(); - } - - - @Override - public void updateGLProjection() { - preMatrixChanged(); - super.updateGLProjection(); - postMatrixChanged(); - } - - - @Override - public void updateGLProjmodelview() { - preMatrixChanged(); - super.updateGLProjmodelview(); - postMatrixChanged(); - } - - ////////////////////////////////////////////////////////////// // MATRIX MORE! @@ -1285,8 +1119,10 @@ protected void end2D() { // 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) { @@ -1299,11 +1135,29 @@ public void filter(PShader 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) { @@ -1498,24 +1352,6 @@ public void lightSpecular(float v1, float v2, float v3) { // PRIVATE IMPLEMENTATION - //superclass does lazy initialization, so we need to as well - private void init() { - if (initialized) return; - initialized = true; - - String[] vertSource = pgl.loadVertexShader(defP2DShaderVertURL); - String[] fragSource = pgl.loadFragmentShader(defP2DShaderFragURL); - twoShader = new PShader(parent, vertSource, fragSource); - loadShaderLocs(twoShader); - defTwoShader = twoShader; - - //generate vbo - IntBuffer vboBuff = IntBuffer.allocate(1); - pgl.genBuffers(1, vboBuff); - vbo = vboBuff.get(0); - } - - //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 @@ -1531,28 +1367,31 @@ private void init() { 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 very good (~1,000,000 layers), so it pretty much won't happen - //unless you're drawing enough geometry per frame to set your computer on fire - if (depth < -0.9999f) { + // 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 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; } - //found to be a small but reliable increment value for a 24-bit depth buffer - //through trial and error. as numbers approach zero, absolute floating point - //precision increases, while absolute fixed point precision stays the same, - //so regardless of representation, this value should work for all depths in - //range (-1, 1), as long as it works for depths at either end of the range - depth -= 0.000001f; - - //TODO: use an increment value based on good math instead of lazy trial-and-error + depth -= smallestDepthIncrement; } @@ -1625,9 +1464,14 @@ private void flushBuffer() { return; } - init(); + if (vbo == 0) { + // Generate vbo + IntBuffer vboBuff = IntBuffer.allocate(1); + pgl.genBuffers(1, vboBuff); + vbo = vboBuff.get(0); + } - //upload vertex data + // Upload vertex data pgl.bindBuffer(PGL.ARRAY_BUFFER, vbo); pgl.bufferData(PGL.ARRAY_BUFFER, usedVerts * vertSize, FloatBuffer.wrap(vertexData), PGL.DYNAMIC_DRAW); @@ -1652,16 +1496,24 @@ private boolean checkShaderLocs(PShader shader) { if (positionLoc == -1) { positionLoc = shader.getAttributeLoc("vertex"); } - int colorLoc = shader.getAttributeLoc("color"); - int texCoordLoc = shader.getAttributeLoc("texCoord"); - int texFactorLoc = shader.getAttributeLoc("texFactor"); +// 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"); - return positionLoc != -1 && colorLoc != -1 && texCoordLoc != -1 && - texFactorLoc != -1 && transformLoc != -1 && texScaleLoc != -1; + 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; } @@ -1678,51 +1530,67 @@ private void loadShaderLocs(PShader shader) { 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); - } +// 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); - pgl.vertexAttribPointer(texCoordLoc, 2, PGL.FLOAT, false, vertSize, 3*Float.BYTES); - pgl.enableVertexAttribArray(texCoordLoc); + 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); - pgl.vertexAttribPointer(texFactorLoc, 1, PGL.FLOAT, false, vertSize, 6*Float.BYTES); - pgl.enableVertexAttribArray(texFactorLoc); + if (-1 < texFactorLoc) { + pgl.vertexAttribPointer(texFactorLoc, 1, PGL.FLOAT, false, vertSize, 6*Float.BYTES); + pgl.enableVertexAttribArray(texFactorLoc); + } } private void loadUniforms() { //set matrix uniform - if (premultiplyMatrices) { - pgl.uniformMatrix4fv(transformLoc, 1, true, FloatBuffer.wrap(new PMatrix3D().get(null))); - } else { - pgl.uniformMatrix4fv(transformLoc, 1, true, FloatBuffer.wrap(projmodelview.get(null))); - } + pgl.uniformMatrix4fv(transformLoc, 1, true, FloatBuffer.wrap(new PMatrix3D().get(null))); //set texture info pgl.activeTexture(PGL.TEXTURE0); pgl.bindTexture(PGL.TEXTURE_2D, tex); - //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); + 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); + } } } @@ -1746,14 +1614,9 @@ private void check(int newVerts) { private void vertexImpl(float x, float y, float u, float v, int c, float f) { int idx = usedVerts * 7; - if (premultiplyMatrices) { - //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; - } else { - vertexData[idx + 0] = x; - vertexData[idx + 1] = y; - } + //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; @@ -1826,30 +1689,6 @@ private void shapeVertex(float x, float y, float u, float v, int c, float f) { } - float ellipseDetailMultiplier = 1; - - - private void preMatrixChanged() { - if (!premultiplyMatrices) { - flushBuffer(); - } - } - - - private void postMatrixChanged() { - //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 Imag = PApplet.sqrt(sxi * sxi + syi * syi); - float Jmag = PApplet.sqrt(sxj * sxj + syj * syj); - ellipseDetailMultiplier = PApplet.max(Imag, Jmag); - } - - 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); @@ -1883,55 +1722,40 @@ private void singleLine(float x1, float y1, float x2, float y2, int color) { triangle(x2 + tx, y2 - ty, x2 - tx, y2 + ty, x1 + tx, y1 - ty, color); if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) { - float angle = PApplet.atan2(dx, dy); - 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; - float psin = ty; - float pcos = tx; - for (int i = 1; i < segments; ++i) { - angle += step; - float nsin = PApplet.sin(angle) * r; - float ncos = PApplet.cos(angle) * r; - - triangle(x2, y2, x2 + psin, y2 + pcos, x2 + nsin, y2 + ncos, color); - triangle(x2, y2, x2 - pcos, y2 + psin, x2 - ncos, y2 + nsin, color); - triangle(x1, y1, x1 - psin, y1 - pcos, x1 - nsin, y1 - ncos, color); - triangle(x1, y1, x1 + pcos, y1 - psin, x1 + ncos, y1 - nsin, color); + 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); - psin = nsin; - pcos = ncos; + tx = nx; + ty = ny; } - - triangle(x2, y2, x2 + psin, y2 + pcos, x2 + tx, y2 - ty, color); - triangle(x2, y2, x2 - pcos, y2 + psin, x2 + ty, y2 + tx, color); - triangle(x1, y1, x1 - psin, y1 - pcos, x1 - tx, y1 + ty, color); - triangle(x1, y1, x1 + pcos, y1 - psin, x1 - ty, y1 - tx, color); } } private void singlePoint(float x, float y, int color) { float r = strokeWeight * 0.5f; - if (strokeCap == ROUND) { + if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) { int segments = circleDetail(r); float step = QUARTER_PI / segments; - float x1 = 0; - float y1 = r; - float angle = 0; + float x1 = 0, y1 = r; + float c = PApplet.cos(step); + float s = PApplet.sin(step); for (int i = 0; i < segments; ++i) { - angle += step; - float x2, y2; - //this is not just for performance - //it also ensures the circle is drawn with no diagonal gaps - if (i < segments - 1) { - x2 = PApplet.sin(angle) * r; - y2 = PApplet.cos(angle) * r; - } else { - x2 = y2 = PApplet.sin(QUARTER_PI) * r; - } + //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); @@ -1964,6 +1788,34 @@ private class StrokeRenderer { 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; @@ -1985,23 +1837,23 @@ void lineVertex(float x, float y) { sx = x; sy = y; } else { - //find leg angles - float angle1 = PApplet.atan2(lx - px, ly - py); - float angle2 = PApplet.atan2(lx - x, ly - y); - - //find minimum absolute angle between the two legs - //FROM: https://stackoverflow.com/a/7869457/3064745 - //NOTE: this only works for angles that are in range [-180, 180] !!! - float diff = angle1 - angle2; - diff += diff > PI? -TWO_PI : diff < -PI? TWO_PI : 0; - - if (strokeJoin == BEVEL || strokeJoin == ROUND || - PApplet.abs(diff) < PI/15 || PApplet.abs(diff) > PI - 0.001f) { - 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; + //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; @@ -2011,28 +1863,17 @@ void lineVertex(float x, float y) { triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor); } - dx = x - lx; - dy = y - ly; - d = PApplet.sqrt(dx*dx + dy*dy); - float nx = dy / d * r; - float ny = -dx / d * r; + float nx = leg2y * r; + float ny = -leg2x * r; + float legCross = leg1x * leg2y - leg1y * leg2x; if (strokeJoin == ROUND) { - float theta1 = diff > 0? angle1 - HALF_PI : angle1 + HALF_PI; - float theta2 = diff > 0? angle2 + HALF_PI : angle2 - HALF_PI; - - //find minimum absolute angle diff (again) - float delta = theta2 - theta1; - delta += delta > PI? -TWO_PI : delta < -PI? TWO_PI : 0; - - //start and end points of arc - float ax1 = diff < 0? lx + tx : lx - tx; - float ay1 = diff < 0? ly + ty : ly - ty; - float ax2 = diff < 0? lx + nx : lx - nx; - float ay2 = diff < 0? ly + ny : ly - ny; - - arcJoin(lx, ly, theta1, delta, ax1, ay1, ax2, ay2); - } else if (diff < 0) { + 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); @@ -2040,19 +1881,17 @@ void lineVertex(float x, float y) { pdx = nx; pdy = ny; - } else { - //find offset (hypotenuse) of miter joint - float theta = HALF_PI - diff/2; - float offset = r / PApplet.cos(theta); - - //find bisecting vector - float angle = (angle1 + angle2)/2; - float bx = PApplet.sin(angle) * offset; - float by = PApplet.cos(angle) * offset; - if (PApplet.abs(angle1 - angle2) < PI) { - bx *= -1; - by *= -1; - } + } 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; @@ -2075,6 +1914,26 @@ void lineVertex(float x, float 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; @@ -2117,7 +1976,7 @@ void endLine(boolean closed) { triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor); if (strokeCap == ROUND) { - lineCap(lx, ly, PApplet.atan2(dx, dy)); + lineCap(lx, ly, -ty, tx); } //draw first line (with cap) @@ -2136,57 +1995,25 @@ void endLine(boolean closed) { triangle(sx + sdx, sy + sdy, fx + tx, fy + ty, fx - tx, fy - ty, strokeColor); if (strokeCap == ROUND) { - lineCap(fx, fy, PApplet.atan2(dx, dy)); + lineCap(fx, fy, -ty, tx); } } } - - void arcJoin(float x, float y, float start, float delta, float x1, float y1, float x3, float y3) { - int segments = circleDetail(r, delta); - float step = delta / segments; - - for (int i = 0; i < segments - 1; ++i) { - start += step; - float x2 = x + PApplet.sin(start) * r; - float y2 = y + PApplet.cos(start) * r; - - triangle(x, y, x1, y1, x2, y2, strokeColor); - - x1 = x2; - y1 = y2; - } - - triangle(x, y, x1, y1, x3, y3, strokeColor); - } - - //XXX: wet code, will probably get removed when we optimize lineCap() - void arcJoin(float x, float y, float start, float delta) { - int segments = circleDetail(r, delta); - float step = delta / segments; - - float x1 = x + PApplet.sin(start) * r; - float y1 = y + PApplet.cos(start) * r; - for (int i = 0; i < segments; ++i) { - start += step; - float x2 = x + PApplet.sin(start) * r; - float y2 = y + PApplet.cos(start) * r; - - triangle(x, y, x1, y1, x2, y2, strokeColor); - - x1 = x2; - y1 = y2; - } - } - - void lineCap(float x, float y, float angle) { - //TODO: optimize this - arcJoin(x, y, angle - HALF_PI, PI); - } } //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); } @@ -2225,4 +2052,4 @@ public String toString() { return x + ", " + y; } } -} \ No newline at end of file +} diff --git a/core/src/processing/opengl/PGraphics3D.java b/libs/processing-core/src/main/java/processing/opengl/PGraphics3D.java similarity index 99% rename from core/src/processing/opengl/PGraphics3D.java rename to libs/processing-core/src/main/java/processing/opengl/PGraphics3D.java index 2e03f0a97..5a5e1c3b3 100644 --- a/core/src/processing/opengl/PGraphics3D.java +++ b/libs/processing-core/src/main/java/processing/opengl/PGraphics3D.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/libs/processing-core/src/main/java/processing/opengl/PGraphicsOpenGL.java similarity index 97% rename from core/src/processing/opengl/PGraphicsOpenGL.java rename to libs/processing-core/src/main/java/processing/opengl/PGraphicsOpenGL.java index bc902677d..478a2f13b 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/libs/processing-core/src/main/java/processing/opengl/PGraphicsOpenGL.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -341,6 +341,18 @@ public void dispose() { /** 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: @@ -520,6 +532,21 @@ public void dispose() { // ........................................................ + // 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 = @@ -830,6 +857,276 @@ public boolean saveImpl(String filename) { } + ////////////////////////////////////////////////////////////// + + // EYE/OBJECT MATRICES + + + @Override + public PMatrix3D getEyeMatrix() { + return getEyeMatrix(null); + } + + + @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 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; + } + + + @Override + public void eye() { + eyeMatrix = getEyeMatrix(eyeMatrix); + + // Erasing any previous transformation in modelview + modelview.set(camera); + modelview.apply(eyeMatrix); + + // 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; + + // Applying the inverse of the previous transformations in the opposite order + // to compute the modelview inverse + modelviewInv.set(eyeMatrix); + modelviewInv.preApply(cameraInv); + + updateProjmodelview(); + } + + + ////////////////////////////////////////////////////////////// + + // 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; + } + + + @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); + } + + + @Override + public boolean intersectsSphere(float r, float screenX, float screenY) { + ray = getRayFromScreen(screenX, screenY, ray); + return intersectsSphere(r, ray[0], ray[1]); + } + + + @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); + + return rayIntersectsSphere(origInObjCoord, dirInObjCoord, r); + } + + + // 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(); + + // The eye is inside the sphere + if (d <= r) return true; + + float p = PVector.dot(orig, dir); + + // Check if sphere is in front of ray + if (p > 0) return false; + + // 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; + } + + + @Override + public boolean intersectsBox(float size, float screenX, float screenY) { + ray = getRayFromScreen(screenX, screenY, ray); + return intersectsBox(size, size, size, ray[0], ray[1]); + } + + + @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]); + } + + + @Override + public boolean intersectsBox(float size, PVector origin, PVector direction) { + return intersectsBox(size, size, size, origin, direction); + } + + + @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); + + return lineIntersectsAABB(origInObjCoord, dirInObjCoord, w, h, d); + } + + + // 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; + + float maxx = +w/2; + float maxy = +h/2; + float maxz = +d/2; + + float idx = 1/dir.x; + float idy = 1/dir.y; + float idz = 1/dir.z; + + boolean sdx = idx < 0; + boolean sdy = idy < 0; + boolean sdz = idz < 0; + + 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; + + if ((txmin > tymax) || (tymin > txmax)) { + return false; + } + if (tymin > txmin) { + txmin = tymin; + } + if (tymax < txmax) { + txmax = tymax; + } + + float bbz = sdz ? maxz : minz; + float tzmin = (bbz - orig.z) * idz; + bbz = sdz ? minz : maxz; + float tzmax = (bbz - orig.z) * idz; + + if ((txmin > tzmax) || (tzmin > txmax)) { + return false; + } + if (tzmin > txmin) { + txmin = tzmin; + } + if (tzmax < txmax) { + txmax = tzmax; + } + + 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; + } + + + @Override + public PVector intersectsPlane(float screenX, float screenY) { + ray = getRayFromScreen(screenX, screenY, ray); + return intersectsPlane(ray[0], ray[1]); + } + + + @Override + public PVector intersectsPlane(PVector origin, PVector direction) { + modelview.mult(origin, origInWorldCoord); + modelview.mult(direction, dirInWorldCoord); + dirInWorldCoord.normalize(); + + // Plane representation + PVector point = new PVector(0, 0, 0); + PVector normal = new PVector(0, 0, 1); + + // Ray-plane intersection algorithm + float d = PApplet.abs(PVector.dot(normal, dirInWorldCoord)); + if (d == 0) return null; + + PVector w = PVector.sub(point, origInWorldCoord); + float k = PApplet.abs(PVector.dot(normal, w)/d); + PVector p = PVector.add(origInWorldCoord, dirInWorldCoord).setMag(k); + + return p; + } + + ////////////////////////////////////////////////////////////// // IMAGE METADATA FOR THIS RENDERER @@ -3958,9 +4255,16 @@ 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); } @@ -4049,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); } @@ -4103,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); @@ -4573,16 +4887,28 @@ public void camera(float eyeX, float eyeY, float eyeZ, z2 /= eyeDist; } + 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; @@ -6448,25 +6774,25 @@ 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; } @@ -6690,7 +7016,7 @@ protected Object initCache(PImage img) { if (tex == null || tex.contextIsOutdated()) { tex = addTexture(img); if (tex != null) { - boolean dispose = !img.loaded; + boolean dispose = img.pixels == null; img.loadPixels(); tex.set(img.pixels, img.format); img.setModified(); @@ -7300,28 +7626,27 @@ protected PShader getPolyShader(boolean lit, boolean tex) { 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); - String[] fragSource = pgl.loadFragmentShader(defTexlightShaderFragURL); - 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); - String[] fragSource = pgl.loadFragmentShader(defLightShaderFragURL); - ppg.defLightShader = new PShader(parent, vertSource, fragSource); + ppg.defLightShader = loadShaderFromURL(defLightShaderFragURL, + defLightShaderVertURL); } shader = ppg.defLightShader; } else { @@ -7329,28 +7654,26 @@ 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 (useDefault || !isPolyShaderTex(polyShader)) { if (ppg.defTextureShader == null) { - String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL); - String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL); - ppg.defTextureShader = new PShader(parent, vertSource, fragSource); + ppg.defTextureShader = loadShaderFromURL(defTextureShaderFragURL, + defTextureShaderVertURL); } shader = ppg.defTextureShader; } else { shader = polyShader; } } else { - if (useDefault || !polyShader.checkPolyType(PShader.COLOR)) { + if (useDefault || !isPolyShaderColor(polyShader)) { if (ppg.defColorShader == null) { - String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL); - String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL); - ppg.defColorShader = new PShader(parent, vertSource, fragSource); + ppg.defColorShader = loadShaderFromURL(defColorShaderFragURL, + defColorShaderVertURL); } shader = ppg.defColorShader; } else { @@ -7359,14 +7682,52 @@ protected PShader getPolyShader(boolean lit, boolean tex) { } } if (shader != polyShader) { - shader.setRenderer(this); - shader.loadAttributes(); - shader.loadUniforms(); + 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(); + } + + protected PShader getLineShader() { PShader shader; PGraphicsOpenGL ppg = getPrimaryPG(); diff --git a/core/src/processing/opengl/PShader.java b/libs/processing-core/src/main/java/processing/opengl/PShader.java similarity index 99% rename from core/src/processing/opengl/PShader.java rename to libs/processing-core/src/main/java/processing/opengl/PShader.java index 1b5a2a9b5..c3e47ead5 100644 --- a/core/src/processing/opengl/PShader.java +++ b/libs/processing-core/src/main/java/processing/opengl/PShader.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/opengl/PShapeOpenGL.java b/libs/processing-core/src/main/java/processing/opengl/PShapeOpenGL.java similarity index 89% rename from core/src/processing/opengl/PShapeOpenGL.java rename to libs/processing-core/src/main/java/processing/opengl/PShapeOpenGL.java index 31750aa4f..3f65727b4 100644 --- a/core/src/processing/opengl/PShapeOpenGL.java +++ b/libs/processing-core/src/main/java/processing/opengl/PShapeOpenGL.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology @@ -43,7 +43,6 @@ 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 @@ -171,7 +170,8 @@ public class PShapeOpenGL extends PShape { // Geometric transformations. protected PMatrix transform; - protected Stack transformStack; + protected PMatrix transformInv; + protected PMatrix matrixInv; // ........................................................ @@ -349,7 +349,7 @@ public PShapeOpenGL(PGraphicsOpenGL pg, int family) { 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 @@ -608,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); @@ -623,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); @@ -638,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); @@ -713,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); } } } @@ -847,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) { @@ -1063,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(); } @@ -1114,7 +1114,7 @@ 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); + PGL.FLOAT, 3); if (attrib != null) attrib.set(x, y, z); } @@ -1122,7 +1122,7 @@ public void attribPosition(String name, float x, float y, float z) { @Override public void attribNormal(String name, float nx, float ny, float nz) { VertexAttribute attrib = attribImpl(name, VertexAttribute.NORMAL, - PGL.FLOAT, 3); + PGL.FLOAT, 3); if (attrib != null) attrib.set(nx, ny, nz); } @@ -1137,7 +1137,7 @@ public void attribColor(String name, int color) { @Override public void attrib(String name, float... values) { VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.FLOAT, - values.length); + values.length); if (attrib != null) attrib.set(values); } @@ -1145,7 +1145,7 @@ public void attrib(String name, float... values) { @Override public void attrib(String name, int... values) { VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.INT, - values.length); + values.length); if (attrib != null) attrib.set(values); } @@ -1153,7 +1153,7 @@ public void attrib(String name, int... values) { @Override public void attrib(String name, boolean... values) { VertexAttribute attrib = attribImpl(name, VertexAttribute.OTHER, PGL.BOOL, - values.length); + values.length); if (attrib != null) attrib.set(values); } @@ -1216,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; } @@ -1251,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); + } } @@ -1308,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); } @@ -1316,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); } @@ -1326,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; @@ -1377,67 +1376,61 @@ 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.preApply(transform); - pushTransform(); - if (tessellated) applyMatrixImpl(transform); - } - - - protected void pushTransform() { - if (transformStack == null) transformStack = new Stack(); - PMatrix mat; - if (transform instanceof PMatrix2D) { - mat = new PMatrix2D(); + 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()) { @@ -1450,14 +1443,14 @@ protected void applyMatrixImpl(PMatrix matrix) { 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); } @@ -1465,6 +1458,23 @@ protected void applyMatrixImpl(PMatrix matrix) { } + @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); + } + } + + /////////////////////////////////////////////////////////// // @@ -1487,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); } @@ -1497,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); } @@ -1506,11 +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()); + x3, y3, z3, + x4, y4, z4, vertexBreak()); } @@ -1518,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); } @@ -1526,17 +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()); + x3, y3, z3, vertexBreak()); } @@ -1581,7 +1591,7 @@ 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()); } @@ -1755,10 +1765,11 @@ public void setAttrib(String name, int index, float... values) { return; } - VertexAttribute attrib = polyAttribs.get(name); + 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 + 0] = values[i]; + array[attrib.size * index + i] = values[i]; } markForTessellation(); } @@ -1771,10 +1782,11 @@ public void setAttrib(String name, int index, int... values) { return; } - VertexAttribute attrib = polyAttribs.get(name); + 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 + 0] = values[i]; + array[attrib.size * index + i] = values[i]; } markForTessellation(); } @@ -1787,10 +1799,11 @@ public void setAttrib(String name, int index, boolean... values) { return; } - VertexAttribute attrib = polyAttribs.get(name); + 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 + 0] = (byte)(values[i]?1:0); + array[attrib.size * index + i] = (byte)(values[i]?1:0); } markForTessellation(); } @@ -1879,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); } } @@ -1974,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); } } @@ -2080,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); } } @@ -2280,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); } } @@ -2345,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); } } @@ -2408,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); } } @@ -2474,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; @@ -2731,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; } } @@ -2822,15 +2835,21 @@ protected void initModified() { protected void tessellate() { if (root == this && parent == null) { // Root shape + boolean initAttr = false; if (polyAttribs == null) { polyAttribs = PGraphicsOpenGL.newAttributeMap(); - collectPolyAttribs(); + initAttr = true; } if (tessGeo == null) { 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); @@ -2848,6 +2867,7 @@ protected void tessellate() { protected void collectPolyAttribs() { AttributeMap rootAttribs = root.polyAttribs; + tessGeo = root.tessGeo; if (family == GROUP) { for (int i = 0; i < childCount; i++) { @@ -2950,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(); } @@ -3018,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(); @@ -3043,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(); } @@ -3066,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(); } @@ -3093,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(); } @@ -3137,26 +3157,26 @@ protected void tessellateRect() { 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) { @@ -3174,7 +3194,7 @@ 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(); @@ -3230,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(); @@ -3290,7 +3310,7 @@ protected void tessellateArc() { 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, arcMode); tessellator.tessellateTriangleFan(); @@ -3310,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(); } @@ -3340,7 +3360,7 @@ 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); @@ -3355,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 @@ -3365,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 @@ -3377,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; } } } @@ -3601,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); } } } @@ -3653,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); } @@ -3686,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]; @@ -3699,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()) { @@ -3721,7 +3741,7 @@ protected void updatePolyIndexCache() { protected boolean startStrokedTex(int n) { return image != null && (n == firstLineIndexCache || - n == firstPointIndexCache); + n == firstPointIndexCache); } @@ -3761,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); } @@ -3780,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]; @@ -3792,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; @@ -3829,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); } @@ -3840,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); @@ -3849,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]; @@ -3861,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; @@ -3912,56 +3932,56 @@ protected void initPolyBuffers() { 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 (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 (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 (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 (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 (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 (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 (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); @@ -3969,7 +3989,7 @@ protected void initPolyBuffers() { 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); + tessGeo.polyAttribBuffers.get(name), glUsage); } pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); @@ -3979,8 +3999,8 @@ protected void initPolyBuffers() { 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); } @@ -3996,21 +4016,21 @@ protected void initLineBuffers() { 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 (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 (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); @@ -4019,8 +4039,8 @@ protected void initLineBuffers() { 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); } @@ -4036,21 +4056,21 @@ protected void initPointBuffers() { 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 (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 (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); @@ -4059,8 +4079,8 @@ protected void initPointBuffers() { 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); } @@ -4247,7 +4267,7 @@ protected void copyPolyVertices(int offset, int size) { 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); } @@ -4258,7 +4278,7 @@ protected void copyPolyColors(int offset, int size) { 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); } @@ -4269,7 +4289,7 @@ protected void copyPolyNormals(int offset, int size) { 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); } @@ -4280,7 +4300,7 @@ protected void copyPolyTexCoords(int offset, int size) { 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); } @@ -4291,7 +4311,7 @@ protected void copyPolyAmbient(int offset, int size) { 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); } @@ -4302,7 +4322,7 @@ protected void copyPolySpecular(int offset, int size) { 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); } @@ -4313,7 +4333,7 @@ protected void copyPolyEmissive(int offset, int size) { 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); } @@ -4324,7 +4344,7 @@ protected void copyPolyShininess(int offset, int size) { 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); } @@ -4336,7 +4356,7 @@ protected void copyPolyAttrib(VertexAttribute attrib, int offset, int size) { Buffer buf = tessGeo.polyAttribBuffers.get(attrib.name); buf.position(attrib.size * offset); pgl.bufferSubData(PGL.ARRAY_BUFFER, attrib.sizeInBytes(offset), - attrib.sizeInBytes(size), buf); + attrib.sizeInBytes(size), buf); buf.rewind(); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); } @@ -4347,7 +4367,7 @@ protected void copyLineVertices(int offset, int size) { 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); } @@ -4358,7 +4378,7 @@ protected void copyLineColors(int offset, int size) { 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); } @@ -4369,7 +4389,7 @@ protected void copyLineAttributes(int offset, int size) { 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); } @@ -4380,7 +4400,7 @@ protected void copyPointVertices(int offset, int size) { 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); } @@ -4391,7 +4411,7 @@ protected void copyPointColors(int offset, int size) { 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); } @@ -4402,7 +4422,7 @@ protected void copyPointAttributes(int offset, int size) { 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); } @@ -4711,63 +4731,63 @@ private void inGeoToVertices() { 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); + case VERTEX: + v = 3 * idx; + x = inGeo.vertices[v++]; + y = inGeo.vertices[v ]; + super.vertex(x, y); - idx++; - break; + idx++; + break; - case QUADRATIC_VERTEX: - v = 3 * idx; - cx = inGeo.vertices[v++]; - cy = inGeo.vertices[v]; + 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]; + v = 3 * (idx + 1); + x3 = inGeo.vertices[v++]; + y3 = inGeo.vertices[v]; - super.quadraticVertex(cx, cy, x3, y3); + super.quadraticVertex(cx, cy, x3, y3); - idx += 2; - break; + idx += 2; + break; - case BEZIER_VERTEX: - v = 3 * idx; - x2 = inGeo.vertices[v++]; - y2 = inGeo.vertices[v ]; + 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 + 1); + x3 = inGeo.vertices[v++]; + y3 = inGeo.vertices[v ]; - v = 3 * (idx + 2); - x4 = inGeo.vertices[v++]; - y4 = inGeo.vertices[v ]; + v = 3 * (idx + 2); + x4 = inGeo.vertices[v++]; + y4 = inGeo.vertices[v ]; - super.bezierVertex(x2, y2, x3, y3, x4, y4); + super.bezierVertex(x2, y2, x3, y3, x4, y4); - idx += 3; - break; + idx += 3; + break; - case CURVE_VERTEX: - v = 3 * idx; - x = inGeo.vertices[v++]; - y = inGeo.vertices[v ]; + case CURVE_VERTEX: + v = 3 * idx; + x = inGeo.vertices[v++]; + y = inGeo.vertices[v ]; - super.curveVertex(x, y); + super.curveVertex(x, y); - idx++; - break; + idx++; + break; - case BREAK: - if (insideContour) { - super.endContourImpl(); - } - super.beginContourImpl(); - insideContour = true; + case BREAK: + if (insideContour) { + super.endContourImpl(); + } + super.beginContourImpl(); + insideContour = true; } } if (insideContour) { @@ -4785,8 +4805,8 @@ private void inGeoToVertices() { // shape to be rendered separately. protected boolean fragmentedGroup(PGraphicsOpenGL g) { return g.getHint(DISABLE_OPTIMIZED_STROKE) || - (textures != null && (1 < textures.size() || untexChild)) || - strokedTexture; + (textures != null && (1 < textures.size() || untexChild)) || + strokedTexture; } @@ -4829,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) { @@ -4871,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); @@ -4907,30 +4927,30 @@ protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) { int voffset = cache.vertexOffset[n]; shader.setVertexAttribute(root.bufPolyVertex.glId, 4, PGL.FLOAT, - 0, 4 * voffset * PGL.SIZEOF_FLOAT); + 0, 4 * voffset * PGL.SIZEOF_FLOAT); shader.setColorAttribute(root.bufPolyColor.glId, 4, PGL.UNSIGNED_BYTE, - 0, 4 * voffset * PGL.SIZEOF_BYTE); + 0, 4 * voffset * PGL.SIZEOF_BYTE); if (g.lights) { shader.setNormalAttribute(root.bufPolyNormal.glId, 3, PGL.FLOAT, - 0, 3 * voffset * PGL.SIZEOF_FLOAT); + 0, 3 * voffset * PGL.SIZEOF_FLOAT); shader.setAmbientAttribute(root.bufPolyAmbient.glId, 4, PGL.UNSIGNED_BYTE, - 0, 4 * voffset * PGL.SIZEOF_BYTE); + 0, 4 * voffset * PGL.SIZEOF_BYTE); shader.setSpecularAttribute(root.bufPolySpecular.glId, 4, PGL.UNSIGNED_BYTE, - 0, 4 * voffset * PGL.SIZEOF_BYTE); + 0, 4 * voffset * PGL.SIZEOF_BYTE); shader.setEmissiveAttribute(root.bufPolyEmissive.glId, 4, PGL.UNSIGNED_BYTE, - 0, 4 * voffset * PGL.SIZEOF_BYTE); + 0, 4 * voffset * PGL.SIZEOF_BYTE); shader.setShininessAttribute(root.bufPolyShininess.glId, 1, PGL.FLOAT, - 0, voffset * PGL.SIZEOF_FLOAT); + 0, voffset * PGL.SIZEOF_FLOAT); } if (g.lights || needNormals) { shader.setNormalAttribute(root.bufPolyNormal.glId, 3, PGL.FLOAT, - 0, 3 * voffset * PGL.SIZEOF_FLOAT); + 0, 3 * voffset * PGL.SIZEOF_FLOAT); } if (tex != null || needTexCoords) { shader.setTexcoordAttribute(root.bufPolyTexcoord.glId, 2, PGL.FLOAT, - 0, 2 * voffset * PGL.SIZEOF_FLOAT); + 0, 2 * voffset * PGL.SIZEOF_FLOAT); shader.setTexture(tex); } @@ -4938,8 +4958,8 @@ protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) { 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)); + attrib.tessSize, attrib.type, + attrib.isColor(), 0, attrib.sizeInBytes(voffset)); } shader.draw(root.bufPolyIndex.glId, icount, ioffset); @@ -5060,11 +5080,11 @@ protected void renderLines(PGraphicsOpenGL g) { int voffset = cache.vertexOffset[n]; shader.setVertexAttribute(root.bufLineVertex.glId, 4, PGL.FLOAT, - 0, 4 * voffset * PGL.SIZEOF_FLOAT); + 0, 4 * voffset * PGL.SIZEOF_FLOAT); shader.setColorAttribute(root.bufLineColor.glId, 4, PGL.UNSIGNED_BYTE, - 0, 4 * voffset * PGL.SIZEOF_BYTE); + 0, 4 * voffset * PGL.SIZEOF_BYTE); shader.setLineAttribute(root.bufLineAttrib.glId, 4, PGL.FLOAT, - 0, 4 * voffset * PGL.SIZEOF_FLOAT); + 0, 4 * voffset * PGL.SIZEOF_FLOAT); shader.draw(root.bufLineIndex.glId, icount, ioffset); } @@ -5157,11 +5177,11 @@ protected void renderPoints(PGraphicsOpenGL g) { int voffset = cache.vertexOffset[n]; shader.setVertexAttribute(root.bufPointVertex.glId, 4, PGL.FLOAT, - 0, 4 * voffset * PGL.SIZEOF_FLOAT); + 0, 4 * voffset * PGL.SIZEOF_FLOAT); shader.setColorAttribute(root.bufPointColor.glId, 4, PGL.UNSIGNED_BYTE, - 0, 4 * voffset * PGL.SIZEOF_BYTE); + 0, 4 * voffset * PGL.SIZEOF_BYTE); shader.setPointAttribute(root.bufPointAttrib.glId, 2, PGL.FLOAT, - 0, 2 * voffset * PGL.SIZEOF_FLOAT); + 0, 2 * voffset * PGL.SIZEOF_FLOAT); shader.draw(root.bufPointIndex.glId, icount, ioffset); } @@ -5197,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/core/src/processing/opengl/PSurfaceGLES.java b/libs/processing-core/src/main/java/processing/opengl/PSurfaceGLES.java similarity index 99% rename from core/src/processing/opengl/PSurfaceGLES.java rename to libs/processing-core/src/main/java/processing/opengl/PSurfaceGLES.java index 5a1289f32..b9a86ae87 100644 --- a/core/src/processing/opengl/PSurfaceGLES.java +++ b/libs/processing-core/src/main/java/processing/opengl/PSurfaceGLES.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -28,7 +28,6 @@ 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; @@ -40,6 +39,7 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; + import processing.android.AppComponent; import processing.android.PFragment; import processing.core.PApplet; @@ -118,7 +118,7 @@ public SurfaceViewGLES(Context context, SurfaceHolder holder) { h.addCallback(this); // Tells the default EGLContextFactory and EGLConfigChooser to create an GLES2 context. - setEGLContextClientVersion(2); + setEGLContextClientVersion(PGLES.version); setPreserveEGLContextOnPause(true); int samples = sketch.sketchSmooth(); @@ -295,7 +295,7 @@ protected class ContextFactoryGLES implements GLSurfaceView.EGLContextFactory { public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - int[] attrib_list = { PGLES.EGL_CONTEXT_CLIENT_VERSION, 2, + int[] attrib_list = { PGLES.EGL_CONTEXT_CLIENT_VERSION, PGLES.version, EGL10.EGL_NONE }; EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, diff --git a/core/src/processing/opengl/Texture.java b/libs/processing-core/src/main/java/processing/opengl/Texture.java similarity index 99% rename from core/src/processing/opengl/Texture.java rename to libs/processing-core/src/main/java/processing/opengl/Texture.java index e6e84c6ba..29d8ba4c5 100644 --- a/core/src/processing/opengl/Texture.java +++ b/libs/processing-core/src/main/java/processing/opengl/Texture.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology diff --git a/core/src/processing/opengl/VertexBuffer.java b/libs/processing-core/src/main/java/processing/opengl/VertexBuffer.java similarity index 98% rename from core/src/processing/opengl/VertexBuffer.java rename to libs/processing-core/src/main/java/processing/opengl/VertexBuffer.java index e4ef85b9c..114428b03 100644 --- a/core/src/processing/opengl/VertexBuffer.java +++ b/libs/processing-core/src/main/java/processing/opengl/VertexBuffer.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2004-12 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology 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/mode/libraries/vr/src/processing/vr/PVR.java b/libs/processing-vr/src/main/java/processing/vr/VRActivity.java similarity index 85% rename from mode/libraries/vr/src/processing/vr/PVR.java rename to libs/processing-vr/src/main/java/processing/vr/VRActivity.java index 7e0767035..28e199585 100644 --- a/mode/libraries/vr/src/processing/vr/PVR.java +++ b/libs/processing-vr/src/main/java/processing/vr/VRActivity.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -22,37 +22,35 @@ package processing.vr; -import com.google.vr.sdk.base.GvrActivity; -import com.google.vr.sdk.base.Eye; - 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 PVR extends GvrActivity implements AppComponent { - public static final int LEFT = Eye.Type.LEFT; - public static final int RIGHT = Eye.Type.RIGHT; - public static final int MONOCULAR = Eye.Type.MONOCULAR; - - static public final int VR = 3; +public class VRActivity extends GvrActivity implements AppComponent { + static public final int GVR = 3; private DisplayMetrics metrics; private PApplet sketch; - public PVR() { + public VRActivity() { } - static public PGraphicsVR getRenderer(PApplet p) { - return (PGraphicsVR) p.g; + static public VRGraphics getRenderer(PApplet p) { + return (VRGraphics) p.g; } - public PVR(PApplet sketch) { + public VRActivity(PApplet sketch) { this.sketch = sketch; } @@ -78,7 +76,7 @@ public float getDisplayDensity() { public int getKind() { - return VR; + return GVR; } @@ -89,7 +87,7 @@ public void dispose() { public void setSketch(PApplet sketch) { this.sketch = sketch; if (sketch != null) { - sketch.initSurface(PVR.this, null); + sketch.initSurface(VRActivity.this, null); // Required to read the paired viewer's distortion parameters. sketch.requestPermission("android.permission.READ_EXTERNAL_STORAGE"); } 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/mode/libraries/vr/src/processing/vr/PGraphicsVR.java b/libs/processing-vr/src/main/java/processing/vr/VRGraphics.java similarity index 73% rename from mode/libraries/vr/src/processing/vr/PGraphicsVR.java rename to libs/processing-vr/src/main/java/processing/vr/VRGraphics.java index 73b2ce12f..f39560457 100644 --- a/mode/libraries/vr/src/processing/vr/PGraphicsVR.java +++ b/libs/processing-vr/src/main/java/processing/vr/VRGraphics.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -30,28 +30,31 @@ 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 PGraphicsVR extends PGraphics3D { +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; - public float forwardX, forwardY, forwardZ; - public float rightX, rightY, rightZ; - public float upX, upY, upZ; - private float[] forwardVector; - private float[] rightVector; - private float[] upVector; + protected float[] forwardVector; + protected float[] rightVector; + protected float[] upVector; + private Viewport eyeViewport; private float[] eyeView; private float[] eyePerspective; - private PMatrix3D eyeMatrix; + @Override protected PGL createPGL(PGraphicsOpenGL pg) { @@ -59,80 +62,6 @@ protected PGL createPGL(PGraphicsOpenGL pg) { } - @Override - public PMatrix3D getEyeMatrix() { - PMatrix3D mat = new PMatrix3D(); - float sign = cameraUp ? +1 : -1; - mat.set(rightX, sign * upX, forwardX, cameraX, - rightY, sign * upY, forwardY, cameraY, - rightZ, sign * upZ, forwardZ, cameraZ, - 0, 0, 0, 1); - return mat; - } - - - @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 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; - } - - - @Override - public void eye() { - eyeMatrix = getEyeMatrix(eyeMatrix); - - // Erasing any previous transformation in modelview - modelview.set(camera); - modelview.apply(eyeMatrix); - - // 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; - - // Applying the inverse of the previous transformations in the opposite order - // to compute the modelview inverse - modelviewInv.set(eyeMatrix); - modelviewInv.preApply(cameraInv); - - updateProjmodelview(); - } - - @Override public void beginDraw() { super.beginDraw(); @@ -144,13 +73,13 @@ public void beginDraw() { 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 cannnot be modified in VR mode"); + PGraphics.showWarning("The camera cannot be set in VR"); } @Override public void perspective(float fov, float aspect, float zNear, float zFar) { - PGraphics.showWarning("Perspective cannnot be modified in VR mode"); + PGraphics.showWarning("Perspective cannot be set in VR"); } diff --git a/mode/libraries/vr/src/processing/vr/PGraphicsVRMono.java b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsMono.java similarity index 87% rename from mode/libraries/vr/src/processing/vr/PGraphicsVRMono.java rename to libs/processing-vr/src/main/java/processing/vr/VRGraphicsMono.java index b53804a88..6a31a2b57 100644 --- a/mode/libraries/vr/src/processing/vr/PGraphicsVRMono.java +++ b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsMono.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -26,10 +26,10 @@ import processing.android.AppComponent; import processing.core.PSurface; -public class PGraphicsVRMono extends PGraphicsVR { +public class VRGraphicsMono extends VRGraphics { @Override public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore if (reset) pgl.resetFBOLayer(); - return new PSurfaceVR(this, component, holder, false); + return new VRSurface(this, component, holder, false); } } \ No newline at end of file diff --git a/mode/libraries/vr/src/processing/vr/PGraphicsVRStereo.java b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsStereo.java similarity index 87% rename from mode/libraries/vr/src/processing/vr/PGraphicsVRStereo.java rename to libs/processing-vr/src/main/java/processing/vr/VRGraphicsStereo.java index 753c8e73b..681de9e1b 100644 --- a/mode/libraries/vr/src/processing/vr/PGraphicsVRStereo.java +++ b/libs/processing-vr/src/main/java/processing/vr/VRGraphicsStereo.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -26,10 +26,10 @@ import processing.android.AppComponent; import processing.core.PSurface; -public class PGraphicsVRStereo extends PGraphicsVR { +public class VRGraphicsStereo extends VRGraphics { @Override public PSurface createSurface(AppComponent component, SurfaceHolder holder, boolean reset) { // ignore if (reset) pgl.resetFBOLayer(); - return new PSurfaceVR(this, component, holder, true); + return new VRSurface(this, component, holder, true); } } diff --git a/mode/libraries/vr/src/processing/vr/PSurfaceVR.java b/libs/processing-vr/src/main/java/processing/vr/VRSurface.java similarity index 97% rename from mode/libraries/vr/src/processing/vr/PSurfaceVR.java rename to libs/processing-vr/src/main/java/processing/vr/VRSurface.java index 6526edc14..dac07ee27 100644 --- a/mode/libraries/vr/src/processing/vr/PSurfaceVR.java +++ b/libs/processing-vr/src/main/java/processing/vr/VRSurface.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2016 The Processing Foundation + 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 @@ -50,16 +50,16 @@ import android.view.Window; import android.view.WindowManager; -public class PSurfaceVR extends PSurfaceGLES { +public class VRSurface extends PSurfaceGLES { protected SurfaceViewVR vrView; - protected PGraphicsVR pvr; + protected VRGraphics pvr; protected GvrActivity vrActivity; protected AndroidVRStereoRenderer renderer; private boolean needCalculate; - public PSurfaceVR(PGraphics graphics, AppComponent component, SurfaceHolder holder, boolean vr) { + public VRSurface(PGraphics graphics, AppComponent component, SurfaceHolder holder, boolean vr) { this.sketch = graphics.parent; this.graphics = graphics; this.component = component; @@ -67,7 +67,7 @@ public PSurfaceVR(PGraphics graphics, AppComponent component, SurfaceHolder hold vrActivity = (GvrActivity)component; this.activity = vrActivity; - pvr = (PGraphicsVR)graphics; + pvr = (VRGraphics)graphics; vrView = new SurfaceViewVR(vrActivity); diff --git a/mode/.classpath b/mode/.classpath deleted file mode 100644 index 1260858d6..000000000 --- a/mode/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/mode/.project b/mode/.project deleted file mode 100644 index 9b2d77470..000000000 --- a/mode/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - android-mode - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/mode/build.gradle b/mode/build.gradle deleted file mode 100644 index 224c87f6c..000000000 --- a/mode/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -import java.nio.file.Files -import org.zeroturnaround.zip.ZipUtil -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - - -// Extend compile to copy the jars from gradle-tooling and slf4j: -// https://stackoverflow.com/a/43602463 -configurations { - compile.extendsFrom compileAndCopy -} - -dependencies { - compile group: "org.processing", name: "core", version: "${processingVersion}" - compile group: "org.processing", name: "pde", version: "${processingVersion}" - compile group: "org.processing", name: "java-mode", version: "${processingVersion}" - - compileAndCopy "org.gradle:gradle-tooling-api:${toolingVersion}" - compileAndCopy "org.slf4j:slf4j-api:${slf4jVersion}" - compileAndCopy "org.slf4j:slf4j-simple:${slf4jVersion}" -} - -// This task copies the gradle tooling jar into the mode folder -task copyToLib(type: Copy) { - from configurations.compileAndCopy.files - into "mode" -} -build.dependsOn(copyToLib) - -sourceSets { - main { - java { - srcDirs = ["src/"] - } - } -} - -task permissions(type: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" -} - -task wrapper(type: Wrapper) { - gradleVersion = "${gradlewVersion}" //version required for gradle wrapper -} -wrapper.doLast { - File 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")) - ZipUtil.pack(file("mode/gradlew"), new File("mode/gradlew.zip")); - delete "mode/gradlew" -} - -clean.doFirst { - delete fileTree("mode") { - include "**/*.jar" - } -} - -build.doLast { - Files.copy(file("$buildDir/libs/mode.jar").toPath(), - file("mode/AndroidMode.jar").toPath(), REPLACE_EXISTING); -} diff --git a/mode/icons/icon-144.png b/mode/icons/icon-144.png deleted file mode 100644 index ebc41bc8d..000000000 Binary files a/mode/icons/icon-144.png and /dev/null differ diff --git a/mode/icons/icon-192.png b/mode/icons/icon-192.png deleted file mode 100644 index 40932779b..000000000 Binary files a/mode/icons/icon-192.png and /dev/null differ diff --git a/mode/icons/icon-36.png b/mode/icons/icon-36.png deleted file mode 100644 index 4412d4f69..000000000 Binary files a/mode/icons/icon-36.png and /dev/null differ diff --git a/mode/icons/icon-48.png b/mode/icons/icon-48.png deleted file mode 100644 index 0359b7c88..000000000 Binary files a/mode/icons/icon-48.png and /dev/null differ diff --git a/mode/icons/icon-72.png b/mode/icons/icon-72.png deleted file mode 100644 index 8f19510ff..000000000 Binary files a/mode/icons/icon-72.png and /dev/null differ diff --git a/mode/icons/icon-96.png b/mode/icons/icon-96.png deleted file mode 100644 index c9016cf0c..000000000 Binary files a/mode/icons/icon-96.png and /dev/null differ diff --git a/mode/icons/preview_circular.png b/mode/icons/preview_circular.png deleted file mode 100644 index 940afd811..000000000 Binary files a/mode/icons/preview_circular.png and /dev/null differ diff --git a/mode/icons/preview_rectangular.png b/mode/icons/preview_rectangular.png deleted file mode 100644 index 277a466e4..000000000 Binary files a/mode/icons/preview_rectangular.png and /dev/null differ diff --git a/mode/libraries/vr/build.gradle b/mode/libraries/vr/build.gradle deleted file mode 100644 index b364cadcb..000000000 --- a/mode/libraries/vr/build.gradle +++ /dev/null @@ -1,174 +0,0 @@ -import com.android.build.gradle.internal.dependency.ExtractAarTransform -import com.android.build.gradle.internal.dependency.AarTransform -import com.android.build.gradle.internal.publishing.AndroidArtifacts -import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType -import com.google.common.collect.ImmutableList -import org.gradle.api.artifacts.transform.ArtifactTransform -import org.gradle.api.artifacts.type.ArtifactTypeDefinition -import java.util.regex.Pattern - -import static org.gradle.api.internal.artifacts.ArtifactAttributes.ARTIFACT_FORMAT - -import java.nio.file.Files -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - -apply plugin: 'maven' - -/** - * Custom aar configuration needed to use aar files as dependencies in a pure java - * library project, lifted from the following repo: - * https://github.com/nekocode/Gradle-Import-Aar - */ -configurations { - aar { - attributes { - attribute(ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE) - } - - // Add the aar inner jars to the compileClasspath - sourceSets.main.compileClasspath += it - - // Put our custom dependencies onto IDEA's PROVIDED scope - apply plugin: "idea" - idea.module.scopes.PROVIDED.plus += [it] - } -} - -dependencies { - // Transforamtions to extract the classes.jar in the aar package - def explodedAarType = ArtifactType.EXPLODED_AAR.getType() - registerTransform { - from.attribute(ARTIFACT_FORMAT, AndroidArtifacts.TYPE_AAR) - to.attribute(ARTIFACT_FORMAT, explodedAarType) - artifactTransform(ExtractAarTransform) - } - - registerTransform { - from.attribute(ARTIFACT_FORMAT, explodedAarType) - to.attribute(ARTIFACT_FORMAT, "classes.jar") - artifactTransform(AarTransform) { params(ArtifactType.JAR) } - } - - registerTransform { - from.attribute(ARTIFACT_FORMAT, "classes.jar") - to.attribute(ARTIFACT_FORMAT, ArtifactTypeDefinition.JAR_TYPE) - artifactTransform(ClassesJarArtifactTransform) - } - - compileOnly name: "android" - - compileOnly "org.p5android:processing-core:${modeVersion}" - - aar "com.google.vr:sdk-audio:${gvrVersion}" - aar "com.google.vr:sdk-base:${gvrVersion}" -} - -/** - * An ArtifactTransform for renaming the classes.jar - */ -class ClassesJarArtifactTransform extends ArtifactTransform { - @Override - List transform(File file) { - final String[] names = file.getPath().split(Pattern.quote(File.separator)) - final String aarName = names[names.length - 4].replace(".aar", "") - final File renamedJar = new File(getOutputDirectory(), aarName + ".jar") - renamedJar << file.bytes - File libraryFolder = new File(System.getProperty("user.dir"), "mode/libraries/vr/library") - libraryFolder.mkdirs(); - final File libraryJar = new File(libraryFolder, aarName + ".jar") - Files.copy(renamedJar.toPath(), libraryJar.toPath(), REPLACE_EXISTING); - return ImmutableList.of(renamedJar) - } -} - -task createPom { - // The compile configuration should be replaced by implementation eventually: - // https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration#new_configurations - pom { - project { - groupId "org.p5android" - artifactId "processing-vr" - version "${vrLibVersion}" - packaging "jar" - licenses { - license { - name "GNU Lesser General Public License, version 2.1" - url "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt" - distribution "repo" - } - } - dependencies { - dependency { - groupId "org.p5android" - artifactId "processing-core" - version "${modeVersion}" - scope "compile" - } - - dependency { - groupId "com.google.vr" - artifactId "sdk-base" - version "${gvrVersion}" - scope "compile" - } - dependency { - groupId "com.google.vr" - artifactId "sdk-audio" - version "${gvrVersion}" - scope "compile" - } - } - } - }.writeTo("dist/processing-vr-${vrLibVersion}.pom") -} - -sourceSets { - main { - java { - srcDirs = ["src/"] - } - } -} - -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = "sources" - from sourceSets.main.allSource -} - -// Does not work because of Processing-specific tags in source code, such as @webref -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = "javadoc" - from javadoc.destinationDir -} - -artifacts { -// archives javadocJar - archives sourcesJar -} - -jar.doLast { task -> - ant.checksum file: task.archivePath -} - -clean.doFirst { - delete "dist" - delete "library/vr.jar" -} - -build.doLast { - // // Copying vr jar to library folder - File vrJar = file("library/vr.jar") - vrJar.mkdirs(); - Files.copy(file("$buildDir/libs/vr.jar").toPath(), - vrJar.toPath(), REPLACE_EXISTING); - - // // Copying the files for release on JCentral - File distFolder = file("dist"); - distFolder.mkdirs(); - Files.copy(file("$buildDir/libs/vr.jar").toPath(), - file("dist/processing-vr-${vrLibVersion}.jar").toPath(), REPLACE_EXISTING); - Files.copy(file("$buildDir/libs/vr-sources.jar").toPath(), - file("dist/processing-vr-${vrLibVersion}-sources.jar").toPath(), REPLACE_EXISTING); - Files.copy(file("$buildDir/libs/vr.jar.MD5").toPath(), - file("dist/processing-vr-${vrLibVersion}.jar.md5").toPath(), REPLACE_EXISTING); -} \ No newline at end of file diff --git a/mode/libraries/vr/examples/Cube/AndroidManifest.xml b/mode/libraries/vr/examples/Cube/AndroidManifest.xml deleted file mode 100644 index 9e67d8615..000000000 --- a/mode/libraries/vr/examples/Cube/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/mode/libraries/vr/examples/Cube/code/sketch.properties b/mode/libraries/vr/examples/Cube/code/sketch.properties deleted file mode 100644 index ab2c3cf6c..000000000 --- a/mode/libraries/vr/examples/Cube/code/sketch.properties +++ /dev/null @@ -1 +0,0 @@ -component=vr diff --git a/mode/libraries/vr/examples/Mono/code/sketch.properties b/mode/libraries/vr/examples/Mono/code/sketch.properties deleted file mode 100644 index ab2c3cf6c..000000000 --- a/mode/libraries/vr/examples/Mono/code/sketch.properties +++ /dev/null @@ -1 +0,0 @@ -component=vr diff --git a/mode/libraries/vr/examples/drawAim/AndroidManifest.xml b/mode/libraries/vr/examples/drawAim/AndroidManifest.xml deleted file mode 100644 index 9e67d8615..000000000 --- a/mode/libraries/vr/examples/drawAim/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/mode/mode.properties b/mode/mode.properties deleted file mode 100644 index c98c49e25..000000000 --- a/mode/mode.properties +++ /dev/null @@ -1,10 +0,0 @@ -name = Android Mode -authorList = [The Processing Foundation](https://processingfoundation.org/) -url = http://android.processing.org -sentence = Create projects with Processing for Android devices -paragraph = This version of the Android Mode is for Processing 3.1+ -imports=processing.mode.java.JavaMode -version = 271 -prettyVersion = 4.0.4 -minRevision = 249 -maxRevision = 0 diff --git a/mode/mode/gradlew.zip b/mode/mode/gradlew.zip deleted file mode 100644 index 4ea799d16..000000000 Binary files a/mode/mode/gradlew.zip and /dev/null differ diff --git a/mode/scripts/permissions.py b/mode/scripts/permissions.py deleted file mode 100644 index fc9eed5a2..000000000 --- a/mode/scripts/permissions.py +++ /dev/null @@ -1,76 +0,0 @@ -import sys, re - -from urllib2 import urlopen -from BeautifulSoup import BeautifulSoup - -def getSoup(url): - print 'Opening', url, '...' - page = urlopen(url) - soup = BeautifulSoup(page) - return soup - -def parseAll(): - soup = getSoup("https://developer.android.com/reference/android/Manifest.permission.html") - print ' parsing...' - table = soup.find('table', { 'id': 'constants', 'class' : 'responsive constants' }) - entries = table.findAll('tr') - strList = '' - for entry in entries: - if not entry or not entry.attrs: continue - if 'absent' in entry.attrs[0][1]: continue - info = entry.find('td', {'width':'100%'}) - if info: - name = info.find('code').find('a').contents[0] - pieces = [] - for piece in info.find('p').contents: - piece_str = re.sub('\s+', ' ', str(piece)).strip() - if '' in piece_str: - piece_str = piece.find('a').contents[0].strip(); - pieces += [piece_str] - if name and pieces: - desc = ' '.join(pieces).strip().replace('"', '\\"') - strList += (',' if strList else '') + '\n "' + name + '", "' + desc + '"' - strList = 'static final String[] listing = {' + strList + '\n };\n' - return strList - -def replaceAll(source, strList): - print ' replacing...' - idx0 = source.find('static final String[] listing = {') - idx1 = source[idx0:].find(' };') - return source[:idx0] + strList + source[idx0+idx1+5:] - -def parseDanger(): - soup = getSoup("https://developer.android.com/guide/topics/security/permissions.html") - print ' parsing...' - table = soup.find('table') - entries = table.findAll('tr') - strList = '' - for entry in entries: - items = entry.findAll('li') - for item in items: - name = item.find('code').find('a').contents[0] - strList += (',' if strList else '') + '\n "' + name + '"' - strList = 'static final String[] dangerous = {' + strList + '\n };\n' - return strList - -def replaceDanger(source, strList): - print ' replacing...' - idx0 = source.find('static final String[] dangerous = {') - idx1 = source[idx0:].find(' };') - return source[:idx0] + strList + source[idx0+idx1+5:] - -javaFile = '../src/processing/mode/android/Permissions.java' -print 'Reading Permissions.java...' -with open(javaFile, 'r') as f: - source = f.read() - -allList = parseAll() -source = replaceAll(source, allList) - -dangerList = parseDanger() -source = replaceDanger(source, dangerList) - -print 'Writing Permissions.java...' -with open(javaFile, 'w') as f: - f.write(source) -print 'Done.' \ No newline at end of file diff --git a/mode/src/processing/mode/android/AndroidPreprocessor.java b/mode/src/processing/mode/android/AndroidPreprocessor.java deleted file mode 100644 index af4a73ce2..000000000 --- a/mode/src/processing/mode/android/AndroidPreprocessor.java +++ /dev/null @@ -1,321 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012-17 The Processing Foundation - Copyright (c) 2009-12 Ben Fry and Casey Reas - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package processing.mode.android; - -import java.io.IOException; -import java.io.PrintWriter; -//import java.io.Writer; -import java.util.List; -import processing.app.*; -import processing.core.PApplet; -import processing.mode.java.preproc.PdePreprocessor; -import processing.mode.java.preproc.SurfaceInfo; - - -/** - * Processing preprocessor, supporting the Android specifics. - */ -public class AndroidPreprocessor extends PdePreprocessor { - protected Sketch sketch; - protected String packageName; - - protected String smoothStatement; - protected String sketchQuality; - - protected String kindStatement; - protected String sketchKind; - - - public static final String SMOOTH_REGEX = - "(?:^|\\s|;)smooth\\s*\\(\\s*([^\\s,]+)\\s*\\)\\s*\\;"; - - public AndroidPreprocessor(final String sketchName) { - super(sketchName); - } - - public AndroidPreprocessor(final Sketch sketch, - final String packageName) throws IOException { - super(sketch.getName()); - this.sketch = sketch; - this.packageName = packageName; - } - - - public SurfaceInfo initSketchSize(String code) throws SketchException { - SurfaceInfo surfaceInfo = parseSketchSize(code, true); - if (surfaceInfo == null) { - System.err.println("More about the size() command on Android can be"); - System.err.println("found here: http://wiki.processing.org/w/Android"); - throw new SketchException("Could not parse the size() command."); - } - return surfaceInfo; - } - - - public String[] initSketchSmooth(String code) throws SketchException { - String[] info = parseSketchSmooth(code, true); - if (info == null) { - System.err.println("More about the smooth() command on Android can be"); - System.err.println("found here: http://wiki.processing.org/w/Android"); - throw new SketchException("Could not parse the smooth() command."); - } - smoothStatement = info[0]; - sketchQuality = info[1]; - return info; - } - - - static public String[] parseSketchSmooth(String code, boolean fussy) { - String[] matches = PApplet.match(scrubComments(code), SMOOTH_REGEX); - - if (matches != null) { - boolean badSmooth = false; - - if (PApplet.parseInt(matches[1], -1) == -1) { - badSmooth = true; - } - - if (badSmooth && fussy) { - // found a reference to smooth, but it didn't seem to contain numbers - final String message = - "The smooth level of this applet could not automatically\n" + - "be determined from your code. Use only a numeric\n" + - "value (not variables) for the smooth() command.\n" + - "See the smooth() reference for an explanation."; - Messages.showWarning("Could not find smooth level", message, null); - return null; - } - - return matches; - } - return new String[] { null, null }; // not an error, just empty - } - - - @Override - protected int writeImports(final PrintWriter out, - final List programImports, - final List codeFolderImports) { - out.println("package " + packageName + ";"); - out.println(); - int count = 2; - count += super.writeImports(out, programImports, codeFolderImports); -// count += writeImportList(out, getAndroidImports()); - return count; - } - - - @Override - protected void writeFooter(PrintWriter out, String className) { - SurfaceInfo info = null; - try { - info = initSketchSize(sketch.getMainProgram()); - } catch (SketchException e) { - e.printStackTrace(); - } - - if (info == null) { - // Cannot get size info, just use parent's implementation. - super.writeFooter(out, className); - } else { - // Same as in the parent, but without writing the main() method, which is - // not needed in Android. - - if (mode == Mode.STATIC) { - // close off setup() definition - out.println(indent + indent + "noLoop();"); - out.println(indent + "}"); - out.println(); - } - - if ((mode == Mode.STATIC) || (mode == Mode.ACTIVE)) { - // doesn't remove the original size() method, but calling size() - // again in setup() is harmless. - if (!hasMethod("settings") && info.hasSettings()) { - out.println(indent + "public void settings() { " + info.getSettings() + " }"); - } - - // close off the class definition - out.println("}"); - } - } - } - - -//////////////////////////////////////////////////////////////////////////////// -// Assorted commented out code -// - - // As of revision 0215 (2.0b7-ish), the default imports are now identical - // between desktop and Android (to avoid unintended incompatibilities). - /* - @Override - public String[] getCoreImports() { - return new String[] { - "processing.core.*", - "processing.data.*", - "processing.event.*", - "processing.opengl.*" - }; - } - - - @Override - public String[] getDefaultImports() { - final String prefsLine = Preferences.get("android.preproc.imports"); - if (prefsLine != null) { - return PApplet.splitTokens(prefsLine, ", "); - } - - // The initial values are stored in here for the day when Android - // is broken out as a separate mode. - - // In the future, this may include standard classes for phone or - // accelerometer access within the Android APIs. This is currently living - // in code rather than preferences.txt because Android mode needs to - // maintain its independence from the rest of processing.app. - final String[] androidImports = new String[] { -// "android.view.MotionEvent", "android.view.KeyEvent", -// "android.graphics.Bitmap", //"java.awt.Image", - "java.io.*", // for BufferedReader, InputStream, etc - //"java.net.*", "java.text.*", // leaving otu for now - "java.util.*" // for ArrayList and friends - //"java.util.zip.*", "java.util.regex.*" // not necessary w/ newer i/o - }; - - Preferences.set("android.preproc.imports", - PApplet.join(androidImports, ",")); - - return androidImports; - } - */ - - // No need for it now - /* - public String[] getDefaultImports() { -// String[] defs = super.getDefaultImports(); -// return defs; - return new String[] { - "java.util.HashMap", - "java.util.ArrayList", - "java.io.File", - "java.io.BufferedReader", - "java.io.PrintWriter", - "java.io.InputStream", - "java.io.OutputStream", - "java.io.IOException", - "android.app.Activity", - "android.app.Fragment" - }; - } - - - public String[] getAndroidImports() { - return new String[] { - "processing.android.ServiceEngine" - }; - } - */ - - /* - protected boolean parseSketchSize() { - // This matches against any uses of the size() function, whether numbers - // or variables or whatever. This way, no warning is shown if size() isn't - // actually used in the applet, which is the case especially for anyone - // who is cutting/pasting from the reference. - - String scrubbed = processing.mode.java.JavaBuild.scrubComments(sketch.getCode(0).getProgram()); - String[] matches = PApplet.match(scrubbed, processing.mode.java.JavaBuild.SIZE_REGEX); -// PApplet.println("matches: " + Sketch.SIZE_REGEX); -// PApplet.println(matches); - - if (matches != null) { - boolean badSize = false; - - if (matches[1].equals("screenWidth") || - matches[1].equals("screenHeight") || - matches[2].equals("screenWidth") || - matches[2].equals("screenHeight")) { - final String message = - "The screenWidth and screenHeight variables are named\n" + - "displayWidth and displayHeight in this release of Processing."; - Base.showWarning("Time for a quick update", message, null); - return false; - } - - if (!matches[1].equals("displayWidth") && - !matches[1].equals("displayHeight") && - PApplet.parseInt(matches[1], -1) == -1) { - badSize = true; - } - if (!matches[2].equals("displayWidth") && - !matches[2].equals("displayHeight") && - PApplet.parseInt(matches[2], -1) == -1) { - badSize = true; - } - - if (badSize) { - // found a reference to size, but it didn't seem to contain numbers - final String message = - "The size of this applet could not automatically be determined\n" + - "from your code. Use only numeric values (not variables) for the\n" + - "size() command. See the size() reference for more information."; - Base.showWarning("Could not find sketch size", message, null); - System.out.println("More about the size() command on Android can be"); - System.out.println("found here: http://wiki.processing.org/w/Android"); - return false; - } - -// PApplet.println(matches); - sizeStatement = matches[0]; // the full method to be removed from the source - sketchWidth = matches[1]; - sketchHeight = matches[2]; - sketchRenderer = matches[3].trim(); - if (sketchRenderer.length() == 0) { - sketchRenderer = null; - } - } else { - sizeStatement = null; - sketchWidth = null; - sketchHeight = null; - sketchRenderer = null; - } - return true; - } - */ - - - /* - public PreprocessorResult write(Writer out, String program, String[] codeFolderPackages) - throws SketchException, RecognitionException, TokenStreamException { - if (sizeStatement != null) { - int start = program.indexOf(sizeStatement); - program = program.substring(0, start) + - program.substring(start + sizeStatement.length()); - } - // the OpenGL package is back in 2.0a5 - //program = program.replaceAll("import\\s+processing\\.opengl\\.\\S+;", ""); - return super.write(out, program, codeFolderPackages); - } - */ -} \ No newline at end of file diff --git a/mode/src/processing/mode/android/AndroidToolbar.java b/mode/src/processing/mode/android/AndroidToolbar.java deleted file mode 100644 index b2c772b60..000000000 --- a/mode/src/processing/mode/android/AndroidToolbar.java +++ /dev/null @@ -1,177 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012-16 The Processing Foundation - Copyright (c) 2011-12 Ben Fry and Casey Reas - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -package processing.mode.android; - -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.util.ArrayList; -import java.util.List; - -import processing.app.Base; -import processing.app.ui.Editor; -import processing.app.ui.EditorButton; -import processing.app.ui.EditorToolbar; -import processing.app.Language; - - -@SuppressWarnings("serial") -public class AndroidToolbar extends EditorToolbar { - static protected final int RUN = 0; - static protected final int STOP = 1; - - static protected final int NEW = 2; - static protected final int OPEN = 3; - static protected final int SAVE = 4; - static protected final int EXPORT = 5; - - - public AndroidToolbar(Editor editor, Base base) { - super(editor); - } - - - // TODO: - // Buttons are initialized in createButtons, see code of EditorToolbar.rebuild() -// public void init() { -// Image[][] images = loadImages(); -// for (int i = 0; i < 6; i++) { -// addButton(getTitle(i, false), getTitle(i, true), images[i], i == NEW); -// } -// } - - - static public String getTitle(int index, boolean shift) { - switch (index) { - case RUN: return !shift ? "Run on Device" : "Run in Emulator"; - case STOP: return "Stop"; - case NEW: return "New"; - case OPEN: return "Open"; - case SAVE: return "Save"; - case EXPORT: return !shift ? "Export Signed Package" : "Export Android Project"; - } - return null; - } - -/* - public void handlePressed(MouseEvent e, int sel) { - boolean shift = e.isShiftDown(); - AndroidEditor aeditor = (AndroidEditor) editor; - - switch (sel) { - case RUN: - if (!shift) { - aeditor.handleRunDevice(); - } else { - aeditor.handleRunEmulator(); - } - break; - - case STOP: - aeditor.handleStop(); - break; - - case OPEN: - // TODO I think we need a longer chain of accessors here. - JPopupMenu popup = editor.getMode().getToolbarMenu().getPopupMenu(); - popup.show(this, e.getX(), e.getY()); - break; - - case NEW: -// if (shift) { - base.handleNew(); -// } else { -// base.handleNewReplace(); -// } - break; - - case SAVE: - aeditor.handleSave(false); - break; - - case EXPORT: - if (!shift) { - aeditor.handleExportPackage(); - } else { - aeditor.handleExportProject(); - } - break; - } - } -*/ - - @Override - public List createButtons() { - ArrayList toReturn = new ArrayList(); - runButton = new EditorButton(this, - "/lib/toolbar/run", - "Run on device", - "Run on emulator") { - @Override - public void actionPerformed(ActionEvent e) { - handleRun(e.getModifiers()); - } - }; - toReturn.add(runButton); - - stopButton = new EditorButton(this, - "/lib/toolbar/stop", - Language.text("toolbar.stop")) { - @Override - public void actionPerformed(ActionEvent e) { - handleStop(); - } - }; - toReturn.add(stopButton); - return toReturn; - } - - @Override - public void handleRun(int modifiers) { - AndroidEditor aEditor = (AndroidEditor) editor; - boolean shift = (modifiers & InputEvent.SHIFT_MASK) != 0; - if (!shift) { - aEditor.handleRunDevice(); - } else { - aEditor.handleRunEmulator(); - } - } - - - @Override - public void handleStop() { - // TODO Auto-generated method stub - AndroidEditor aEditor = (AndroidEditor) editor; - aEditor.handleStop(); - } - - - public void activateExport() { - // TODO added to match the new API in EditorToolbar (activateRun, etc). - } - - - public void deactivateExport() { - // TODO added to match the new API in EditorToolbar (activateRun, etc). - } -} \ No newline at end of file diff --git a/mode/src/processing/mode/android/JarSigner.java b/mode/src/processing/mode/android/JarSigner.java deleted file mode 100644 index c50022b69..000000000 --- a/mode/src/processing/mode/android/JarSigner.java +++ /dev/null @@ -1,290 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2014-16 The Processing Foundation - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.android; - -import java.io.*; - -import java.security.*; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.Base64; -import sun.security.pkcs.SignerInfo; -import sun.security.x509.AlgorithmId; -import sun.security.x509.X500Name; -import sun.security.pkcs.PKCS7; -import sun.security.pkcs.ContentInfo; - -/** - * Created by ibziy_000 on 17.08.2014. - */ -public class JarSigner { - private static final String DIGEST_ALGORITHM = "SHA1"; - private static final String DIGEST_ATTR = "SHA1-Digest"; - private static final String DIGEST_MANIFEST_ATTR = "SHA1-Digest-Manifest"; - private static SignatureOutputStream certFileContents = null; - private static byte[] buffer; - - public static void signJar(File jarToSign, File outputJar, String alias, - String keypass, String keystore, String storepass) - throws GeneralSecurityException, IOException, NoSuchAlgorithmException { - - PrivateKey privateKey = null; - X509Certificate x509Cert = null; - Manifest manifest = null; - - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - FileInputStream fis = new FileInputStream(keystore); - keyStore.load(fis, storepass.toCharArray()); - fis.close(); - - KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( - alias, new KeyStore.PasswordProtection(keypass.toCharArray())); - if (entry != null) { - privateKey = entry.getPrivateKey(); - x509Cert = (X509Certificate) entry.getCertificate(); - } else { - throw new KeyStoreException("Couldn't get key"); - } - - JarOutputStream signedJar = new JarOutputStream(new FileOutputStream(outputJar, false)); - signedJar.setLevel(9); - if (privateKey != null && x509Cert != null) { - manifest = new Manifest(); - Attributes main = manifest.getMainAttributes(); - main.putValue("Manifest-Version", "1.0"); - main.putValue("Created-By", "1.0 (Android)"); - } - - writeZip(new FileInputStream(jarToSign), signedJar, manifest); - - closeJar(signedJar, manifest, privateKey, x509Cert); - } - - private static void writeZip(InputStream input, JarOutputStream output, Manifest manifest) - throws IOException, NoSuchAlgorithmException { - Base64.Encoder base64Encoder = Base64.getEncoder(); - MessageDigest messageDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); - buffer = new byte[4096]; - - ZipInputStream zis = new ZipInputStream(input); - - try { - // loop on the entries of the intermediary package and put them in the final package. - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { - String name = entry.getName(); - - // do not take directories or anything inside a potential META-INF folder. - if (entry.isDirectory() || name.startsWith("META-INF/")) { - continue; - } - - JarEntry newEntry; - - // Preserve the STORED method of the input entry. - if (entry.getMethod() == JarEntry.STORED) { - newEntry = new JarEntry(entry); - } else { - // Create a new entry so that the compressed len is recomputed. - newEntry = new JarEntry(name); - } - - writeEntry(output, zis, newEntry, messageDigest, manifest, base64Encoder); - - zis.closeEntry(); - } - } finally { - zis.close(); - } - } - - private static void writeEntry(JarOutputStream output, InputStream input, JarEntry entry, - MessageDigest digest, Manifest manifest, Base64.Encoder encoder) throws IOException { - output.putNextEntry(entry); - - // Write input stream to the jar output. - int count; - while ((count = input.read(buffer)) != -1) { - output.write(buffer, 0, count); - - if (digest != null) digest.update(buffer, 0, count); - } - - output.closeEntry(); - - if (manifest != null) { - Attributes attr = manifest.getAttributes(entry.getName()); - if (attr == null) { - attr = new Attributes(); - manifest.getEntries().put(entry.getName(), attr); - } - attr.putValue(DIGEST_ATTR, encoder.encodeToString(digest.digest())); - } - } - - private static void closeJar(JarOutputStream jar, Manifest manifest, - PrivateKey key, X509Certificate cert) - throws IOException, GeneralSecurityException { - if (manifest != null) { - // write the manifest to the jar file - jar.putNextEntry(new JarEntry(JarFile.MANIFEST_NAME)); - manifest.write(jar); - - // CERT.SF - Signature signature = Signature.getInstance("SHA1with" + key.getAlgorithm()); - signature.initSign(key); - jar.putNextEntry(new JarEntry("META-INF/CERT.SF")); - //Caching the SignatureOutputStream object for future use by the signature provider extensions. - certFileContents = new SignatureOutputStream(jar, signature); - writeSignatureFile(certFileContents, manifest); - - // CERT.* - jar.putNextEntry(new JarEntry("META-INF/CERT." + key.getAlgorithm())); - writeSignature(jar, signature, cert, key); - } - - jar.close(); - } - - // Writes a .SF file with a digest to the manifest. - private static void writeSignatureFile(SignatureOutputStream out, Manifest manifest) - throws IOException, GeneralSecurityException { - Manifest sf = new Manifest(); - Attributes main = sf.getMainAttributes(); - main.putValue("Signature-Version", "1.0"); - main.putValue("Created-By", "1.0 (Android)"); - - Base64.Encoder base64 = Base64.getEncoder(); - MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM); - PrintStream print = new PrintStream( - new DigestOutputStream(new ByteArrayOutputStream(), md), - true, "UTF-8"); - - // Digest of the entire manifest - manifest.write(print); - print.flush(); - main.putValue(DIGEST_MANIFEST_ATTR, base64.encodeToString(md.digest())); - - Map entries = manifest.getEntries(); - for (Map.Entry entry : entries.entrySet()) { - // Digest of the manifest stanza for this entry. - print.print("Name: " + entry.getKey() + "\r\n"); - for (Map.Entry att : entry.getValue().entrySet()) { - print.print(att.getKey() + ": " + att.getValue() + "\r\n"); - } - print.print("\r\n"); - print.flush(); - - Attributes sfAttr = new Attributes(); - sfAttr.putValue(DIGEST_ATTR, base64.encodeToString(md.digest())); - sf.getEntries().put(entry.getKey(), sfAttr); - } - - sf.write(out); - - // A bug in the java.util.jar implementation of Android platforms - // up to version 1.6 will cause a spurious IOException to be thrown - // if the length of the signature file is a multiple of 1024 bytes. - // As a workaround, add an extra CRLF in this case. - if ((out.size() % 1024) == 0) { - out.write('\r'); - out.write('\n'); - } - } - - private static void writeSignature(JarOutputStream outputJar, - Signature signature, X509Certificate publicKey, PrivateKey privateKey) - throws IOException, GeneralSecurityException{ - writeSignatureBlock(outputJar, signature, publicKey, privateKey); - } - - // Write the certificate file with a digital signature. - private static void writeSignatureBlock(JarOutputStream outputJar, - Signature signature, X509Certificate publicKey, PrivateKey privateKey) - throws IOException, GeneralSecurityException { - - SignerInfo signerInfo = new SignerInfo( - new X500Name(publicKey.getIssuerX500Principal().getName()), - publicKey.getSerialNumber(), - AlgorithmId.get(DIGEST_ALGORITHM), - AlgorithmId.get(privateKey.getAlgorithm()), - signature.sign()); - - PKCS7 pkcs7 = new PKCS7( - new AlgorithmId[] { AlgorithmId.get(DIGEST_ALGORITHM) }, - new ContentInfo(ContentInfo.DATA_OID, null), - new X509Certificate[] { publicKey }, - new SignerInfo[] { signerInfo }); - - pkcs7.encodeSignedData(outputJar); - } - - private static class SignatureOutputStream extends FilterOutputStream { - private Signature signature; - private int count = 0; - private List contents = new ArrayList(); - - public SignatureOutputStream(OutputStream out, Signature sig) { - super(out); - signature = sig; - } - - @Override - public void write(int b) throws IOException { - try { - signature.update((byte) b); - contents.add((byte)b); - } catch (SignatureException e) { - throw new IOException("SignatureException: " + e); - } - super.write(b); - count++; - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - try { - signature.update(b, off, len); - for (byte myByte: b) { - contents.add(myByte); - } - } catch (SignatureException e) { - throw new IOException("SignatureException: " + e); - } - super.write(b, off, len); - count += len; - } - - public int size() { - return count; - } - } -} \ No newline at end of file diff --git a/mode/src/processing/mode/android/Permissions.java b/mode/src/processing/mode/android/Permissions.java deleted file mode 100644 index 68c2501de..000000000 --- a/mode/src/processing/mode/android/Permissions.java +++ /dev/null @@ -1,514 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012-16 The Processing Foundation - Copyright (c) 2010-12 Ben Fry and Casey Reas - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.android; - -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.event.*; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; - -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; - -import processing.app.Platform; -import processing.app.Sketch; -import processing.app.ui.Toolkit; - - -@SuppressWarnings("serial") -public class Permissions extends JFrame { - static final String GUIDE_URL = - "https://developer.android.com/training/articles/security-tips.html#Permissions"; - - static final int BORDER_HORIZ = Toolkit.zoom(5); - static final int BORDER_VERT = Toolkit.zoom(3); - static final int CELL_HEIGHT = Toolkit.zoom(20); - static final int BORDER = Toolkit.zoom(13); - static final int TEXT_WIDTH = Toolkit.zoom(400); - static final int TEXT_HEIGHT = Toolkit.zoom(80); - static final int URL_WIDTH = Toolkit.zoom(400); - static final int URL_HEIGHT = Toolkit.zoom(30); - static final int DESC_WIDTH = Toolkit.zoom(400); - static final int DESC_HEIGHT = Toolkit.zoom(50); - static final int GAP = Toolkit.zoom(8); - - JScrollPane permissionScroller; - JList permissionList; - JLabel descriptionLabel; - Sketch sketch; - - int appComp; - - File modeFolder; - - - public Permissions(Sketch sketch, int appComp, File modeFolder) { - super("Android Permissions Selector"); - this.appComp = appComp; - this.sketch = sketch; - this.modeFolder = modeFolder; - - permissionList = new CheckBoxList(); - permissionList.addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { - if (e.getValueIsAdjusting() == false) { - int index = permissionList.getSelectedIndex(); - if (index == -1) { - descriptionLabel.setText(""); - } else { - descriptionLabel.setText("" + description[index] + ""); - } - } - } - }); - permissionList.setFixedCellHeight(CELL_HEIGHT); - permissionList.setBorder(new EmptyBorder(BORDER_VERT, BORDER_HORIZ, - BORDER_VERT, BORDER_HORIZ)); - - DefaultListModel model = new DefaultListModel(); - permissionList.setModel(model); - for (String item : title) { - model.addElement(new JCheckBox(item)); - } - - permissionScroller = - new JScrollPane(permissionList, - ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - permissionList.setVisibleRowCount(12); - permissionList.addKeyListener(new KeyAdapter() { - public void keyTyped(KeyEvent e) { - if (e.getKeyChar() == ' ') { - int index = permissionList.getSelectedIndex(); - JCheckBox checkbox = - permissionList.getModel().getElementAt(index); - checkbox.setSelected(!checkbox.isSelected()); - permissionList.repaint(); - } - } - }); - - Container outer = getContentPane(); - Box vbox = Box.createVerticalBox(); - vbox.setBorder(new EmptyBorder(BORDER, BORDER, BORDER, BORDER)); - outer.add(vbox); - - String labelText = - "" + - "Android applications must specifically ask for permission\n" + - "to do things like connect to the internet, write a file,\n" + - "or make phone calls. When installing your application,\n" + - "users will be asked whether they want to allow such access."; - String urlText = "More about permissions can be found " + - "here."; - JLabel textarea = new JLabel(labelText); - JLabel urlarea = new JLabel(urlText); - textarea.setPreferredSize(new Dimension(TEXT_WIDTH, TEXT_HEIGHT)); - urlarea.setPreferredSize(new Dimension(URL_WIDTH, URL_HEIGHT)); - urlarea.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - Platform.openURL(GUIDE_URL); - } - }); - urlarea.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); - textarea.setAlignmentX(LEFT_ALIGNMENT); - urlarea.setAlignmentX(LEFT_ALIGNMENT); - vbox.add(textarea); - vbox.add(urlarea); - - permissionScroller.setAlignmentX(LEFT_ALIGNMENT); - vbox.add(permissionScroller); - vbox.add(Box.createVerticalStrut(GAP)); - - descriptionLabel = new JLabel(); - descriptionLabel.setPreferredSize(new Dimension(DESC_WIDTH, DESC_HEIGHT)); - descriptionLabel.setVerticalAlignment(SwingConstants.TOP); - descriptionLabel.setAlignmentX(LEFT_ALIGNMENT); - vbox.add(descriptionLabel); - vbox.add(Box.createVerticalStrut(GAP)); - - JPanel buttons = new JPanel(); - buttons.setAlignmentX(LEFT_ALIGNMENT); - JButton okButton = new JButton("OK"); - Dimension dim = new Dimension(Toolkit.getButtonWidth(), - okButton.getPreferredSize().height); - okButton.setPreferredSize(dim); - okButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - saveSelections(); - setVisible(false); - } - }); - okButton.setEnabled(true); - - JButton cancelButton = new JButton("Cancel"); - cancelButton.setPreferredSize(dim); - cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setVisible(false); - } - }); - cancelButton.setEnabled(true); - - // think different, biznatchios! - if (Platform.isMacOS()) { - buttons.add(cancelButton); - buttons.add(okButton); - } else { - buttons.add(okButton); - buttons.add(cancelButton); - } - vbox.add(buttons); - - JRootPane root = getRootPane(); - root.setDefaultButton(okButton); - ActionListener disposer = new ActionListener() { - public void actionPerformed(ActionEvent actionEvent) { - setVisible(false); - } - }; - Toolkit.registerWindowCloseKeys(root, disposer); - Toolkit.setIcon(this); - - pack(); - - Dimension screen = Toolkit.getScreenSize(); - Dimension windowSize = getSize(); - - setLocation((screen.width - windowSize.width) / 2, - (screen.height - windowSize.height) / 2); - - Manifest mf = new Manifest(sketch, appComp, modeFolder, false); - setSelections(mf.getPermissions()); - - // show the window and get to work - setVisible(true); - } - - - @SuppressWarnings("rawtypes") - protected void setSelections(String[] sel) { -// processing.core.PApplet.println("permissions are:"); -// processing.core.PApplet.println(sel); - HashMap map = new HashMap(); - for (String s : sel) { - map.put(s, new Object()); - } - DefaultListModel model = (DefaultListModel) permissionList.getModel(); - for (int i = 0; i < count; i++) { - JCheckBox box = (JCheckBox) model.get(i); -// System.out.println(map.containsKey(box.getText()) + " " + box.getText()); - box.setSelected(map.containsKey(box.getText())); - } - } - - - @SuppressWarnings("rawtypes") - protected String[] getSelections() { - ArrayList sel = new ArrayList(); - DefaultListModel model = (DefaultListModel) permissionList.getModel(); - for (int i = 0; i < count; i++) { - if (((JCheckBox) model.get(i)).isSelected()) { - sel.add(title[i]); - } - } - return sel.toArray(new String[0]); - } - - - protected void saveSelections() { - String[] sel = getSelections(); - Manifest mf = new Manifest(sketch, appComp, modeFolder, false); - mf.setPermissions(sel); - } - - - public String getMenuTitle() { - return "Android Permissions"; - } - - - // List of constants for each permission and a brief description: - // https://developer.android.com/reference/android/Manifest.permission - static final String[] listing = { - "ACCEPT_HANDOVER", "Allows a calling app to continue a call which was started in another app.", - "ACCESS_CHECKIN_PROPERTIES", "Allows read/write access to the \"properties\" table in the checkin database, to change values that get uploaded.", - "ACCESS_COARSE_LOCATION", "Allows an app to access approximate location.", - "ACCESS_FINE_LOCATION", "Allows an app to access precise location.", - "ACCESS_LOCATION_EXTRA_COMMANDS", "Allows an application to access extra location provider commands.", - "ACCESS_NETWORK_STATE", "Allows applications to access information about networks.", - "ACCESS_NOTIFICATION_POLICY", "Marker permission for applications that wish to access notification policy.", - "ACCESS_WIFI_STATE", "Allows applications to access information about Wi-Fi networks.", - "ACCOUNT_MANAGER", "Allows applications to call into AccountAuthenticators.", - "ADD_VOICEMAIL", "Allows an application to add voicemails into the system.", - "ANSWER_PHONE_CALLS", "Allows the app to answer an incoming phone call.", - "BATTERY_STATS", "Allows an application to collect battery statistics", - "BIND_ACCESSIBILITY_SERVICE", "Must be required by an AccessibilityService , to ensure that only the system can bind to it.", - "BIND_APPWIDGET", "Allows an application to tell the AppWidget service which application can access AppWidget's data.", - "BIND_AUTOFILL_SERVICE", "Must be required by a AutofillService , to ensure that only the system can bind to it.", - "BIND_CARRIER_MESSAGING_SERVICE", "BIND_CARRIER_SERVICES", - "BIND_CARRIER_SERVICES", "The system process that is allowed to bind to services in carrier apps will have this permission.", - "BIND_CHOOSER_TARGET_SERVICE", "Must be required by a ChooserTargetService , to ensure that only the system can bind to it.", - "BIND_CONDITION_PROVIDER_SERVICE", "Must be required by a ConditionProviderService , to ensure that only the system can bind to it.", - "BIND_DEVICE_ADMIN", "Must be required by device administration receiver, to ensure that only the system can interact with it.", - "BIND_DREAM_SERVICE", "Must be required by an DreamService , to ensure that only the system can bind to it.", - "BIND_INCALL_SERVICE", "Must be required by a InCallService , to ensure that only the system can bind to it.", - "BIND_INPUT_METHOD", "Must be required by an InputMethodService , to ensure that only the system can bind to it.", - "BIND_MIDI_DEVICE_SERVICE", "Must be required by an MidiDeviceService , to ensure that only the system can bind to it.", - "BIND_NFC_SERVICE", "Must be required by a HostApduService or OffHostApduService to ensure that only the system can bind to it.", - "BIND_NOTIFICATION_LISTENER_SERVICE", "Must be required by an NotificationListenerService , to ensure that only the system can bind to it.", - "BIND_PRINT_SERVICE", "Must be required by a PrintService , to ensure that only the system can bind to it.", - "BIND_QUICK_SETTINGS_TILE", "Allows an application to bind to third party quick settings tiles.", - "BIND_REMOTEVIEWS", "Must be required by a RemoteViewsService , to ensure that only the system can bind to it.", - "BIND_SCREENING_SERVICE", "Must be required by a CallScreeningService , to ensure that only the system can bind to it.", - "BIND_TELECOM_CONNECTION_SERVICE", "Must be required by a ConnectionService , to ensure that only the system can bind to it.", - "BIND_TEXT_SERVICE", "Must be required by a TextService (e.g.", - "BIND_TV_INPUT", "Must be required by a TvInputService to ensure that only the system can bind to it.", - "BIND_VISUAL_VOICEMAIL_SERVICE", "Must be required by a link VisualVoicemailService to ensure that only the system can bind to it.", - "BIND_VOICE_INTERACTION", "Must be required by a VoiceInteractionService , to ensure that only the system can bind to it.", - "BIND_VPN_SERVICE", "Must be required by a VpnService , to ensure that only the system can bind to it.", - "BIND_VR_LISTENER_SERVICE", "Must be required by an VrListenerService , to ensure that only the system can bind to it.", - "BIND_WALLPAPER", "Must be required by a WallpaperService , to ensure that only the system can bind to it.", - "BLUETOOTH", "Allows applications to connect to paired bluetooth devices.", - "BLUETOOTH_ADMIN", "Allows applications to discover and pair bluetooth devices.", - "BLUETOOTH_PRIVILEGED", "Allows applications to pair bluetooth devices without user interaction, and to allow or disallow phonebook access or message access.", - "BODY_SENSORS", "Allows an application to access data from sensors that the user uses to measure what is happening inside his/her body, such as heart rate.", - "BROADCAST_PACKAGE_REMOVED", "Allows an application to broadcast a notification that an application package has been removed.", - "BROADCAST_SMS", "Allows an application to broadcast an SMS receipt notification.", - "BROADCAST_STICKY", "Allows an application to broadcast sticky intents.", - "BROADCAST_WAP_PUSH", "Allows an application to broadcast a WAP PUSH receipt notification.", - "CALL_PHONE", "Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call.", - "CALL_PRIVILEGED", "Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.", - "CAMERA", "Required to be able to access the camera device.", - "CAPTURE_AUDIO_OUTPUT", "Allows an application to capture audio output.", - "CAPTURE_SECURE_VIDEO_OUTPUT", "Allows an application to capture secure video output.", - "CAPTURE_VIDEO_OUTPUT", "Allows an application to capture video output.", - "CHANGE_COMPONENT_ENABLED_STATE", "Allows an application to change whether an application component (other than its own) is enabled or not.", - "CHANGE_CONFIGURATION", "Allows an application to modify the current configuration, such as locale.", - "CHANGE_NETWORK_STATE", "Allows applications to change network connectivity state.", - "CHANGE_WIFI_MULTICAST_STATE", "Allows applications to enter Wi-Fi Multicast mode.", - "CHANGE_WIFI_STATE", "Allows applications to change Wi-Fi connectivity state.", - "CLEAR_APP_CACHE", "Allows an application to clear the caches of all installed applications on the device.", - "CONTROL_LOCATION_UPDATES", "Allows enabling/disabling location update notifications from the radio.", - "DELETE_CACHE_FILES", "Old permission for deleting an app's cache files, no longer used, but signals for us to quietly ignore calls instead of throwing an exception.", - "DELETE_PACKAGES", "Allows an application to delete packages.", - "DIAGNOSTIC", "Allows applications to RW to diagnostic resources.", - "DISABLE_KEYGUARD", "Allows applications to disable the keyguard if it is not secure.", - "DUMP", "Allows an application to retrieve state dump information from system services.", - "EXPAND_STATUS_BAR", "Allows an application to expand or collapse the status bar.", - "FACTORY_TEST", "Run as a manufacturer test application, running as the root user.", - "FOREGROUND_SERVICE", "Allows a regular application to use Service.startForeground .", - "GET_ACCOUNTS", "Allows access to the list of accounts in the Accounts Service.", - "GET_ACCOUNTS_PRIVILEGED", "Allows access to the list of accounts in the Accounts Service.", - "GET_PACKAGE_SIZE", "Allows an application to find out the space used by any package.", - "GET_TASKS", " This constant was deprecated in API level 21. No longer enforced. ", - "GLOBAL_SEARCH", "This permission can be used on content providers to allow the global search system to access their data.", - "INSTALL_LOCATION_PROVIDER", "Allows an application to install a location provider into the Location Manager.", - "INSTALL_PACKAGES", "Allows an application to install packages.", - "INSTALL_SHORTCUT", "Allows an application to install a shortcut in Launcher.", - "INSTANT_APP_FOREGROUND_SERVICE", "Allows an instant app to create foreground services.", - "INTERNET", "Allows applications to open network sockets.", - "KILL_BACKGROUND_PROCESSES", "Allows an application to call killBackgroundProcesses(String) .", - "LOCATION_HARDWARE", "Allows an application to use location features in hardware, such as the geofencing api.", - "MANAGE_DOCUMENTS", "Allows an application to manage access to documents, usually as part of a document picker.", - "MANAGE_OWN_CALLS", "Allows a calling application which manages it own calls through the self-managed ConnectionService APIs.", - "MASTER_CLEAR", "Not for use by third-party applications.", - "MEDIA_CONTENT_CONTROL", "Allows an application to know what content is playing and control its playback.", - "MODIFY_AUDIO_SETTINGS", "Allows an application to modify global audio settings.", - "MODIFY_PHONE_STATE", "Allows modification of the telephony state - power on, mmi, etc.", - "MOUNT_FORMAT_FILESYSTEMS", "Allows formatting file systems for removable storage.", - "MOUNT_UNMOUNT_FILESYSTEMS", "Allows mounting and unmounting file systems for removable storage.", - "NFC", "Allows applications to perform I/O operations over NFC.", - "NFC_TRANSACTION_EVENT", "Allows applications to receive NFC transaction events.", - "PACKAGE_USAGE_STATS", "Allows an application to collect component usage statistics", - "PERSISTENT_ACTIVITY", " This constant was deprecated in API level 9. This functionality will be removed in the future; please do not use. Allow an application to make its activities persistent. ", - "PROCESS_OUTGOING_CALLS", "Allows an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether.", - "READ_CALENDAR", "Allows an application to read the user's calendar data.", - "READ_CALL_LOG", "Allows an application to read the user's call log.", - "READ_CONTACTS", "Allows an application to read the user's contacts data.", - "READ_EXTERNAL_STORAGE", "Allows an application to read from external storage.", - "READ_FRAME_BUFFER", "Allows an application to take screen shots and more generally get access to the frame buffer data.", - "READ_INPUT_STATE", " This constant was deprecated in API level 16. The API that used this permission has been removed. ", - "READ_LOGS", "Allows an application to read the low-level system log files.", - "READ_PHONE_NUMBERS", "Allows read access to the device's phone number(s).", - "READ_PHONE_STATE", "Allows read only access to phone state, including the phone number of the device, current cellular network information, the status of any ongoing calls, and a list of any PhoneAccount s registered on the device.", - "READ_SMS", "Allows an application to read SMS messages.", - "READ_SYNC_SETTINGS", "Allows applications to read the sync settings.", - "READ_SYNC_STATS", "Allows applications to read the sync stats.", - "READ_VOICEMAIL", "Allows an application to read voicemails in the system.", - "REBOOT", "Required to be able to reboot the device.", - "RECEIVE_BOOT_COMPLETED", "Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.", - "RECEIVE_MMS", "Allows an application to monitor incoming MMS messages.", - "RECEIVE_SMS", "Allows an application to receive SMS messages.", - "RECEIVE_WAP_PUSH", "Allows an application to receive WAP push messages.", - "RECORD_AUDIO", "Allows an application to record audio.", - "REORDER_TASKS", "Allows an application to change the Z-order of tasks.", - "REQUEST_COMPANION_RUN_IN_BACKGROUND", "Allows a companion app to run in the background.", - "REQUEST_COMPANION_USE_DATA_IN_BACKGROUND", "Allows a companion app to use data in the background.", - "REQUEST_DELETE_PACKAGES", "Allows an application to request deleting packages.", - "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", "Permission an application must hold in order to use ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS .", - "REQUEST_INSTALL_PACKAGES", "Allows an application to request installing packages.", - "RESTART_PACKAGES", "restartPackage(String)", - "SEND_RESPOND_VIA_MESSAGE", "Allows an application (Phone) to send a request to other applications to handle the respond-via-message action during incoming calls.", - "SEND_SMS", "Allows an application to send SMS messages.", - "SET_ALARM", "Allows an application to broadcast an Intent to set an alarm for the user.", - "SET_ALWAYS_FINISH", "Allows an application to control whether activities are immediately finished when put in the background.", - "SET_ANIMATION_SCALE", "Modify the global animation scaling factor.", - "SET_DEBUG_APP", "Configure an application for debugging.", - "SET_PREFERRED_APPLICATIONS", "addPackageToPreferred(String)", - "SET_PROCESS_LIMIT", "Allows an application to set the maximum number of (not needed) application processes that can be running.", - "SET_TIME", "Allows applications to set the system time.", - "SET_TIME_ZONE", "Allows applications to set the system time zone.", - "SET_WALLPAPER", "Allows applications to set the wallpaper.", - "SET_WALLPAPER_HINTS", "Allows applications to set the wallpaper hints.", - "SIGNAL_PERSISTENT_PROCESSES", "Allow an application to request that a signal be sent to all persistent processes.", - "STATUS_BAR", "Allows an application to open, close, or disable the status bar and its icons.", - "SYSTEM_ALERT_WINDOW", "Allows an app to create windows using the type TYPE_APPLICATION_OVERLAY , shown on top of all other apps.", - "TRANSMIT_IR", "Allows using the device's IR transmitter, if available.", - "UNINSTALL_SHORTCUT", "This permission is no longer supported.", - "UPDATE_DEVICE_STATS", "Allows an application to update device statistics.", - "USE_BIOMETRIC", "Allows an app to use device supported biometric modalities.", - "USE_FINGERPRINT", "USE_BIOMETRIC", - "USE_SIP", "Allows an application to use SIP service.", - "VIBRATE", "Allows access to the vibrator.", - "WAKE_LOCK", "Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming.", - "WRITE_APN_SETTINGS", "Allows applications to write the apn settings.", - "WRITE_CALENDAR", "Allows an application to write the user's calendar data.", - "WRITE_CALL_LOG", "Allows an application to write (but not read) the user's call log data.", - "WRITE_CONTACTS", "Allows an application to write the user's contacts data.", - "WRITE_EXTERNAL_STORAGE", "Allows an application to write to external storage.", - "WRITE_GSERVICES", "Allows an application to modify the Google service map.", - "WRITE_SECURE_SETTINGS", "Allows an application to read or write the secure system settings.", - "WRITE_SETTINGS", "Allows an application to read or write the system settings.", - "WRITE_SYNC_SETTINGS", "Allows applications to write the sync settings.", - "WRITE_VOICEMAIL", "Allows an application to modify and remove existing voicemails in the system." - }; - - // Dangerous permissions that need runtime approval: - // https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous - public static final String[] dangerous = { - "READ_CALENDAR", - "WRITE_CALENDAR", - "CAMERA", - "READ_CONTACTS", - "WRITE_CONTACTS", - "GET_ACCOUNTS", - "ACCESS_FINE_LOCATION", - "ACCESS_COARSE_LOCATION", - "RECORD_AUDIO", - "READ_PHONE_STATE", - "READ_PHONE_NUMBERS", - "CALL_PHONE", - "ANSWER_PHONE_CALLS", - "READ_CALL_LOG", - "WRITE_CALL_LOG", - "ADD_VOICEMAIL", - "USE_SIP", - "PROCESS_OUTGOING_CALLS", - "BODY_SENSORS", - "SEND_SMS", - "RECEIVE_SMS", - "READ_SMS", - "RECEIVE_WAP_PUSH", - "RECEIVE_MMS", - "READ_EXTERNAL_STORAGE", - "WRITE_EXTERNAL_STORAGE" - }; - - static String[] title; - static String[] description; - static int count; - static { - count = listing.length / 2; - title = new String[count]; - description = new String[count]; - for (int i = 0; i < count; i++) { - title[i] = listing[i*2]; - description[i] = listing[i*2+1]; - } - } -} - - -// Code for this CheckBoxList class found on the net, though I've lost the -// link. If you run across the original version, please let me know so that -// the original author can be credited properly. It was from a snippet -// collection, but it seems to have been picked up so many places with others -// placing their copyright on it that I haven't been able to determine the -// original author. [fry 20100216] -@SuppressWarnings("serial") -class CheckBoxList extends JList { - protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); - int checkboxWidth; - - public CheckBoxList() { - setCellRenderer(new CellRenderer()); - - // get the width of a checkbox so we can figure out if the mouse is inside - checkboxWidth = new JCheckBox().getPreferredSize().width; - // add the amount for the inset - checkboxWidth += Permissions.BORDER_HORIZ; - - addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - if (isEnabled()) { -// System.out.println("cbw = " + checkboxWidth); - int index = locationToIndex(e.getPoint()); -// descriptionLabel.setText(description[index]); - if (index != -1) { - JCheckBox checkbox = getModel().getElementAt(index); - //System.out.println("mouse event in list: " + e); -// System.out.println(checkbox.getSize() + " ... " + checkbox); -// if (e.getX() < checkbox.getSize().height) { - if (e.getX() < checkboxWidth) { - checkbox.setSelected(!checkbox.isSelected()); - repaint(); - } - } - } - } - }); - setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - } - - - protected class CellRenderer implements ListCellRenderer { - public Component getListCellRendererComponent(JList list, - JCheckBox checkbox, - int index, boolean isSelected, - boolean cellHasFocus) { - checkbox.setBackground(isSelected ? getSelectionBackground() : getBackground()); - checkbox.setForeground(isSelected ? getSelectionForeground() : getForeground()); - checkbox.setEnabled(list.isEnabled()); - checkbox.setFont(getFont()); - checkbox.setFocusPainted(false); - checkbox.setBorderPainted(true); - checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); - return checkbox; - } - } -} diff --git a/mode/templates/VRActivity.java.tmpl b/mode/templates/VRActivity.java.tmpl deleted file mode 100644 index ce92c4e08..000000000 --- a/mode/templates/VRActivity.java.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -package @@package_name@@; - -import android.os.Bundle; - -import processing.vr.PVR; -import processing.core.PApplet; - -public class MainActivity extends PVR { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PApplet sketch = new @@sketch_class_name@@(); - @@external@@ - setSketch(sketch); - } -} \ No newline at end of file diff --git a/mode/templates/VRBuild.gradle.tmpl b/mode/templates/VRBuild.gradle.tmpl deleted file mode 100644 index 3f58f6606..000000000 --- a/mode/templates/VRBuild.gradle.tmpl +++ /dev/null @@ -1,43 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion @@target_sdk@@ - defaultConfig { - applicationId "@@package_name@@" - minSdkVersion @@min_sdk@@ - targetSdkVersion @@target_sdk@@ - versionCode @@version_code@@ - versionName "@@version_name@@" - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - buildTypes { - debug { - debuggable true - } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - lintOptions { - abortOnError false - } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:@@support_version@@' - implementation 'com.android.support:design:@@support_version@@' - implementation 'com.google.android.support:wearable:@@wear_version@@' - compileOnly 'com.google.android.wearable:wearable:@@wear_version@@' - implementation 'com.google.vr:sdk-audio:@@gvr_version@@' - implementation 'com.google.vr:sdk-base:@@gvr_version@@' - implementation files('libs/processing-core.jar') - implementation files('libs/vr.jar') - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' -} diff --git a/mode/tools/SDKUpdater/.classpath b/mode/tools/SDKUpdater/.classpath deleted file mode 100644 index baec41f58..000000000 --- a/mode/tools/SDKUpdater/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/mode/tools/SDKUpdater/.project b/mode/tools/SDKUpdater/.project deleted file mode 100644 index 82aae162e..000000000 --- a/mode/tools/SDKUpdater/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - android-mode-sdkupdater - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/mode/tools/SDKUpdater/build.gradle b/mode/tools/SDKUpdater/build.gradle deleted file mode 100644 index f9b26d43f..000000000 --- a/mode/tools/SDKUpdater/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -import java.nio.file.Files -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - -dependencies { - compile group: "org.processing", name: "pde", version: "${processingVersion}" - - compile name: "sdklib-${toolsLibVersion}" - compile name: "repository-${toolsLibVersion}" -} - -sourceSets { - main { - java { - srcDirs = ["src/"] - } - } -} - -clean.doFirst { - delete "tool" -} - -build.doLast { - // Copy jar file to tool folder - File toolJar = file("tool/SDKUpdater.jar"); - toolJar.mkdirs(); - Files.copy(file("$buildDir/libs/SDKUpdater.jar").toPath(), - toolJar.toPath(), REPLACE_EXISTING); -} 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/gradlew.bat b/processing/gradlew.bat similarity index 64% rename from gradlew.bat rename to processing/gradlew.bat index e95643d6a..ac1b06f93 100644 --- a/gradlew.bat +++ b/processing/gradlew.bat @@ -1,3 +1,19 @@ +@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 @@ -13,15 +29,18 @@ 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= +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 init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :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 %CMD_LINE_ARGS% +"%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 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/mode/tools/SDKUpdater/.settings/org.eclipse.jdt.core.prefs b/processing/mode/.settings/org.eclipse.jdt.core.prefs similarity index 61% rename from mode/tools/SDKUpdater/.settings/org.eclipse.jdt.core.prefs rename to processing/mode/.settings/org.eclipse.jdt.core.prefs index d17b6724d..0fee6a9c4 100644 --- a/mode/tools/SDKUpdater/.settings/org.eclipse.jdt.core.prefs +++ b/processing/mode/.settings/org.eclipse.jdt.core.prefs @@ -1,12 +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.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +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.source=1.7 +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/mode/examples/Basics/Arrays/Array/Array.pde b/processing/mode/examples/Basics/Arrays/Array/Array.pde similarity index 100% rename from mode/examples/Basics/Arrays/Array/Array.pde rename to processing/mode/examples/Basics/Arrays/Array/Array.pde diff --git a/mode/examples/Basics/Arrays/Array2D/Array2D.pde b/processing/mode/examples/Basics/Arrays/Array2D/Array2D.pde similarity index 100% rename from mode/examples/Basics/Arrays/Array2D/Array2D.pde rename to processing/mode/examples/Basics/Arrays/Array2D/Array2D.pde diff --git a/mode/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde b/processing/mode/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde similarity index 100% rename from mode/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde rename to processing/mode/examples/Basics/Arrays/ArrayObjects/ArrayObjects.pde diff --git a/mode/examples/Basics/Arrays/ArrayObjects/Module.pde b/processing/mode/examples/Basics/Arrays/ArrayObjects/Module.pde similarity index 100% rename from mode/examples/Basics/Arrays/ArrayObjects/Module.pde rename to processing/mode/examples/Basics/Arrays/ArrayObjects/Module.pde diff --git a/mode/examples/Basics/Camera/MoveEye/MoveEye.pde b/processing/mode/examples/Basics/Camera/MoveEye/MoveEye.pde similarity index 100% rename from mode/examples/Basics/Camera/MoveEye/MoveEye.pde rename to processing/mode/examples/Basics/Camera/MoveEye/MoveEye.pde diff --git a/mode/examples/Basics/Camera/Perspective/Perspective.pde b/processing/mode/examples/Basics/Camera/Perspective/Perspective.pde similarity index 100% rename from mode/examples/Basics/Camera/Perspective/Perspective.pde rename to processing/mode/examples/Basics/Camera/Perspective/Perspective.pde diff --git a/mode/examples/Basics/Color/Brightness/Brightness.pde b/processing/mode/examples/Basics/Color/Brightness/Brightness.pde similarity index 100% rename from mode/examples/Basics/Color/Brightness/Brightness.pde rename to processing/mode/examples/Basics/Color/Brightness/Brightness.pde diff --git a/mode/examples/Basics/Color/ColorWheel/ColorWheel.pde b/processing/mode/examples/Basics/Color/ColorWheel/ColorWheel.pde similarity index 100% rename from mode/examples/Basics/Color/ColorWheel/ColorWheel.pde rename to processing/mode/examples/Basics/Color/ColorWheel/ColorWheel.pde diff --git a/mode/examples/Basics/Color/Creating/Creating.pde b/processing/mode/examples/Basics/Color/Creating/Creating.pde similarity index 100% rename from mode/examples/Basics/Color/Creating/Creating.pde rename to processing/mode/examples/Basics/Color/Creating/Creating.pde diff --git a/mode/examples/Basics/Color/Hue/Hue.pde b/processing/mode/examples/Basics/Color/Hue/Hue.pde similarity index 100% rename from mode/examples/Basics/Color/Hue/Hue.pde rename to processing/mode/examples/Basics/Color/Hue/Hue.pde diff --git a/mode/examples/Basics/Color/LinearGradient/LinearGradient.pde b/processing/mode/examples/Basics/Color/LinearGradient/LinearGradient.pde similarity index 100% rename from mode/examples/Basics/Color/LinearGradient/LinearGradient.pde rename to processing/mode/examples/Basics/Color/LinearGradient/LinearGradient.pde diff --git a/mode/examples/Basics/Color/RadialGradient/RadialGradient.pde b/processing/mode/examples/Basics/Color/RadialGradient/RadialGradient.pde similarity index 100% rename from mode/examples/Basics/Color/RadialGradient/RadialGradient.pde rename to processing/mode/examples/Basics/Color/RadialGradient/RadialGradient.pde diff --git a/mode/examples/Basics/Color/RadialGradient2/RadialGradient2.pde b/processing/mode/examples/Basics/Color/RadialGradient2/RadialGradient2.pde similarity index 100% rename from mode/examples/Basics/Color/RadialGradient2/RadialGradient2.pde rename to processing/mode/examples/Basics/Color/RadialGradient2/RadialGradient2.pde diff --git a/mode/examples/Basics/Color/Reading/Reading.pde b/processing/mode/examples/Basics/Color/Reading/Reading.pde similarity index 100% rename from mode/examples/Basics/Color/Reading/Reading.pde rename to processing/mode/examples/Basics/Color/Reading/Reading.pde diff --git a/mode/examples/Basics/Color/Reading/data/cait.jpg b/processing/mode/examples/Basics/Color/Reading/data/cait.jpg similarity index 100% rename from mode/examples/Basics/Color/Reading/data/cait.jpg rename to processing/mode/examples/Basics/Color/Reading/data/cait.jpg diff --git a/mode/examples/Basics/Color/Relativity/Relativity.pde b/processing/mode/examples/Basics/Color/Relativity/Relativity.pde similarity index 100% rename from mode/examples/Basics/Color/Relativity/Relativity.pde rename to processing/mode/examples/Basics/Color/Relativity/Relativity.pde diff --git a/mode/examples/Basics/Color/Saturation/Saturation.pde b/processing/mode/examples/Basics/Color/Saturation/Saturation.pde similarity index 100% rename from mode/examples/Basics/Color/Saturation/Saturation.pde rename to processing/mode/examples/Basics/Color/Saturation/Saturation.pde diff --git a/mode/examples/Basics/Color/WaveGradient/WaveGradient.pde b/processing/mode/examples/Basics/Color/WaveGradient/WaveGradient.pde similarity index 100% rename from mode/examples/Basics/Color/WaveGradient/WaveGradient.pde rename to processing/mode/examples/Basics/Color/WaveGradient/WaveGradient.pde diff --git a/mode/examples/Basics/Control/Conditionals1/Conditionals1.pde b/processing/mode/examples/Basics/Control/Conditionals1/Conditionals1.pde similarity index 100% rename from mode/examples/Basics/Control/Conditionals1/Conditionals1.pde rename to processing/mode/examples/Basics/Control/Conditionals1/Conditionals1.pde diff --git a/mode/examples/Basics/Control/Conditionals2/Conditionals2.pde b/processing/mode/examples/Basics/Control/Conditionals2/Conditionals2.pde similarity index 100% rename from mode/examples/Basics/Control/Conditionals2/Conditionals2.pde rename to processing/mode/examples/Basics/Control/Conditionals2/Conditionals2.pde diff --git a/mode/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde b/processing/mode/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde similarity index 100% rename from mode/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde rename to processing/mode/examples/Basics/Control/EmbeddedIteration/EmbeddedIteration.pde diff --git a/mode/examples/Basics/Control/Iteration/Iteration.pde b/processing/mode/examples/Basics/Control/Iteration/Iteration.pde similarity index 100% rename from mode/examples/Basics/Control/Iteration/Iteration.pde rename to processing/mode/examples/Basics/Control/Iteration/Iteration.pde diff --git a/mode/examples/Basics/Control/LogicalOperators/LogicalOperators.pde b/processing/mode/examples/Basics/Control/LogicalOperators/LogicalOperators.pde similarity index 100% rename from mode/examples/Basics/Control/LogicalOperators/LogicalOperators.pde rename to processing/mode/examples/Basics/Control/LogicalOperators/LogicalOperators.pde diff --git a/mode/examples/Basics/Data/CharactersStrings/CharactersStrings.pde b/processing/mode/examples/Basics/Data/CharactersStrings/CharactersStrings.pde similarity index 100% rename from mode/examples/Basics/Data/CharactersStrings/CharactersStrings.pde rename to processing/mode/examples/Basics/Data/CharactersStrings/CharactersStrings.pde diff --git a/mode/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw b/processing/mode/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw similarity index 100% rename from mode/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw rename to processing/mode/examples/Basics/Data/CharactersStrings/data/Eureka-90.vlw diff --git a/mode/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg b/processing/mode/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg similarity index 100% rename from mode/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg rename to processing/mode/examples/Basics/Data/CharactersStrings/data/rathausFrog.jpg diff --git a/mode/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde b/processing/mode/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde similarity index 100% rename from mode/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde rename to processing/mode/examples/Basics/Data/DatatypeConversion/DatatypeConversion.pde diff --git a/mode/examples/Basics/Data/IntegersFloats/IntegersFloats.pde b/processing/mode/examples/Basics/Data/IntegersFloats/IntegersFloats.pde similarity index 100% rename from mode/examples/Basics/Data/IntegersFloats/IntegersFloats.pde rename to processing/mode/examples/Basics/Data/IntegersFloats/IntegersFloats.pde diff --git a/mode/examples/Basics/Data/TrueFalse/TrueFalse.pde b/processing/mode/examples/Basics/Data/TrueFalse/TrueFalse.pde similarity index 100% rename from mode/examples/Basics/Data/TrueFalse/TrueFalse.pde rename to processing/mode/examples/Basics/Data/TrueFalse/TrueFalse.pde diff --git a/mode/examples/Basics/Data/VariableScope/VariableScope.pde b/processing/mode/examples/Basics/Data/VariableScope/VariableScope.pde similarity index 100% rename from mode/examples/Basics/Data/VariableScope/VariableScope.pde rename to processing/mode/examples/Basics/Data/VariableScope/VariableScope.pde diff --git a/mode/examples/Basics/Data/Variables/Variables.pde b/processing/mode/examples/Basics/Data/Variables/Variables.pde similarity index 100% rename from mode/examples/Basics/Data/Variables/Variables.pde rename to processing/mode/examples/Basics/Data/Variables/Variables.pde diff --git a/mode/examples/Basics/Form/Bezier/Bezier.pde b/processing/mode/examples/Basics/Form/Bezier/Bezier.pde similarity index 100% rename from mode/examples/Basics/Form/Bezier/Bezier.pde rename to processing/mode/examples/Basics/Form/Bezier/Bezier.pde diff --git a/mode/examples/Basics/Form/BezierEllipse/BezierEllipse.pde b/processing/mode/examples/Basics/Form/BezierEllipse/BezierEllipse.pde similarity index 100% rename from mode/examples/Basics/Form/BezierEllipse/BezierEllipse.pde rename to processing/mode/examples/Basics/Form/BezierEllipse/BezierEllipse.pde diff --git a/mode/examples/Basics/Form/PieChart/PieChart.pde b/processing/mode/examples/Basics/Form/PieChart/PieChart.pde similarity index 100% rename from mode/examples/Basics/Form/PieChart/PieChart.pde rename to processing/mode/examples/Basics/Form/PieChart/PieChart.pde diff --git a/mode/examples/Basics/Form/PointsLines/PointsLines.pde b/processing/mode/examples/Basics/Form/PointsLines/PointsLines.pde similarity index 100% rename from mode/examples/Basics/Form/PointsLines/PointsLines.pde rename to processing/mode/examples/Basics/Form/PointsLines/PointsLines.pde diff --git a/mode/examples/Basics/Form/Primitives3D/Primitives3D.pde b/processing/mode/examples/Basics/Form/Primitives3D/Primitives3D.pde similarity index 100% rename from mode/examples/Basics/Form/Primitives3D/Primitives3D.pde rename to processing/mode/examples/Basics/Form/Primitives3D/Primitives3D.pde diff --git a/mode/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde b/processing/mode/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde similarity index 100% rename from mode/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde rename to processing/mode/examples/Basics/Form/ShapePrimitives/ShapePrimitives.pde diff --git a/mode/examples/Basics/Form/SimpleCurves/SimpleCurves.pde b/processing/mode/examples/Basics/Form/SimpleCurves/SimpleCurves.pde similarity index 100% rename from mode/examples/Basics/Form/SimpleCurves/SimpleCurves.pde rename to processing/mode/examples/Basics/Form/SimpleCurves/SimpleCurves.pde diff --git a/mode/examples/Basics/Form/TriangleStrip/TriangleStrip.pde b/processing/mode/examples/Basics/Form/TriangleStrip/TriangleStrip.pde similarity index 100% rename from mode/examples/Basics/Form/TriangleStrip/TriangleStrip.pde rename to processing/mode/examples/Basics/Form/TriangleStrip/TriangleStrip.pde diff --git a/mode/examples/Basics/Form/Vertices/Vertices.pde b/processing/mode/examples/Basics/Form/Vertices/Vertices.pde similarity index 100% rename from mode/examples/Basics/Form/Vertices/Vertices.pde rename to processing/mode/examples/Basics/Form/Vertices/Vertices.pde diff --git a/mode/examples/Basics/Image/Alphamask/Alphamask.pde b/processing/mode/examples/Basics/Image/Alphamask/Alphamask.pde similarity index 100% rename from mode/examples/Basics/Image/Alphamask/Alphamask.pde rename to processing/mode/examples/Basics/Image/Alphamask/Alphamask.pde diff --git a/mode/examples/Basics/Image/Alphamask/data/mask.jpg b/processing/mode/examples/Basics/Image/Alphamask/data/mask.jpg similarity index 100% rename from mode/examples/Basics/Image/Alphamask/data/mask.jpg rename to processing/mode/examples/Basics/Image/Alphamask/data/mask.jpg diff --git a/mode/examples/Basics/Image/Alphamask/data/test.jpg b/processing/mode/examples/Basics/Image/Alphamask/data/test.jpg similarity index 100% rename from mode/examples/Basics/Image/Alphamask/data/test.jpg rename to processing/mode/examples/Basics/Image/Alphamask/data/test.jpg diff --git a/mode/examples/Basics/Image/BackgroundImage/BackgroundImage.pde b/processing/mode/examples/Basics/Image/BackgroundImage/BackgroundImage.pde similarity index 100% rename from mode/examples/Basics/Image/BackgroundImage/BackgroundImage.pde rename to processing/mode/examples/Basics/Image/BackgroundImage/BackgroundImage.pde diff --git a/mode/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg b/processing/mode/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg similarity index 100% rename from mode/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg rename to processing/mode/examples/Basics/Image/BackgroundImage/data/milan_rubbish.jpg diff --git a/mode/examples/Basics/Image/CreateImage/CreateImage.pde b/processing/mode/examples/Basics/Image/CreateImage/CreateImage.pde similarity index 100% rename from mode/examples/Basics/Image/CreateImage/CreateImage.pde rename to processing/mode/examples/Basics/Image/CreateImage/CreateImage.pde diff --git a/mode/examples/Basics/Image/CreateImage/data/mask.jpg b/processing/mode/examples/Basics/Image/CreateImage/data/mask.jpg similarity index 100% rename from mode/examples/Basics/Image/CreateImage/data/mask.jpg rename to processing/mode/examples/Basics/Image/CreateImage/data/mask.jpg diff --git a/mode/examples/Basics/Image/CreateImage/data/test.jpg b/processing/mode/examples/Basics/Image/CreateImage/data/test.jpg similarity index 100% rename from mode/examples/Basics/Image/CreateImage/data/test.jpg rename to processing/mode/examples/Basics/Image/CreateImage/data/test.jpg diff --git a/mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde b/processing/mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde similarity index 93% rename from mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde rename to processing/mode/examples/Basics/Image/LoadDisplayImage/LoadDisplayImage.pde index 8252c5cbc..495d1f124 100644 --- a/mode/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/mode/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg b/processing/mode/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg similarity index 100% rename from mode/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg rename to processing/mode/examples/Basics/Image/LoadDisplayImage/data/jelly.jpg diff --git a/mode/examples/Basics/Image/Pointillism/Pointillism.pde b/processing/mode/examples/Basics/Image/Pointillism/Pointillism.pde similarity index 100% rename from mode/examples/Basics/Image/Pointillism/Pointillism.pde rename to processing/mode/examples/Basics/Image/Pointillism/Pointillism.pde diff --git a/mode/examples/Basics/Image/Pointillism/data/eames.jpg b/processing/mode/examples/Basics/Image/Pointillism/data/eames.jpg similarity index 100% rename from mode/examples/Basics/Image/Pointillism/data/eames.jpg rename to processing/mode/examples/Basics/Image/Pointillism/data/eames.jpg diff --git a/mode/examples/Basics/Image/Pointillism/data/sunflower.jpg b/processing/mode/examples/Basics/Image/Pointillism/data/sunflower.jpg similarity index 100% rename from mode/examples/Basics/Image/Pointillism/data/sunflower.jpg rename to processing/mode/examples/Basics/Image/Pointillism/data/sunflower.jpg diff --git a/mode/examples/Basics/Image/RequestImage/RequestImage.pde b/processing/mode/examples/Basics/Image/RequestImage/RequestImage.pde similarity index 100% rename from mode/examples/Basics/Image/RequestImage/RequestImage.pde rename to processing/mode/examples/Basics/Image/RequestImage/RequestImage.pde diff --git a/mode/examples/Basics/Image/Sprite/Sprite.pde b/processing/mode/examples/Basics/Image/Sprite/Sprite.pde similarity index 100% rename from mode/examples/Basics/Image/Sprite/Sprite.pde rename to processing/mode/examples/Basics/Image/Sprite/Sprite.pde diff --git a/mode/examples/Basics/Image/Sprite/data/teddy.gif b/processing/mode/examples/Basics/Image/Sprite/data/teddy.gif similarity index 100% rename from mode/examples/Basics/Image/Sprite/data/teddy.gif rename to processing/mode/examples/Basics/Image/Sprite/data/teddy.gif diff --git a/mode/examples/Basics/Image/Sprite2/Sprite2.pde b/processing/mode/examples/Basics/Image/Sprite2/Sprite2.pde similarity index 100% rename from mode/examples/Basics/Image/Sprite2/Sprite2.pde rename to processing/mode/examples/Basics/Image/Sprite2/Sprite2.pde diff --git a/mode/examples/Basics/Image/Sprite2/data/sky.jpg b/processing/mode/examples/Basics/Image/Sprite2/data/sky.jpg similarity index 100% rename from mode/examples/Basics/Image/Sprite2/data/sky.jpg rename to processing/mode/examples/Basics/Image/Sprite2/data/sky.jpg diff --git a/mode/examples/Basics/Image/Sprite2/data/teddy.gif b/processing/mode/examples/Basics/Image/Sprite2/data/teddy.gif similarity index 100% rename from mode/examples/Basics/Image/Sprite2/data/teddy.gif rename to processing/mode/examples/Basics/Image/Sprite2/data/teddy.gif diff --git a/mode/examples/Basics/Image/Transparency/Transparency.pde b/processing/mode/examples/Basics/Image/Transparency/Transparency.pde similarity index 100% rename from mode/examples/Basics/Image/Transparency/Transparency.pde rename to processing/mode/examples/Basics/Image/Transparency/Transparency.pde diff --git a/mode/examples/Basics/Image/Transparency/data/construct.jpg b/processing/mode/examples/Basics/Image/Transparency/data/construct.jpg similarity index 100% rename from mode/examples/Basics/Image/Transparency/data/construct.jpg rename to processing/mode/examples/Basics/Image/Transparency/data/construct.jpg diff --git a/mode/examples/Basics/Image/Transparency/data/wash.jpg b/processing/mode/examples/Basics/Image/Transparency/data/wash.jpg similarity index 100% rename from mode/examples/Basics/Image/Transparency/data/wash.jpg rename to processing/mode/examples/Basics/Image/Transparency/data/wash.jpg diff --git a/mode/examples/Basics/Input/Clock/Clock.pde b/processing/mode/examples/Basics/Input/Clock/Clock.pde similarity index 100% rename from mode/examples/Basics/Input/Clock/Clock.pde rename to processing/mode/examples/Basics/Input/Clock/Clock.pde diff --git a/mode/examples/Basics/Input/Constrain/Constrain.pde b/processing/mode/examples/Basics/Input/Constrain/Constrain.pde similarity index 100% rename from mode/examples/Basics/Input/Constrain/Constrain.pde rename to processing/mode/examples/Basics/Input/Constrain/Constrain.pde diff --git a/mode/examples/Basics/Input/Easing/Easing.pde b/processing/mode/examples/Basics/Input/Easing/Easing.pde similarity index 100% rename from mode/examples/Basics/Input/Easing/Easing.pde rename to processing/mode/examples/Basics/Input/Easing/Easing.pde diff --git a/mode/examples/Basics/Input/Keyboard/Keyboard.pde b/processing/mode/examples/Basics/Input/Keyboard/Keyboard.pde similarity index 100% rename from mode/examples/Basics/Input/Keyboard/Keyboard.pde rename to processing/mode/examples/Basics/Input/Keyboard/Keyboard.pde diff --git a/mode/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde b/processing/mode/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde similarity index 100% rename from mode/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde rename to processing/mode/examples/Basics/Input/KeyboardFunctions/KeyboardFunctions.pde diff --git a/mode/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg b/processing/mode/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg similarity index 100% rename from mode/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg rename to processing/mode/examples/Basics/Input/KeyboardFunctions/data/brugges.jpg diff --git a/mode/examples/Basics/Input/Milliseconds/Milliseconds.pde b/processing/mode/examples/Basics/Input/Milliseconds/Milliseconds.pde similarity index 100% rename from mode/examples/Basics/Input/Milliseconds/Milliseconds.pde rename to processing/mode/examples/Basics/Input/Milliseconds/Milliseconds.pde diff --git a/mode/examples/Basics/Input/Mouse1D/Mouse1D.pde b/processing/mode/examples/Basics/Input/Mouse1D/Mouse1D.pde similarity index 100% rename from mode/examples/Basics/Input/Mouse1D/Mouse1D.pde rename to processing/mode/examples/Basics/Input/Mouse1D/Mouse1D.pde diff --git a/mode/examples/Basics/Input/Mouse2D/Mouse2D.pde b/processing/mode/examples/Basics/Input/Mouse2D/Mouse2D.pde similarity index 100% rename from mode/examples/Basics/Input/Mouse2D/Mouse2D.pde rename to processing/mode/examples/Basics/Input/Mouse2D/Mouse2D.pde diff --git a/mode/examples/Basics/Input/MouseFunctions/MouseFunctions.pde b/processing/mode/examples/Basics/Input/MouseFunctions/MouseFunctions.pde similarity index 100% rename from mode/examples/Basics/Input/MouseFunctions/MouseFunctions.pde rename to processing/mode/examples/Basics/Input/MouseFunctions/MouseFunctions.pde diff --git a/mode/examples/Basics/Input/MousePress/MousePress.pde b/processing/mode/examples/Basics/Input/MousePress/MousePress.pde similarity index 100% rename from mode/examples/Basics/Input/MousePress/MousePress.pde rename to processing/mode/examples/Basics/Input/MousePress/MousePress.pde diff --git a/mode/examples/Basics/Input/MouseSignals/MouseSignals.pde b/processing/mode/examples/Basics/Input/MouseSignals/MouseSignals.pde similarity index 100% rename from mode/examples/Basics/Input/MouseSignals/MouseSignals.pde rename to processing/mode/examples/Basics/Input/MouseSignals/MouseSignals.pde diff --git a/mode/examples/Basics/Input/StoringInput/StoringInput.pde b/processing/mode/examples/Basics/Input/StoringInput/StoringInput.pde similarity index 100% rename from mode/examples/Basics/Input/StoringInput/StoringInput.pde rename to processing/mode/examples/Basics/Input/StoringInput/StoringInput.pde diff --git a/mode/examples/Basics/Lights/Directional/Directional.pde b/processing/mode/examples/Basics/Lights/Directional/Directional.pde similarity index 100% rename from mode/examples/Basics/Lights/Directional/Directional.pde rename to processing/mode/examples/Basics/Lights/Directional/Directional.pde diff --git a/mode/examples/Basics/Lights/Mixture/Mixture.pde b/processing/mode/examples/Basics/Lights/Mixture/Mixture.pde similarity index 100% rename from mode/examples/Basics/Lights/Mixture/Mixture.pde rename to processing/mode/examples/Basics/Lights/Mixture/Mixture.pde diff --git a/mode/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde b/processing/mode/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde similarity index 100% rename from mode/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde rename to processing/mode/examples/Basics/Lights/MixtureGrid/MixtureGrid.pde diff --git a/mode/examples/Basics/Lights/OnOff/OnOff.pde b/processing/mode/examples/Basics/Lights/OnOff/OnOff.pde similarity index 100% rename from mode/examples/Basics/Lights/OnOff/OnOff.pde rename to processing/mode/examples/Basics/Lights/OnOff/OnOff.pde diff --git a/mode/examples/Basics/Lights/Reflection/Reflection.pde b/processing/mode/examples/Basics/Lights/Reflection/Reflection.pde similarity index 100% rename from mode/examples/Basics/Lights/Reflection/Reflection.pde rename to processing/mode/examples/Basics/Lights/Reflection/Reflection.pde diff --git a/mode/examples/Basics/Lights/Spot/Spot.pde b/processing/mode/examples/Basics/Lights/Spot/Spot.pde similarity index 100% rename from mode/examples/Basics/Lights/Spot/Spot.pde rename to processing/mode/examples/Basics/Lights/Spot/Spot.pde diff --git a/mode/examples/Basics/Math/AdditiveWave/AdditiveWave.pde b/processing/mode/examples/Basics/Math/AdditiveWave/AdditiveWave.pde similarity index 100% rename from mode/examples/Basics/Math/AdditiveWave/AdditiveWave.pde rename to processing/mode/examples/Basics/Math/AdditiveWave/AdditiveWave.pde diff --git a/mode/examples/Basics/Math/Arctangent/Arctangent.pde b/processing/mode/examples/Basics/Math/Arctangent/Arctangent.pde similarity index 100% rename from mode/examples/Basics/Math/Arctangent/Arctangent.pde rename to processing/mode/examples/Basics/Math/Arctangent/Arctangent.pde diff --git a/mode/examples/Basics/Math/Distance1D/Distance1D.pde b/processing/mode/examples/Basics/Math/Distance1D/Distance1D.pde similarity index 100% rename from mode/examples/Basics/Math/Distance1D/Distance1D.pde rename to processing/mode/examples/Basics/Math/Distance1D/Distance1D.pde diff --git a/mode/examples/Basics/Math/Distance2D/Distance2D.pde b/processing/mode/examples/Basics/Math/Distance2D/Distance2D.pde similarity index 100% rename from mode/examples/Basics/Math/Distance2D/Distance2D.pde rename to processing/mode/examples/Basics/Math/Distance2D/Distance2D.pde diff --git a/mode/examples/Basics/Math/DoubleRandom/DoubleRandom.pde b/processing/mode/examples/Basics/Math/DoubleRandom/DoubleRandom.pde similarity index 100% rename from mode/examples/Basics/Math/DoubleRandom/DoubleRandom.pde rename to processing/mode/examples/Basics/Math/DoubleRandom/DoubleRandom.pde diff --git a/mode/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde b/processing/mode/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde similarity index 100% rename from mode/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde rename to processing/mode/examples/Basics/Math/Graphing2DEquation/Graphing2DEquation.pde diff --git a/mode/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde b/processing/mode/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde similarity index 100% rename from mode/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde rename to processing/mode/examples/Basics/Math/IncrementDecrement/IncrementDecrement.pde diff --git a/mode/examples/Basics/Math/Modulo/Modulo.pde b/processing/mode/examples/Basics/Math/Modulo/Modulo.pde similarity index 100% rename from mode/examples/Basics/Math/Modulo/Modulo.pde rename to processing/mode/examples/Basics/Math/Modulo/Modulo.pde diff --git a/mode/examples/Basics/Math/Noise1D/Noise1D.pde b/processing/mode/examples/Basics/Math/Noise1D/Noise1D.pde similarity index 100% rename from mode/examples/Basics/Math/Noise1D/Noise1D.pde rename to processing/mode/examples/Basics/Math/Noise1D/Noise1D.pde diff --git a/mode/examples/Basics/Math/Noise2D/Noise2D.pde b/processing/mode/examples/Basics/Math/Noise2D/Noise2D.pde similarity index 100% rename from mode/examples/Basics/Math/Noise2D/Noise2D.pde rename to processing/mode/examples/Basics/Math/Noise2D/Noise2D.pde diff --git a/mode/examples/Basics/Math/Noise3D/Noise3D.pde b/processing/mode/examples/Basics/Math/Noise3D/Noise3D.pde similarity index 100% rename from mode/examples/Basics/Math/Noise3D/Noise3D.pde rename to processing/mode/examples/Basics/Math/Noise3D/Noise3D.pde diff --git a/mode/examples/Basics/Math/NoiseWave/NoiseWave.pde b/processing/mode/examples/Basics/Math/NoiseWave/NoiseWave.pde similarity index 100% rename from mode/examples/Basics/Math/NoiseWave/NoiseWave.pde rename to processing/mode/examples/Basics/Math/NoiseWave/NoiseWave.pde diff --git a/mode/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde b/processing/mode/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde similarity index 100% rename from mode/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde rename to processing/mode/examples/Basics/Math/OperatorPrecedence/OperatorPrecedence.pde diff --git a/mode/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde b/processing/mode/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde similarity index 100% rename from mode/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde rename to processing/mode/examples/Basics/Math/PolarToCartesian/PolarToCartesian.pde diff --git a/mode/examples/Basics/Math/Random/Random.pde b/processing/mode/examples/Basics/Math/Random/Random.pde similarity index 100% rename from mode/examples/Basics/Math/Random/Random.pde rename to processing/mode/examples/Basics/Math/Random/Random.pde diff --git a/mode/examples/Basics/Math/Sine/Sine.pde b/processing/mode/examples/Basics/Math/Sine/Sine.pde similarity index 100% rename from mode/examples/Basics/Math/Sine/Sine.pde rename to processing/mode/examples/Basics/Math/Sine/Sine.pde diff --git a/mode/examples/Basics/Math/SineCosine/SineCosine.pde b/processing/mode/examples/Basics/Math/SineCosine/SineCosine.pde similarity index 100% rename from mode/examples/Basics/Math/SineCosine/SineCosine.pde rename to processing/mode/examples/Basics/Math/SineCosine/SineCosine.pde diff --git a/mode/examples/Basics/Math/SineWave/SineWave.pde b/processing/mode/examples/Basics/Math/SineWave/SineWave.pde similarity index 100% rename from mode/examples/Basics/Math/SineWave/SineWave.pde rename to processing/mode/examples/Basics/Math/SineWave/SineWave.pde diff --git a/mode/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde similarity index 100% rename from mode/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde rename to processing/mode/examples/Basics/Objects/CompositeObjects/CompositeObjects.pde diff --git a/mode/examples/Basics/Objects/CompositeObjects/Egg.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/Egg.pde similarity index 100% rename from mode/examples/Basics/Objects/CompositeObjects/Egg.pde rename to processing/mode/examples/Basics/Objects/CompositeObjects/Egg.pde diff --git a/mode/examples/Basics/Objects/CompositeObjects/EggRing.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/EggRing.pde similarity index 100% rename from mode/examples/Basics/Objects/CompositeObjects/EggRing.pde rename to processing/mode/examples/Basics/Objects/CompositeObjects/EggRing.pde diff --git a/mode/examples/Basics/Objects/CompositeObjects/Ring.pde b/processing/mode/examples/Basics/Objects/CompositeObjects/Ring.pde similarity index 100% rename from mode/examples/Basics/Objects/CompositeObjects/Ring.pde rename to processing/mode/examples/Basics/Objects/CompositeObjects/Ring.pde diff --git a/mode/examples/Basics/Objects/Inheritance/Inheritance.pde b/processing/mode/examples/Basics/Objects/Inheritance/Inheritance.pde similarity index 100% rename from mode/examples/Basics/Objects/Inheritance/Inheritance.pde rename to processing/mode/examples/Basics/Objects/Inheritance/Inheritance.pde diff --git a/mode/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde b/processing/mode/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde similarity index 100% rename from mode/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde rename to processing/mode/examples/Basics/Objects/MultipleConstructors/MultipleConstructors.pde diff --git a/mode/examples/Basics/Objects/Neighborhood/Neighborhood.pde b/processing/mode/examples/Basics/Objects/Neighborhood/Neighborhood.pde similarity index 100% rename from mode/examples/Basics/Objects/Neighborhood/Neighborhood.pde rename to processing/mode/examples/Basics/Objects/Neighborhood/Neighborhood.pde diff --git a/mode/examples/Basics/Objects/Objects/Objects.pde b/processing/mode/examples/Basics/Objects/Objects/Objects.pde similarity index 100% rename from mode/examples/Basics/Objects/Objects/Objects.pde rename to processing/mode/examples/Basics/Objects/Objects/Objects.pde diff --git a/mode/examples/Basics/Shape/DisableStyle/DisableStyle.pde b/processing/mode/examples/Basics/Shape/DisableStyle/DisableStyle.pde similarity index 100% rename from mode/examples/Basics/Shape/DisableStyle/DisableStyle.pde rename to processing/mode/examples/Basics/Shape/DisableStyle/DisableStyle.pde diff --git a/mode/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg b/processing/mode/examples/Basics/Shape/DisableStyle/data/bot1.svg similarity index 100% rename from mode/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg rename to processing/mode/examples/Basics/Shape/DisableStyle/data/bot1.svg diff --git a/mode/examples/Basics/Shape/GetChild/GetChild.pde b/processing/mode/examples/Basics/Shape/GetChild/GetChild.pde similarity index 100% rename from mode/examples/Basics/Shape/GetChild/GetChild.pde rename to processing/mode/examples/Basics/Shape/GetChild/GetChild.pde diff --git a/mode/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg b/processing/mode/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg similarity index 100% rename from mode/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg rename to processing/mode/examples/Basics/Shape/GetChild/data/usa-wikipedia.svg diff --git a/mode/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde similarity index 100% rename from mode/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/LoadDisplayOBJ.pde diff --git a/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl similarity index 100% rename from mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.mtl diff --git a/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj similarity index 100% rename from mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.obj diff --git a/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png b/processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png similarity index 100% rename from mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png rename to processing/mode/examples/Basics/Shape/LoadDisplayOBJ/data/rocket.png diff --git a/mode/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde b/processing/mode/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde similarity index 100% rename from mode/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde rename to processing/mode/examples/Basics/Shape/LoadDisplaySVG/LoadDisplaySVG.pde diff --git a/mode/examples/Basics/Shape/ScaleShape/data/bot1.svg b/processing/mode/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg similarity index 100% rename from mode/examples/Basics/Shape/ScaleShape/data/bot1.svg rename to processing/mode/examples/Basics/Shape/LoadDisplaySVG/data/bot1.svg diff --git a/mode/examples/Basics/Shape/ScaleShape/ScaleShape.pde b/processing/mode/examples/Basics/Shape/ScaleShape/ScaleShape.pde similarity index 100% rename from mode/examples/Basics/Shape/ScaleShape/ScaleShape.pde rename to processing/mode/examples/Basics/Shape/ScaleShape/ScaleShape.pde diff --git a/studio/apps/fast2d/src/main/assets/bot1.svg b/processing/mode/examples/Basics/Shape/ScaleShape/data/bot1.svg similarity index 100% rename from studio/apps/fast2d/src/main/assets/bot1.svg rename to processing/mode/examples/Basics/Shape/ScaleShape/data/bot1.svg diff --git a/mode/examples/Basics/Structure/Coordinates/Coordinates.pde b/processing/mode/examples/Basics/Structure/Coordinates/Coordinates.pde similarity index 100% rename from mode/examples/Basics/Structure/Coordinates/Coordinates.pde rename to processing/mode/examples/Basics/Structure/Coordinates/Coordinates.pde diff --git a/mode/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde b/processing/mode/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde similarity index 100% rename from mode/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde rename to processing/mode/examples/Basics/Structure/CreateGraphics/CreateGraphics.pde diff --git a/mode/examples/Basics/Structure/CreateGraphics/data/mask.jpg b/processing/mode/examples/Basics/Structure/CreateGraphics/data/mask.jpg similarity index 100% rename from mode/examples/Basics/Structure/CreateGraphics/data/mask.jpg rename to processing/mode/examples/Basics/Structure/CreateGraphics/data/mask.jpg diff --git a/mode/examples/Basics/Structure/CreateGraphics/data/test.jpg b/processing/mode/examples/Basics/Structure/CreateGraphics/data/test.jpg similarity index 100% rename from mode/examples/Basics/Structure/CreateGraphics/data/test.jpg rename to processing/mode/examples/Basics/Structure/CreateGraphics/data/test.jpg diff --git a/mode/examples/Basics/Structure/Functions/Functions.pde b/processing/mode/examples/Basics/Structure/Functions/Functions.pde similarity index 100% rename from mode/examples/Basics/Structure/Functions/Functions.pde rename to processing/mode/examples/Basics/Structure/Functions/Functions.pde diff --git a/mode/examples/Basics/Structure/Loop/Loop.pde b/processing/mode/examples/Basics/Structure/Loop/Loop.pde similarity index 100% rename from mode/examples/Basics/Structure/Loop/Loop.pde rename to processing/mode/examples/Basics/Structure/Loop/Loop.pde diff --git a/mode/examples/Basics/Structure/NoLoop/NoLoop.pde b/processing/mode/examples/Basics/Structure/NoLoop/NoLoop.pde similarity index 100% rename from mode/examples/Basics/Structure/NoLoop/NoLoop.pde rename to processing/mode/examples/Basics/Structure/NoLoop/NoLoop.pde diff --git a/mode/examples/Basics/Structure/Recursion/Recursion.pde b/processing/mode/examples/Basics/Structure/Recursion/Recursion.pde similarity index 100% rename from mode/examples/Basics/Structure/Recursion/Recursion.pde rename to processing/mode/examples/Basics/Structure/Recursion/Recursion.pde diff --git a/mode/examples/Basics/Structure/Recursion2/Recursion2.pde b/processing/mode/examples/Basics/Structure/Recursion2/Recursion2.pde similarity index 100% rename from mode/examples/Basics/Structure/Recursion2/Recursion2.pde rename to processing/mode/examples/Basics/Structure/Recursion2/Recursion2.pde diff --git a/mode/examples/Basics/Structure/Redraw/Redraw.pde b/processing/mode/examples/Basics/Structure/Redraw/Redraw.pde similarity index 100% rename from mode/examples/Basics/Structure/Redraw/Redraw.pde rename to processing/mode/examples/Basics/Structure/Redraw/Redraw.pde diff --git a/mode/examples/Basics/Structure/SetupDraw/SetupDraw.pde b/processing/mode/examples/Basics/Structure/SetupDraw/SetupDraw.pde similarity index 100% rename from mode/examples/Basics/Structure/SetupDraw/SetupDraw.pde rename to processing/mode/examples/Basics/Structure/SetupDraw/SetupDraw.pde diff --git a/mode/examples/Basics/Structure/StatementsComments/StatementsComments.pde b/processing/mode/examples/Basics/Structure/StatementsComments/StatementsComments.pde similarity index 100% rename from mode/examples/Basics/Structure/StatementsComments/StatementsComments.pde rename to processing/mode/examples/Basics/Structure/StatementsComments/StatementsComments.pde diff --git a/mode/examples/Basics/Structure/WidthHeight/WidthHeight.pde b/processing/mode/examples/Basics/Structure/WidthHeight/WidthHeight.pde similarity index 100% rename from mode/examples/Basics/Structure/WidthHeight/WidthHeight.pde rename to processing/mode/examples/Basics/Structure/WidthHeight/WidthHeight.pde diff --git a/mode/examples/Basics/Transform/Arm/Arm.pde b/processing/mode/examples/Basics/Transform/Arm/Arm.pde similarity index 100% rename from mode/examples/Basics/Transform/Arm/Arm.pde rename to processing/mode/examples/Basics/Transform/Arm/Arm.pde diff --git a/mode/examples/Basics/Transform/Rotate/Rotate.pde b/processing/mode/examples/Basics/Transform/Rotate/Rotate.pde similarity index 100% rename from mode/examples/Basics/Transform/Rotate/Rotate.pde rename to processing/mode/examples/Basics/Transform/Rotate/Rotate.pde diff --git a/mode/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde b/processing/mode/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde similarity index 100% rename from mode/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde rename to processing/mode/examples/Basics/Transform/RotatePushPop/RotatePushPop.pde diff --git a/mode/examples/Basics/Transform/RotateXY/RotateXY.pde b/processing/mode/examples/Basics/Transform/RotateXY/RotateXY.pde similarity index 100% rename from mode/examples/Basics/Transform/RotateXY/RotateXY.pde rename to processing/mode/examples/Basics/Transform/RotateXY/RotateXY.pde diff --git a/mode/examples/Basics/Transform/Scale/Scale.pde b/processing/mode/examples/Basics/Transform/Scale/Scale.pde similarity index 100% rename from mode/examples/Basics/Transform/Scale/Scale.pde rename to processing/mode/examples/Basics/Transform/Scale/Scale.pde diff --git a/mode/examples/Basics/Transform/Translate/Translate.pde b/processing/mode/examples/Basics/Transform/Translate/Translate.pde similarity index 100% rename from mode/examples/Basics/Transform/Translate/Translate.pde rename to processing/mode/examples/Basics/Transform/Translate/Translate.pde diff --git a/mode/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde b/processing/mode/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde similarity index 100% rename from mode/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde rename to processing/mode/examples/Basics/Transform/TriangleFlower/TriangleFlower.pde diff --git a/mode/examples/Basics/Typography/Letters/Letters.pde b/processing/mode/examples/Basics/Typography/Letters/Letters.pde similarity index 100% rename from mode/examples/Basics/Typography/Letters/Letters.pde rename to processing/mode/examples/Basics/Typography/Letters/Letters.pde diff --git a/mode/examples/Basics/Typography/Letters/data/CourierNew36.vlw b/processing/mode/examples/Basics/Typography/Letters/data/CourierNew36.vlw similarity index 100% rename from mode/examples/Basics/Typography/Letters/data/CourierNew36.vlw rename to processing/mode/examples/Basics/Typography/Letters/data/CourierNew36.vlw diff --git a/mode/examples/Basics/Typography/Words/Words.pde b/processing/mode/examples/Basics/Typography/Words/Words.pde similarity index 100% rename from mode/examples/Basics/Typography/Words/Words.pde rename to processing/mode/examples/Basics/Typography/Words/Words.pde diff --git a/mode/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 mode/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/mode/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde b/processing/mode/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde similarity index 100% rename from mode/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde rename to processing/mode/examples/Basics/Web/EmbeddedLinks/EmbeddedLinks.pde diff --git a/mode/examples/Basics/Web/LoadingImages/LoadingImages.pde b/processing/mode/examples/Basics/Web/LoadingImages/LoadingImages.pde similarity index 100% rename from mode/examples/Basics/Web/LoadingImages/LoadingImages.pde rename to processing/mode/examples/Basics/Web/LoadingImages/LoadingImages.pde diff --git a/mode/examples/Demos/Graphics/Particles/Particle.pde b/processing/mode/examples/Demos/Graphics/Particles/Particle.pde similarity index 100% rename from mode/examples/Demos/Graphics/Particles/Particle.pde rename to processing/mode/examples/Demos/Graphics/Particles/Particle.pde diff --git a/mode/examples/Demos/Graphics/Particles/ParticleSystem.pde b/processing/mode/examples/Demos/Graphics/Particles/ParticleSystem.pde similarity index 100% rename from mode/examples/Demos/Graphics/Particles/ParticleSystem.pde rename to processing/mode/examples/Demos/Graphics/Particles/ParticleSystem.pde diff --git a/mode/examples/Demos/Graphics/Particles/Particles.pde b/processing/mode/examples/Demos/Graphics/Particles/Particles.pde similarity index 100% rename from mode/examples/Demos/Graphics/Particles/Particles.pde rename to processing/mode/examples/Demos/Graphics/Particles/Particles.pde diff --git a/mode/examples/Demos/Graphics/Particles/data/sprite.png b/processing/mode/examples/Demos/Graphics/Particles/data/sprite.png similarity index 100% rename from mode/examples/Demos/Graphics/Particles/data/sprite.png rename to processing/mode/examples/Demos/Graphics/Particles/data/sprite.png diff --git a/mode/examples/Demos/Graphics/Patch/Patch.pde b/processing/mode/examples/Demos/Graphics/Patch/Patch.pde similarity index 100% rename from mode/examples/Demos/Graphics/Patch/Patch.pde rename to processing/mode/examples/Demos/Graphics/Patch/Patch.pde diff --git a/mode/examples/Demos/Graphics/Planets/Perlin.pde b/processing/mode/examples/Demos/Graphics/Planets/Perlin.pde similarity index 100% rename from mode/examples/Demos/Graphics/Planets/Perlin.pde rename to processing/mode/examples/Demos/Graphics/Planets/Perlin.pde diff --git a/mode/examples/Demos/Graphics/Planets/Planets.pde b/processing/mode/examples/Demos/Graphics/Planets/Planets.pde similarity index 100% rename from mode/examples/Demos/Graphics/Planets/Planets.pde rename to processing/mode/examples/Demos/Graphics/Planets/Planets.pde diff --git a/mode/examples/Demos/Graphics/Planets/data/mercury.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/mercury.jpg similarity index 100% rename from mode/examples/Demos/Graphics/Planets/data/mercury.jpg rename to processing/mode/examples/Demos/Graphics/Planets/data/mercury.jpg diff --git a/mode/examples/Demos/Graphics/Planets/data/planet.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/planet.jpg similarity index 100% rename from mode/examples/Demos/Graphics/Planets/data/planet.jpg rename to processing/mode/examples/Demos/Graphics/Planets/data/planet.jpg diff --git a/mode/examples/Demos/Graphics/Planets/data/starfield.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/starfield.jpg similarity index 100% rename from mode/examples/Demos/Graphics/Planets/data/starfield.jpg rename to processing/mode/examples/Demos/Graphics/Planets/data/starfield.jpg diff --git a/mode/examples/Demos/Graphics/Planets/data/sun.jpg b/processing/mode/examples/Demos/Graphics/Planets/data/sun.jpg similarity index 100% rename from mode/examples/Demos/Graphics/Planets/data/sun.jpg rename to processing/mode/examples/Demos/Graphics/Planets/data/sun.jpg diff --git a/mode/examples/Demos/Graphics/Ribbons/ArcBall.pde b/processing/mode/examples/Demos/Graphics/Ribbons/ArcBall.pde similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/ArcBall.pde rename to processing/mode/examples/Demos/Graphics/Ribbons/ArcBall.pde diff --git a/mode/examples/Demos/Graphics/Ribbons/BSpline.pde b/processing/mode/examples/Demos/Graphics/Ribbons/BSpline.pde similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/BSpline.pde rename to processing/mode/examples/Demos/Graphics/Ribbons/BSpline.pde diff --git a/mode/examples/Demos/Graphics/Ribbons/Geometry.pde b/processing/mode/examples/Demos/Graphics/Ribbons/Geometry.pde similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/Geometry.pde rename to processing/mode/examples/Demos/Graphics/Ribbons/Geometry.pde diff --git a/mode/examples/Demos/Graphics/Ribbons/PDB.pde b/processing/mode/examples/Demos/Graphics/Ribbons/PDB.pde similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/PDB.pde rename to processing/mode/examples/Demos/Graphics/Ribbons/PDB.pde diff --git a/mode/examples/Demos/Graphics/Ribbons/Ribbons.pde b/processing/mode/examples/Demos/Graphics/Ribbons/Ribbons.pde similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/Ribbons.pde rename to processing/mode/examples/Demos/Graphics/Ribbons/Ribbons.pde diff --git a/mode/examples/Demos/Graphics/Ribbons/data/1CBS.pdb b/processing/mode/examples/Demos/Graphics/Ribbons/data/1CBS.pdb similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/data/1CBS.pdb rename to processing/mode/examples/Demos/Graphics/Ribbons/data/1CBS.pdb diff --git a/mode/examples/Demos/Graphics/Ribbons/data/2POR.pdb b/processing/mode/examples/Demos/Graphics/Ribbons/data/2POR.pdb similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/data/2POR.pdb rename to processing/mode/examples/Demos/Graphics/Ribbons/data/2POR.pdb diff --git a/mode/examples/Demos/Graphics/Ribbons/data/4HHB.pdb b/processing/mode/examples/Demos/Graphics/Ribbons/data/4HHB.pdb similarity index 100% rename from mode/examples/Demos/Graphics/Ribbons/data/4HHB.pdb rename to processing/mode/examples/Demos/Graphics/Ribbons/data/4HHB.pdb diff --git a/mode/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde b/processing/mode/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde similarity index 100% rename from mode/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde rename to processing/mode/examples/Demos/Graphics/RotatingArcs/RotatingArcs.pde diff --git a/mode/examples/Demos/Graphics/Trefoil/Surface.pde b/processing/mode/examples/Demos/Graphics/Trefoil/Surface.pde similarity index 100% rename from mode/examples/Demos/Graphics/Trefoil/Surface.pde rename to processing/mode/examples/Demos/Graphics/Trefoil/Surface.pde diff --git a/mode/examples/Demos/Graphics/Trefoil/Trefoil.pde b/processing/mode/examples/Demos/Graphics/Trefoil/Trefoil.pde similarity index 100% rename from mode/examples/Demos/Graphics/Trefoil/Trefoil.pde rename to processing/mode/examples/Demos/Graphics/Trefoil/Trefoil.pde diff --git a/mode/examples/Demos/Graphics/Trefoil/data/particle.png b/processing/mode/examples/Demos/Graphics/Trefoil/data/particle.png similarity index 100% rename from mode/examples/Demos/Graphics/Trefoil/data/particle.png rename to processing/mode/examples/Demos/Graphics/Trefoil/data/particle.png diff --git a/mode/examples/Demos/Graphics/Wiggling/Wiggling.pde b/processing/mode/examples/Demos/Graphics/Wiggling/Wiggling.pde similarity index 100% rename from mode/examples/Demos/Graphics/Wiggling/Wiggling.pde rename to processing/mode/examples/Demos/Graphics/Wiggling/Wiggling.pde diff --git a/mode/examples/Demos/Graphics/Yellowtail/Gesture.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Gesture.pde similarity index 100% rename from mode/examples/Demos/Graphics/Yellowtail/Gesture.pde rename to processing/mode/examples/Demos/Graphics/Yellowtail/Gesture.pde diff --git a/mode/examples/Demos/Graphics/Yellowtail/Polygon.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Polygon.pde similarity index 100% rename from mode/examples/Demos/Graphics/Yellowtail/Polygon.pde rename to processing/mode/examples/Demos/Graphics/Yellowtail/Polygon.pde diff --git a/mode/examples/Demos/Graphics/Yellowtail/Vec3f.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Vec3f.pde similarity index 100% rename from mode/examples/Demos/Graphics/Yellowtail/Vec3f.pde rename to processing/mode/examples/Demos/Graphics/Yellowtail/Vec3f.pde diff --git a/mode/examples/Demos/Graphics/Yellowtail/Yellowtail.pde b/processing/mode/examples/Demos/Graphics/Yellowtail/Yellowtail.pde similarity index 100% rename from mode/examples/Demos/Graphics/Yellowtail/Yellowtail.pde rename to processing/mode/examples/Demos/Graphics/Yellowtail/Yellowtail.pde diff --git a/mode/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde b/processing/mode/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde similarity index 100% rename from mode/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde rename to processing/mode/examples/Demos/Performance/CubicGridImmediate/CubicGridImmediate.pde diff --git a/mode/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde b/processing/mode/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde similarity index 100% rename from mode/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde rename to processing/mode/examples/Demos/Performance/CubicGridRetained/CubicGridRetained.pde diff --git a/mode/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde b/processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde similarity index 100% rename from mode/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde rename to processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/DynamicParticlesImmediate.pde diff --git a/mode/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png b/processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png similarity index 100% rename from mode/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png rename to processing/mode/examples/Demos/Performance/DynamicParticlesImmediate/data/sprite.png diff --git a/mode/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde b/processing/mode/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde similarity index 100% rename from mode/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde rename to processing/mode/examples/Demos/Performance/DynamicParticlesRetained/DynamicParticlesRetained.pde diff --git a/mode/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png b/processing/mode/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png similarity index 100% rename from mode/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png rename to processing/mode/examples/Demos/Performance/DynamicParticlesRetained/data/sprite.png diff --git a/mode/examples/Demos/Performance/Esfera/Esfera.pde b/processing/mode/examples/Demos/Performance/Esfera/Esfera.pde similarity index 100% rename from mode/examples/Demos/Performance/Esfera/Esfera.pde rename to processing/mode/examples/Demos/Performance/Esfera/Esfera.pde diff --git a/mode/examples/Demos/Performance/LineRendering/LineRendering.pde b/processing/mode/examples/Demos/Performance/LineRendering/LineRendering.pde similarity index 100% rename from mode/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/mode/examples/Demos/Performance/QuadRendering/QuadRendering.pde b/processing/mode/examples/Demos/Performance/QuadRendering/QuadRendering.pde similarity index 100% rename from mode/examples/Demos/Performance/QuadRendering/QuadRendering.pde rename to processing/mode/examples/Demos/Performance/QuadRendering/QuadRendering.pde diff --git a/mode/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde b/processing/mode/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde similarity index 100% rename from mode/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde rename to processing/mode/examples/Demos/Performance/StaticParticlesImmediate/StaticParticlesImmediate.pde diff --git a/mode/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png b/processing/mode/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png similarity index 100% rename from mode/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png rename to processing/mode/examples/Demos/Performance/StaticParticlesImmediate/data/sprite.png diff --git a/mode/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde b/processing/mode/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde similarity index 100% rename from mode/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde rename to processing/mode/examples/Demos/Performance/StaticParticlesRetained/StaticParticlesRetained.pde diff --git a/mode/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png b/processing/mode/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png similarity index 100% rename from mode/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png rename to processing/mode/examples/Demos/Performance/StaticParticlesRetained/data/sprite.png diff --git a/mode/examples/Demos/Performance/TextRendering/TextRendering.pde b/processing/mode/examples/Demos/Performance/TextRendering/TextRendering.pde similarity index 100% rename from mode/examples/Demos/Performance/TextRendering/TextRendering.pde rename to processing/mode/examples/Demos/Performance/TextRendering/TextRendering.pde diff --git a/mode/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde b/processing/mode/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde similarity index 100% rename from mode/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde rename to processing/mode/examples/Demos/Tests/NoBackgroundTest/NoBackgroundTest.pde diff --git a/mode/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde b/processing/mode/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde similarity index 100% rename from mode/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde rename to processing/mode/examples/Demos/Tests/OffscreenTest/OffscreenTest.pde diff --git a/mode/examples/Demos/Tests/RedrawTest/RedrawTest.pde b/processing/mode/examples/Demos/Tests/RedrawTest/RedrawTest.pde similarity index 100% rename from mode/examples/Demos/Tests/RedrawTest/RedrawTest.pde rename to processing/mode/examples/Demos/Tests/RedrawTest/RedrawTest.pde diff --git a/mode/examples/Sensors/Accelerometer/Accelerometer.pde b/processing/mode/examples/Sensors/Accelerometer/Accelerometer.pde similarity index 100% rename from mode/examples/Sensors/Accelerometer/Accelerometer.pde rename to processing/mode/examples/Sensors/Accelerometer/Accelerometer.pde diff --git a/mode/examples/Sensors/Accelerometer/AccelerometerManager.java b/processing/mode/examples/Sensors/Accelerometer/AccelerometerManager.java similarity index 100% rename from mode/examples/Sensors/Accelerometer/AccelerometerManager.java rename to processing/mode/examples/Sensors/Accelerometer/AccelerometerManager.java diff --git a/mode/examples/Sensors/Compass/Compass.pde b/processing/mode/examples/Sensors/Compass/Compass.pde similarity index 100% rename from mode/examples/Sensors/Compass/Compass.pde rename to processing/mode/examples/Sensors/Compass/Compass.pde diff --git a/mode/examples/Sensors/Compass/CompassManager.java b/processing/mode/examples/Sensors/Compass/CompassManager.java similarity index 100% rename from mode/examples/Sensors/Compass/CompassManager.java rename to processing/mode/examples/Sensors/Compass/CompassManager.java diff --git a/mode/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde b/processing/mode/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde similarity index 100% rename from mode/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde rename to processing/mode/examples/Topics/Advanced Data/ArrayListClass/ArrayListClass.pde diff --git a/mode/examples/Topics/Advanced Data/ArrayListClass/Ball.pde b/processing/mode/examples/Topics/Advanced Data/ArrayListClass/Ball.pde similarity index 100% rename from mode/examples/Topics/Advanced Data/ArrayListClass/Ball.pde rename to processing/mode/examples/Topics/Advanced Data/ArrayListClass/Ball.pde diff --git a/mode/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde b/processing/mode/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde similarity index 100% rename from mode/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde rename to processing/mode/examples/Topics/Advanced Data/DirectoryList/DirectoryList.pde diff --git a/mode/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde b/processing/mode/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde similarity index 100% rename from mode/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde diff --git a/mode/examples/Topics/Advanced Data/HashMapClass/Word.pde b/processing/mode/examples/Topics/Advanced Data/HashMapClass/Word.pde similarity index 100% rename from mode/examples/Topics/Advanced Data/HashMapClass/Word.pde rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/Word.pde diff --git a/mode/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt b/processing/mode/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt similarity index 100% rename from mode/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/data/dracula.txt diff --git a/mode/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt b/processing/mode/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt similarity index 100% rename from mode/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt rename to processing/mode/examples/Topics/Advanced Data/HashMapClass/data/hamlet.txt diff --git a/mode/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde b/processing/mode/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde similarity index 100% rename from mode/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde rename to processing/mode/examples/Topics/Animation/AnimatedSprite/AnimatedSprite.pde diff --git a/mode/examples/Topics/Animation/AnimatedSprite/Animation.pde b/processing/mode/examples/Topics/Animation/AnimatedSprite/Animation.pde similarity index 100% rename from mode/examples/Topics/Animation/AnimatedSprite/Animation.pde rename to processing/mode/examples/Topics/Animation/AnimatedSprite/Animation.pde diff --git a/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/examples/Topics/Animation/Sequential/Sequential.pde b/processing/mode/examples/Topics/Animation/Sequential/Sequential.pde similarity index 100% rename from mode/examples/Topics/Animation/Sequential/Sequential.pde rename to processing/mode/examples/Topics/Animation/Sequential/Sequential.pde diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0000.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0000.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0000.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0000.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0001.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0001.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0001.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0001.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0002.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0002.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0002.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0002.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0003.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0003.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0003.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0003.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0004.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0004.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0004.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0004.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0005.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0005.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0005.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0005.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0006.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0006.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0006.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0006.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0007.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0007.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0007.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0007.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0008.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0008.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0008.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0008.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0009.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0009.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0009.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0009.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0010.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0010.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0010.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0010.gif diff --git a/mode/examples/Topics/Animation/Sequential/data/PT_anim0011.gif b/processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0011.gif similarity index 100% rename from mode/examples/Topics/Animation/Sequential/data/PT_anim0011.gif rename to processing/mode/examples/Topics/Animation/Sequential/data/PT_anim0011.gif diff --git a/mode/examples/Topics/Cellular Automata/Conway/Conway.pde b/processing/mode/examples/Topics/Cellular Automata/Conway/Conway.pde similarity index 100% rename from mode/examples/Topics/Cellular Automata/Conway/Conway.pde rename to processing/mode/examples/Topics/Cellular Automata/Conway/Conway.pde diff --git a/mode/examples/Topics/Cellular Automata/Spore1/Spore1.pde b/processing/mode/examples/Topics/Cellular Automata/Spore1/Spore1.pde similarity index 100% rename from mode/examples/Topics/Cellular Automata/Spore1/Spore1.pde rename to processing/mode/examples/Topics/Cellular Automata/Spore1/Spore1.pde diff --git a/mode/examples/Topics/Cellular Automata/Spore2/Spore2.pde b/processing/mode/examples/Topics/Cellular Automata/Spore2/Spore2.pde similarity index 100% rename from mode/examples/Topics/Cellular Automata/Spore2/Spore2.pde rename to processing/mode/examples/Topics/Cellular Automata/Spore2/Spore2.pde diff --git a/mode/examples/Topics/Cellular Automata/Wolfram/CA.pde b/processing/mode/examples/Topics/Cellular Automata/Wolfram/CA.pde similarity index 100% rename from mode/examples/Topics/Cellular Automata/Wolfram/CA.pde rename to processing/mode/examples/Topics/Cellular Automata/Wolfram/CA.pde diff --git a/mode/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde b/processing/mode/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde similarity index 100% rename from mode/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde rename to processing/mode/examples/Topics/Cellular Automata/Wolfram/Wolfram.pde diff --git a/mode/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde b/processing/mode/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde rename to processing/mode/examples/Topics/Create Shapes/BeginEndContour/BeginEndContour.pde diff --git a/mode/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde b/processing/mode/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde rename to processing/mode/examples/Topics/Create Shapes/GroupPShape/GroupPShape.pde diff --git a/mode/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/Particle.pde diff --git a/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystem.pde diff --git a/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/ParticleSystemPShape.pde diff --git a/mode/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png b/processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png similarity index 100% rename from mode/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png rename to processing/mode/examples/Topics/Create Shapes/ParticleSystemPShape/data/sprite.png diff --git a/mode/examples/Topics/Create Shapes/PathPShape/PathPShape.pde b/processing/mode/examples/Topics/Create Shapes/PathPShape/PathPShape.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PathPShape/PathPShape.pde rename to processing/mode/examples/Topics/Create Shapes/PathPShape/PathPShape.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShape/PolygonPShape.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/PolygonPShapeOOP.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP/Star.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/Polygon.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP2/PolygonPShapeOOP2.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/Polygon.pde diff --git a/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde b/processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde rename to processing/mode/examples/Topics/Create Shapes/PolygonPShapeOOP3/PolygonPShapeOOP3.pde diff --git a/mode/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde b/processing/mode/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde rename to processing/mode/examples/Topics/Create Shapes/PrimitivePShape/PrimitivePShape.pde diff --git a/mode/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde b/processing/mode/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde rename to processing/mode/examples/Topics/Create Shapes/WigglePShape/WigglePShape.pde diff --git a/mode/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde b/processing/mode/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde similarity index 100% rename from mode/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde rename to processing/mode/examples/Topics/Create Shapes/WigglePShape/Wiggler.pde diff --git a/mode/examples/Topics/Drawing/Animator/Animator.pde b/processing/mode/examples/Topics/Drawing/Animator/Animator.pde similarity index 100% rename from mode/examples/Topics/Drawing/Animator/Animator.pde rename to processing/mode/examples/Topics/Drawing/Animator/Animator.pde diff --git a/mode/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde b/processing/mode/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde similarity index 100% rename from mode/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde rename to processing/mode/examples/Topics/Drawing/ContinuousLines/ContinuousLines.pde diff --git a/mode/examples/Topics/Drawing/CustomTool/CustomTool.pde b/processing/mode/examples/Topics/Drawing/CustomTool/CustomTool.pde similarity index 100% rename from mode/examples/Topics/Drawing/CustomTool/CustomTool.pde rename to processing/mode/examples/Topics/Drawing/CustomTool/CustomTool.pde diff --git a/mode/examples/Topics/Drawing/CustomTool/data/milan.jpg b/processing/mode/examples/Topics/Drawing/CustomTool/data/milan.jpg similarity index 100% rename from mode/examples/Topics/Drawing/CustomTool/data/milan.jpg rename to processing/mode/examples/Topics/Drawing/CustomTool/data/milan.jpg diff --git a/mode/examples/Topics/Drawing/CustomTool/data/paris.jpg b/processing/mode/examples/Topics/Drawing/CustomTool/data/paris.jpg similarity index 100% rename from mode/examples/Topics/Drawing/CustomTool/data/paris.jpg rename to processing/mode/examples/Topics/Drawing/CustomTool/data/paris.jpg diff --git a/mode/examples/Topics/Drawing/Pattern/Pattern.pde b/processing/mode/examples/Topics/Drawing/Pattern/Pattern.pde similarity index 100% rename from mode/examples/Topics/Drawing/Pattern/Pattern.pde rename to processing/mode/examples/Topics/Drawing/Pattern/Pattern.pde diff --git a/mode/examples/Topics/Drawing/Pulses/Pulses.pde b/processing/mode/examples/Topics/Drawing/Pulses/Pulses.pde similarity index 100% rename from mode/examples/Topics/Drawing/Pulses/Pulses.pde rename to processing/mode/examples/Topics/Drawing/Pulses/Pulses.pde diff --git a/mode/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde b/processing/mode/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde similarity index 100% rename from mode/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde rename to processing/mode/examples/Topics/Drawing/ScribblePlotter/ScribblePlotter.pde diff --git a/mode/examples/Topics/Effects/FireCube/FireCube.pde b/processing/mode/examples/Topics/Effects/FireCube/FireCube.pde similarity index 100% rename from mode/examples/Topics/Effects/FireCube/FireCube.pde rename to processing/mode/examples/Topics/Effects/FireCube/FireCube.pde diff --git a/mode/examples/Topics/Effects/Lens/Lens.pde b/processing/mode/examples/Topics/Effects/Lens/Lens.pde similarity index 100% rename from mode/examples/Topics/Effects/Lens/Lens.pde rename to processing/mode/examples/Topics/Effects/Lens/Lens.pde diff --git a/mode/examples/Topics/Effects/Lens/data/red_smoke.jpg b/processing/mode/examples/Topics/Effects/Lens/data/red_smoke.jpg similarity index 100% rename from mode/examples/Topics/Effects/Lens/data/red_smoke.jpg rename to processing/mode/examples/Topics/Effects/Lens/data/red_smoke.jpg diff --git a/mode/examples/Topics/Effects/Metaball/Metaball.pde b/processing/mode/examples/Topics/Effects/Metaball/Metaball.pde similarity index 100% rename from mode/examples/Topics/Effects/Metaball/Metaball.pde rename to processing/mode/examples/Topics/Effects/Metaball/Metaball.pde diff --git a/mode/examples/Topics/Effects/Plasma/Plasma.pde b/processing/mode/examples/Topics/Effects/Plasma/Plasma.pde similarity index 100% rename from mode/examples/Topics/Effects/Plasma/Plasma.pde rename to processing/mode/examples/Topics/Effects/Plasma/Plasma.pde diff --git a/mode/examples/Topics/Effects/Tunnel/Tunnel.pde b/processing/mode/examples/Topics/Effects/Tunnel/Tunnel.pde similarity index 100% rename from mode/examples/Topics/Effects/Tunnel/Tunnel.pde rename to processing/mode/examples/Topics/Effects/Tunnel/Tunnel.pde diff --git a/mode/examples/Topics/Effects/Tunnel/data/red_smoke.jpg b/processing/mode/examples/Topics/Effects/Tunnel/data/red_smoke.jpg similarity index 100% rename from mode/examples/Topics/Effects/Tunnel/data/red_smoke.jpg rename to processing/mode/examples/Topics/Effects/Tunnel/data/red_smoke.jpg diff --git a/mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde b/processing/mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde similarity index 100% rename from mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde rename to processing/mode/examples/Topics/Effects/UnlimitedSprites/UnlimitedSprites.pde diff --git a/mode/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 mode/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/mode/examples/Topics/Effects/Wormhole/Wormhole.pde b/processing/mode/examples/Topics/Effects/Wormhole/Wormhole.pde similarity index 100% rename from mode/examples/Topics/Effects/Wormhole/Wormhole.pde rename to processing/mode/examples/Topics/Effects/Wormhole/Wormhole.pde diff --git a/mode/examples/Topics/Effects/Wormhole/data/texture.gif b/processing/mode/examples/Topics/Effects/Wormhole/data/texture.gif similarity index 100% rename from mode/examples/Topics/Effects/Wormhole/data/texture.gif rename to processing/mode/examples/Topics/Effects/Wormhole/data/texture.gif diff --git a/mode/examples/Topics/Effects/Wormhole/data/wormhole.png b/processing/mode/examples/Topics/Effects/Wormhole/data/wormhole.png similarity index 100% rename from mode/examples/Topics/Effects/Wormhole/data/wormhole.png rename to processing/mode/examples/Topics/Effects/Wormhole/data/wormhole.png diff --git a/mode/examples/Topics/File IO/LoadFile1/LoadFile1.pde b/processing/mode/examples/Topics/File IO/LoadFile1/LoadFile1.pde similarity index 100% rename from mode/examples/Topics/File IO/LoadFile1/LoadFile1.pde rename to processing/mode/examples/Topics/File IO/LoadFile1/LoadFile1.pde diff --git a/mode/examples/Topics/File IO/LoadFile1/data/positions.txt b/processing/mode/examples/Topics/File IO/LoadFile1/data/positions.txt similarity index 100% rename from mode/examples/Topics/File IO/LoadFile1/data/positions.txt rename to processing/mode/examples/Topics/File IO/LoadFile1/data/positions.txt diff --git a/mode/examples/Topics/File IO/LoadFile2/LoadFile2.pde b/processing/mode/examples/Topics/File IO/LoadFile2/LoadFile2.pde similarity index 100% rename from mode/examples/Topics/File IO/LoadFile2/LoadFile2.pde rename to processing/mode/examples/Topics/File IO/LoadFile2/LoadFile2.pde diff --git a/mode/examples/Topics/File IO/LoadFile2/Record.pde b/processing/mode/examples/Topics/File IO/LoadFile2/Record.pde similarity index 100% rename from mode/examples/Topics/File IO/LoadFile2/Record.pde rename to processing/mode/examples/Topics/File IO/LoadFile2/Record.pde diff --git a/mode/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 mode/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/mode/examples/Topics/File IO/LoadFile2/data/cars2.tsv b/processing/mode/examples/Topics/File IO/LoadFile2/data/cars2.tsv similarity index 100% rename from mode/examples/Topics/File IO/LoadFile2/data/cars2.tsv rename to processing/mode/examples/Topics/File IO/LoadFile2/data/cars2.tsv diff --git a/mode/examples/Topics/File IO/SaveFile1/SaveFile1.pde b/processing/mode/examples/Topics/File IO/SaveFile1/SaveFile1.pde similarity index 100% rename from mode/examples/Topics/File IO/SaveFile1/SaveFile1.pde rename to processing/mode/examples/Topics/File IO/SaveFile1/SaveFile1.pde diff --git a/mode/examples/Topics/File IO/SaveFile2/SaveFile2.pde b/processing/mode/examples/Topics/File IO/SaveFile2/SaveFile2.pde similarity index 100% rename from mode/examples/Topics/File IO/SaveFile2/SaveFile2.pde rename to processing/mode/examples/Topics/File IO/SaveFile2/SaveFile2.pde diff --git a/mode/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde b/processing/mode/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde similarity index 100% rename from mode/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde rename to processing/mode/examples/Topics/File IO/SaveManyImages/SaveManyImages.pde diff --git a/mode/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde b/processing/mode/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde similarity index 100% rename from mode/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde rename to processing/mode/examples/Topics/File IO/SaveOneImage/SaveOneImage.pde diff --git a/mode/examples/Topics/File IO/TileImages/TileImages.pde b/processing/mode/examples/Topics/File IO/TileImages/TileImages.pde similarity index 100% rename from mode/examples/Topics/File IO/TileImages/TileImages.pde rename to processing/mode/examples/Topics/File IO/TileImages/TileImages.pde diff --git a/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/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 mode/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/mode/examples/Topics/GUI/Button/Button.pde b/processing/mode/examples/Topics/GUI/Button/Button.pde similarity index 100% rename from mode/examples/Topics/GUI/Button/Button.pde rename to processing/mode/examples/Topics/GUI/Button/Button.pde diff --git a/mode/examples/Topics/GUI/Buttons/Buttons.pde b/processing/mode/examples/Topics/GUI/Buttons/Buttons.pde similarity index 100% rename from mode/examples/Topics/GUI/Buttons/Buttons.pde rename to processing/mode/examples/Topics/GUI/Buttons/Buttons.pde diff --git a/mode/examples/Topics/GUI/Handles/Handles.pde b/processing/mode/examples/Topics/GUI/Handles/Handles.pde similarity index 100% rename from mode/examples/Topics/GUI/Handles/Handles.pde rename to processing/mode/examples/Topics/GUI/Handles/Handles.pde diff --git a/mode/examples/Topics/GUI/ImageButton/ImageButton.pde b/processing/mode/examples/Topics/GUI/ImageButton/ImageButton.pde similarity index 100% rename from mode/examples/Topics/GUI/ImageButton/ImageButton.pde rename to processing/mode/examples/Topics/GUI/ImageButton/ImageButton.pde diff --git a/mode/examples/Topics/GUI/ImageButton/data/base.gif b/processing/mode/examples/Topics/GUI/ImageButton/data/base.gif similarity index 100% rename from mode/examples/Topics/GUI/ImageButton/data/base.gif rename to processing/mode/examples/Topics/GUI/ImageButton/data/base.gif diff --git a/mode/examples/Topics/GUI/ImageButton/data/down.gif b/processing/mode/examples/Topics/GUI/ImageButton/data/down.gif similarity index 100% rename from mode/examples/Topics/GUI/ImageButton/data/down.gif rename to processing/mode/examples/Topics/GUI/ImageButton/data/down.gif diff --git a/mode/examples/Topics/GUI/ImageButton/data/roll.gif b/processing/mode/examples/Topics/GUI/ImageButton/data/roll.gif similarity index 100% rename from mode/examples/Topics/GUI/ImageButton/data/roll.gif rename to processing/mode/examples/Topics/GUI/ImageButton/data/roll.gif diff --git a/mode/examples/Topics/GUI/Rollover/Rollover.pde b/processing/mode/examples/Topics/GUI/Rollover/Rollover.pde similarity index 100% rename from mode/examples/Topics/GUI/Rollover/Rollover.pde rename to processing/mode/examples/Topics/GUI/Rollover/Rollover.pde diff --git a/mode/examples/Topics/GUI/Scrollbar/Scrollbar.pde b/processing/mode/examples/Topics/GUI/Scrollbar/Scrollbar.pde similarity index 100% rename from mode/examples/Topics/GUI/Scrollbar/Scrollbar.pde rename to processing/mode/examples/Topics/GUI/Scrollbar/Scrollbar.pde diff --git a/mode/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg b/processing/mode/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg similarity index 100% rename from mode/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg rename to processing/mode/examples/Topics/GUI/Scrollbar/data/seedBottom.jpg diff --git a/mode/examples/Topics/GUI/Scrollbar/data/seedTop.jpg b/processing/mode/examples/Topics/GUI/Scrollbar/data/seedTop.jpg similarity index 100% rename from mode/examples/Topics/GUI/Scrollbar/data/seedTop.jpg rename to processing/mode/examples/Topics/GUI/Scrollbar/data/seedTop.jpg diff --git a/mode/examples/Topics/Geometry/Icosahedra/Dimension3D.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Dimension3D.pde similarity index 100% rename from mode/examples/Topics/Geometry/Icosahedra/Dimension3D.pde rename to processing/mode/examples/Topics/Geometry/Icosahedra/Dimension3D.pde diff --git a/mode/examples/Topics/Geometry/Icosahedra/Icosahedra.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedra.pde similarity index 100% rename from mode/examples/Topics/Geometry/Icosahedra/Icosahedra.pde rename to processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedra.pde diff --git a/mode/examples/Topics/Geometry/Icosahedra/Icosahedron.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedron.pde similarity index 100% rename from mode/examples/Topics/Geometry/Icosahedra/Icosahedron.pde rename to processing/mode/examples/Topics/Geometry/Icosahedra/Icosahedron.pde diff --git a/mode/examples/Topics/Geometry/Icosahedra/Shape3D.pde b/processing/mode/examples/Topics/Geometry/Icosahedra/Shape3D.pde similarity index 100% rename from mode/examples/Topics/Geometry/Icosahedra/Shape3D.pde rename to processing/mode/examples/Topics/Geometry/Icosahedra/Shape3D.pde diff --git a/mode/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde b/processing/mode/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde similarity index 100% rename from mode/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde rename to processing/mode/examples/Topics/Geometry/NoiseSphere/NoiseSphere.pde diff --git a/mode/examples/Topics/Geometry/RGBCube/RGBCube.pde b/processing/mode/examples/Topics/Geometry/RGBCube/RGBCube.pde similarity index 100% rename from mode/examples/Topics/Geometry/RGBCube/RGBCube.pde rename to processing/mode/examples/Topics/Geometry/RGBCube/RGBCube.pde diff --git a/mode/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde b/processing/mode/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde similarity index 100% rename from mode/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde rename to processing/mode/examples/Topics/Geometry/ShapeTransform/ShapeTransform.pde diff --git a/mode/examples/Topics/Geometry/SpaceJunk/Cube.pde b/processing/mode/examples/Topics/Geometry/SpaceJunk/Cube.pde similarity index 100% rename from mode/examples/Topics/Geometry/SpaceJunk/Cube.pde rename to processing/mode/examples/Topics/Geometry/SpaceJunk/Cube.pde diff --git a/mode/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde b/processing/mode/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde similarity index 100% rename from mode/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde rename to processing/mode/examples/Topics/Geometry/SpaceJunk/SpaceJunk.pde diff --git a/mode/examples/Topics/Geometry/Toroid/Toroid.pde b/processing/mode/examples/Topics/Geometry/Toroid/Toroid.pde similarity index 100% rename from mode/examples/Topics/Geometry/Toroid/Toroid.pde rename to processing/mode/examples/Topics/Geometry/Toroid/Toroid.pde diff --git a/mode/examples/Topics/Geometry/Vertices/Vertices.pde b/processing/mode/examples/Topics/Geometry/Vertices/Vertices.pde similarity index 100% rename from mode/examples/Topics/Geometry/Vertices/Vertices.pde rename to processing/mode/examples/Topics/Geometry/Vertices/Vertices.pde diff --git a/mode/examples/Topics/Image Processing/Blur/Blur.pde b/processing/mode/examples/Topics/Image Processing/Blur/Blur.pde similarity index 100% rename from mode/examples/Topics/Image Processing/Blur/Blur.pde rename to processing/mode/examples/Topics/Image Processing/Blur/Blur.pde diff --git a/mode/examples/Topics/Image Processing/Blur/data/trees.jpg b/processing/mode/examples/Topics/Image Processing/Blur/data/trees.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/Blur/data/trees.jpg rename to processing/mode/examples/Topics/Image Processing/Blur/data/trees.jpg diff --git a/mode/examples/Topics/Image Processing/Brightness/Brightness.pde b/processing/mode/examples/Topics/Image Processing/Brightness/Brightness.pde similarity index 100% rename from mode/examples/Topics/Image Processing/Brightness/Brightness.pde rename to processing/mode/examples/Topics/Image Processing/Brightness/Brightness.pde diff --git a/mode/examples/Topics/Image Processing/Brightness/data/wires.jpg b/processing/mode/examples/Topics/Image Processing/Brightness/data/wires.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/Brightness/data/wires.jpg rename to processing/mode/examples/Topics/Image Processing/Brightness/data/wires.jpg diff --git a/mode/examples/Topics/Image Processing/Convolution/Convolution.pde b/processing/mode/examples/Topics/Image Processing/Convolution/Convolution.pde similarity index 100% rename from mode/examples/Topics/Image Processing/Convolution/Convolution.pde rename to processing/mode/examples/Topics/Image Processing/Convolution/Convolution.pde diff --git a/mode/examples/Topics/Image Processing/Convolution/data/end.jpg b/processing/mode/examples/Topics/Image Processing/Convolution/data/end.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/Convolution/data/end.jpg rename to processing/mode/examples/Topics/Image Processing/Convolution/data/end.jpg diff --git a/mode/examples/Topics/Image Processing/Convolution/data/sunflower.jpg b/processing/mode/examples/Topics/Image Processing/Convolution/data/sunflower.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/Convolution/data/sunflower.jpg rename to processing/mode/examples/Topics/Image Processing/Convolution/data/sunflower.jpg diff --git a/mode/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde b/processing/mode/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde similarity index 100% rename from mode/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde rename to processing/mode/examples/Topics/Image Processing/EdgeDetection/EdgeDetection.pde diff --git a/mode/examples/Topics/Image Processing/EdgeDetection/data/house.jpg b/processing/mode/examples/Topics/Image Processing/EdgeDetection/data/house.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/EdgeDetection/data/house.jpg rename to processing/mode/examples/Topics/Image Processing/EdgeDetection/data/house.jpg diff --git a/mode/examples/Topics/Image Processing/Histogram/Histogram.pde b/processing/mode/examples/Topics/Image Processing/Histogram/Histogram.pde similarity index 100% rename from mode/examples/Topics/Image Processing/Histogram/Histogram.pde rename to processing/mode/examples/Topics/Image Processing/Histogram/Histogram.pde diff --git a/mode/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 mode/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/mode/examples/Topics/Image Processing/Histogram/data/ystone08.jpg b/processing/mode/examples/Topics/Image Processing/Histogram/data/ystone08.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/Histogram/data/ystone08.jpg rename to processing/mode/examples/Topics/Image Processing/Histogram/data/ystone08.jpg diff --git a/mode/examples/Topics/Image Processing/LinearImage/LinearImage.pde b/processing/mode/examples/Topics/Image Processing/LinearImage/LinearImage.pde similarity index 100% rename from mode/examples/Topics/Image Processing/LinearImage/LinearImage.pde rename to processing/mode/examples/Topics/Image Processing/LinearImage/LinearImage.pde diff --git a/mode/examples/Topics/Image Processing/LinearImage/data/florence03.jpg b/processing/mode/examples/Topics/Image Processing/LinearImage/data/florence03.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/LinearImage/data/florence03.jpg rename to processing/mode/examples/Topics/Image Processing/LinearImage/data/florence03.jpg diff --git a/mode/examples/Topics/Image Processing/PixelArray/PixelArray.pde b/processing/mode/examples/Topics/Image Processing/PixelArray/PixelArray.pde similarity index 100% rename from mode/examples/Topics/Image Processing/PixelArray/PixelArray.pde rename to processing/mode/examples/Topics/Image Processing/PixelArray/PixelArray.pde diff --git a/mode/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg b/processing/mode/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg similarity index 100% rename from mode/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg rename to processing/mode/examples/Topics/Image Processing/PixelArray/data/ystone08.jpg diff --git a/mode/examples/Topics/Interaction/Follow1/Follow1.pde b/processing/mode/examples/Topics/Interaction/Follow1/Follow1.pde similarity index 100% rename from mode/examples/Topics/Interaction/Follow1/Follow1.pde rename to processing/mode/examples/Topics/Interaction/Follow1/Follow1.pde diff --git a/mode/examples/Topics/Interaction/Follow2/Follow2.pde b/processing/mode/examples/Topics/Interaction/Follow2/Follow2.pde similarity index 100% rename from mode/examples/Topics/Interaction/Follow2/Follow2.pde rename to processing/mode/examples/Topics/Interaction/Follow2/Follow2.pde diff --git a/mode/examples/Topics/Interaction/Follow3/Follow3.pde b/processing/mode/examples/Topics/Interaction/Follow3/Follow3.pde similarity index 100% rename from mode/examples/Topics/Interaction/Follow3/Follow3.pde rename to processing/mode/examples/Topics/Interaction/Follow3/Follow3.pde diff --git a/mode/examples/Topics/Interaction/Multitouch/Multitouch.pde b/processing/mode/examples/Topics/Interaction/Multitouch/Multitouch.pde similarity index 100% rename from mode/examples/Topics/Interaction/Multitouch/Multitouch.pde rename to processing/mode/examples/Topics/Interaction/Multitouch/Multitouch.pde diff --git a/mode/examples/Topics/Interaction/Reach1/Reach1.pde b/processing/mode/examples/Topics/Interaction/Reach1/Reach1.pde similarity index 100% rename from mode/examples/Topics/Interaction/Reach1/Reach1.pde rename to processing/mode/examples/Topics/Interaction/Reach1/Reach1.pde diff --git a/mode/examples/Topics/Interaction/Reach2/Reach2.pde b/processing/mode/examples/Topics/Interaction/Reach2/Reach2.pde similarity index 100% rename from mode/examples/Topics/Interaction/Reach2/Reach2.pde rename to processing/mode/examples/Topics/Interaction/Reach2/Reach2.pde diff --git a/mode/examples/Topics/Interaction/Reach3/Reach3.pde b/processing/mode/examples/Topics/Interaction/Reach3/Reach3.pde similarity index 100% rename from mode/examples/Topics/Interaction/Reach3/Reach3.pde rename to processing/mode/examples/Topics/Interaction/Reach3/Reach3.pde diff --git a/mode/examples/Topics/Interaction/Tickle/Tickle.pde b/processing/mode/examples/Topics/Interaction/Tickle/Tickle.pde similarity index 100% rename from mode/examples/Topics/Interaction/Tickle/Tickle.pde rename to processing/mode/examples/Topics/Interaction/Tickle/Tickle.pde diff --git a/mode/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw b/processing/mode/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw similarity index 100% rename from mode/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw rename to processing/mode/examples/Topics/Interaction/Tickle/data/AmericanTypewriter-24.vlw diff --git a/mode/examples/Topics/Motion/Bounce/Bounce.pde b/processing/mode/examples/Topics/Motion/Bounce/Bounce.pde similarity index 100% rename from mode/examples/Topics/Motion/Bounce/Bounce.pde rename to processing/mode/examples/Topics/Motion/Bounce/Bounce.pde diff --git a/mode/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde b/processing/mode/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde similarity index 100% rename from mode/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde rename to processing/mode/examples/Topics/Motion/BouncyBubbles/BouncyBubbles.pde diff --git a/mode/examples/Topics/Motion/Brownian/Brownian.pde b/processing/mode/examples/Topics/Motion/Brownian/Brownian.pde similarity index 100% rename from mode/examples/Topics/Motion/Brownian/Brownian.pde rename to processing/mode/examples/Topics/Motion/Brownian/Brownian.pde diff --git a/mode/examples/Topics/Motion/CircleCollision/Ball.pde b/processing/mode/examples/Topics/Motion/CircleCollision/Ball.pde similarity index 100% rename from mode/examples/Topics/Motion/CircleCollision/Ball.pde rename to processing/mode/examples/Topics/Motion/CircleCollision/Ball.pde diff --git a/mode/examples/Topics/Motion/CircleCollision/CircleCollision.pde b/processing/mode/examples/Topics/Motion/CircleCollision/CircleCollision.pde similarity index 100% rename from mode/examples/Topics/Motion/CircleCollision/CircleCollision.pde rename to processing/mode/examples/Topics/Motion/CircleCollision/CircleCollision.pde diff --git a/mode/examples/Topics/Motion/Collision/Collision.pde b/processing/mode/examples/Topics/Motion/Collision/Collision.pde similarity index 100% rename from mode/examples/Topics/Motion/Collision/Collision.pde rename to processing/mode/examples/Topics/Motion/Collision/Collision.pde diff --git a/mode/examples/Topics/Motion/Linear/Linear.pde b/processing/mode/examples/Topics/Motion/Linear/Linear.pde similarity index 100% rename from mode/examples/Topics/Motion/Linear/Linear.pde rename to processing/mode/examples/Topics/Motion/Linear/Linear.pde diff --git a/mode/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde b/processing/mode/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde similarity index 100% rename from mode/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde rename to processing/mode/examples/Topics/Motion/MovingOnCurves/MovingOnCurves.pde diff --git a/mode/examples/Topics/Motion/Puff/Puff.pde b/processing/mode/examples/Topics/Motion/Puff/Puff.pde similarity index 100% rename from mode/examples/Topics/Motion/Puff/Puff.pde rename to processing/mode/examples/Topics/Motion/Puff/Puff.pde diff --git a/mode/examples/Topics/Motion/Reflection1/Reflection1.pde b/processing/mode/examples/Topics/Motion/Reflection1/Reflection1.pde similarity index 100% rename from mode/examples/Topics/Motion/Reflection1/Reflection1.pde rename to processing/mode/examples/Topics/Motion/Reflection1/Reflection1.pde diff --git a/mode/examples/Topics/Motion/Reflection2/Ground.pde b/processing/mode/examples/Topics/Motion/Reflection2/Ground.pde similarity index 100% rename from mode/examples/Topics/Motion/Reflection2/Ground.pde rename to processing/mode/examples/Topics/Motion/Reflection2/Ground.pde diff --git a/mode/examples/Topics/Motion/Reflection2/Orb.pde b/processing/mode/examples/Topics/Motion/Reflection2/Orb.pde similarity index 100% rename from mode/examples/Topics/Motion/Reflection2/Orb.pde rename to processing/mode/examples/Topics/Motion/Reflection2/Orb.pde diff --git a/mode/examples/Topics/Motion/Reflection2/Reflection2.pde b/processing/mode/examples/Topics/Motion/Reflection2/Reflection2.pde similarity index 100% rename from mode/examples/Topics/Motion/Reflection2/Reflection2.pde rename to processing/mode/examples/Topics/Motion/Reflection2/Reflection2.pde diff --git a/mode/examples/Topics/Shaders/BlurFilter/BlurFilter.pde b/processing/mode/examples/Topics/Shaders/BlurFilter/BlurFilter.pde similarity index 100% rename from mode/examples/Topics/Shaders/BlurFilter/BlurFilter.pde rename to processing/mode/examples/Topics/Shaders/BlurFilter/BlurFilter.pde diff --git a/studio/apps/fast2d/src/main/assets/edges.glsl b/processing/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl similarity index 82% rename from studio/apps/fast2d/src/main/assets/edges.glsl rename to processing/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl index 62109e3e3..2aa9d6079 100644 --- a/studio/apps/fast2d/src/main/assets/edges.glsl +++ b/processing/mode/examples/Topics/Shaders/BlurFilter/data/blur.glsl @@ -24,7 +24,7 @@ void main(void) { 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); @@ -35,6 +35,8 @@ void main(void) { vec4 col7 = texture2D(texture, tc7); vec4 col8 = texture2D(texture, tc8); - vec4 sum = 8.0 * col4 - (col0 + col1 + col2 + col3 + col5 + col6 + col7 + col8); - gl_FragColor = vec4(sum.rgb, 1.0) * vertColor; + 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/mode/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde b/processing/mode/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde similarity index 100% rename from mode/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde rename to processing/mode/examples/Topics/Shaders/EdgeDetect/EdgeDetect.pde diff --git a/mode/examples/Topics/Shaders/EdgeDetect/data/edges.glsl b/processing/mode/examples/Topics/Shaders/EdgeDetect/data/edges.glsl similarity index 100% rename from mode/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/mode/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde b/processing/mode/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde similarity index 100% rename from mode/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde rename to processing/mode/examples/Topics/Shaders/EdgeFilter/EdgeFilter.pde diff --git a/mode/examples/Topics/Shaders/EdgeFilter/data/edges.glsl b/processing/mode/examples/Topics/Shaders/EdgeFilter/data/edges.glsl similarity index 100% rename from mode/examples/Topics/Shaders/EdgeFilter/data/edges.glsl rename to processing/mode/examples/Topics/Shaders/EdgeFilter/data/edges.glsl diff --git a/mode/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde b/processing/mode/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde similarity index 100% rename from mode/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde rename to processing/mode/examples/Topics/Shaders/LowLevelGL/LowLevelGL.pde diff --git a/mode/examples/Topics/Shaders/LowLevelGL/data/frag.glsl b/processing/mode/examples/Topics/Shaders/LowLevelGL/data/frag.glsl similarity index 100% rename from mode/examples/Topics/Shaders/LowLevelGL/data/frag.glsl rename to processing/mode/examples/Topics/Shaders/LowLevelGL/data/frag.glsl diff --git a/mode/examples/Topics/Shaders/LowLevelGL/data/vert.glsl b/processing/mode/examples/Topics/Shaders/LowLevelGL/data/vert.glsl similarity index 100% rename from mode/examples/Topics/Shaders/LowLevelGL/data/vert.glsl rename to processing/mode/examples/Topics/Shaders/LowLevelGL/data/vert.glsl diff --git a/mode/examples/Topics/Shaders/ToonShading/ToonShading.pde b/processing/mode/examples/Topics/Shaders/ToonShading/ToonShading.pde similarity index 100% rename from mode/examples/Topics/Shaders/ToonShading/ToonShading.pde rename to processing/mode/examples/Topics/Shaders/ToonShading/ToonShading.pde diff --git a/mode/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl b/processing/mode/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl similarity index 100% rename from mode/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl rename to processing/mode/examples/Topics/Shaders/ToonShading/data/ToonFrag.glsl diff --git a/mode/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl b/processing/mode/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl similarity index 100% rename from mode/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl rename to processing/mode/examples/Topics/Shaders/ToonShading/data/ToonVert.glsl diff --git a/mode/examples/Topics/Simulate/Chain/Chain.pde b/processing/mode/examples/Topics/Simulate/Chain/Chain.pde similarity index 100% rename from mode/examples/Topics/Simulate/Chain/Chain.pde rename to processing/mode/examples/Topics/Simulate/Chain/Chain.pde diff --git a/mode/examples/Topics/Simulate/Flocking/Boid.pde b/processing/mode/examples/Topics/Simulate/Flocking/Boid.pde similarity index 100% rename from mode/examples/Topics/Simulate/Flocking/Boid.pde rename to processing/mode/examples/Topics/Simulate/Flocking/Boid.pde diff --git a/mode/examples/Topics/Simulate/Flocking/Flock.pde b/processing/mode/examples/Topics/Simulate/Flocking/Flock.pde similarity index 100% rename from mode/examples/Topics/Simulate/Flocking/Flock.pde rename to processing/mode/examples/Topics/Simulate/Flocking/Flock.pde diff --git a/mode/examples/Topics/Simulate/Flocking/Flocking.pde b/processing/mode/examples/Topics/Simulate/Flocking/Flocking.pde similarity index 100% rename from mode/examples/Topics/Simulate/Flocking/Flocking.pde rename to processing/mode/examples/Topics/Simulate/Flocking/Flocking.pde diff --git a/mode/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde b/processing/mode/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde similarity index 100% rename from mode/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde rename to processing/mode/examples/Topics/Simulate/ForcesWithVectors/ForcesWithVectors.pde diff --git a/mode/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde b/processing/mode/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde similarity index 100% rename from mode/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde rename to processing/mode/examples/Topics/Simulate/ForcesWithVectors/Liquid.pde diff --git a/mode/examples/Topics/Simulate/ForcesWithVectors/Mover.pde b/processing/mode/examples/Topics/Simulate/ForcesWithVectors/Mover.pde similarity index 100% rename from mode/examples/Topics/Simulate/ForcesWithVectors/Mover.pde rename to processing/mode/examples/Topics/Simulate/ForcesWithVectors/Mover.pde diff --git a/mode/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde b/processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde similarity index 100% rename from mode/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde rename to processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/GravitationalAttraction3D.pde diff --git a/mode/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde b/processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde similarity index 100% rename from mode/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde rename to processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Planet.pde diff --git a/mode/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde b/processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde similarity index 100% rename from mode/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde rename to processing/mode/examples/Topics/Simulate/GravitationalAttraction3D/Sun.pde diff --git a/mode/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde similarity index 100% rename from mode/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/CrazyParticle.pde diff --git a/mode/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde similarity index 100% rename from mode/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/MultipleParticleSystems.pde diff --git a/mode/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde similarity index 100% rename from mode/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/Particle.pde diff --git a/mode/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde b/processing/mode/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde similarity index 100% rename from mode/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde rename to processing/mode/examples/Topics/Simulate/MultipleParticleSystems/ParticleSystem.pde diff --git a/mode/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde b/processing/mode/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde similarity index 100% rename from mode/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde rename to processing/mode/examples/Topics/Simulate/SimpleParticleSystem/Particle.pde diff --git a/mode/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde similarity index 100% rename from mode/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde rename to processing/mode/examples/Topics/Simulate/SimpleParticleSystem/ParticleSystem.pde diff --git a/mode/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde similarity index 100% rename from mode/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde rename to processing/mode/examples/Topics/Simulate/SimpleParticleSystem/SimpleParticleSystem.pde diff --git a/mode/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde similarity index 100% rename from mode/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/Particle.pde diff --git a/mode/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde similarity index 100% rename from mode/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/ParticleSystem.pde diff --git a/mode/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde similarity index 100% rename from mode/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/SmokeParticleSystem.pde diff --git a/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif similarity index 100% rename from mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.gif diff --git a/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png b/processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png similarity index 100% rename from mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png rename to processing/mode/examples/Topics/Simulate/SmokeParticleSystem/data/texture.png diff --git a/mode/examples/Topics/Simulate/SoftBody/SoftBody.pde b/processing/mode/examples/Topics/Simulate/SoftBody/SoftBody.pde similarity index 100% rename from mode/examples/Topics/Simulate/SoftBody/SoftBody.pde rename to processing/mode/examples/Topics/Simulate/SoftBody/SoftBody.pde diff --git a/mode/examples/Topics/Simulate/Spring/Spring.pde b/processing/mode/examples/Topics/Simulate/Spring/Spring.pde similarity index 100% rename from mode/examples/Topics/Simulate/Spring/Spring.pde rename to processing/mode/examples/Topics/Simulate/Spring/Spring.pde diff --git a/mode/examples/Topics/Simulate/Springs/Springs.pde b/processing/mode/examples/Topics/Simulate/Springs/Springs.pde similarity index 100% rename from mode/examples/Topics/Simulate/Springs/Springs.pde rename to processing/mode/examples/Topics/Simulate/Springs/Springs.pde diff --git a/mode/examples/Topics/Textures/TextureCube/TextureCube.pde b/processing/mode/examples/Topics/Textures/TextureCube/TextureCube.pde similarity index 100% rename from mode/examples/Topics/Textures/TextureCube/TextureCube.pde rename to processing/mode/examples/Topics/Textures/TextureCube/TextureCube.pde diff --git a/mode/examples/Topics/Textures/TextureCube/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureCube/data/berlin-1.jpg similarity index 100% rename from mode/examples/Topics/Textures/TextureCube/data/berlin-1.jpg rename to processing/mode/examples/Topics/Textures/TextureCube/data/berlin-1.jpg diff --git a/mode/examples/Topics/Textures/TextureCube/data/uvtex.jpg b/processing/mode/examples/Topics/Textures/TextureCube/data/uvtex.jpg similarity index 100% rename from mode/examples/Topics/Textures/TextureCube/data/uvtex.jpg rename to processing/mode/examples/Topics/Textures/TextureCube/data/uvtex.jpg diff --git a/mode/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde b/processing/mode/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde similarity index 100% rename from mode/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde rename to processing/mode/examples/Topics/Textures/TextureCylinder/TextureCylinder.pde diff --git a/mode/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg similarity index 100% rename from mode/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg rename to processing/mode/examples/Topics/Textures/TextureCylinder/data/berlin-1.jpg diff --git a/mode/examples/Topics/Textures/TextureQuad/TextureQuad.pde b/processing/mode/examples/Topics/Textures/TextureQuad/TextureQuad.pde similarity index 100% rename from mode/examples/Topics/Textures/TextureQuad/TextureQuad.pde rename to processing/mode/examples/Topics/Textures/TextureQuad/TextureQuad.pde diff --git a/mode/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg similarity index 100% rename from mode/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg rename to processing/mode/examples/Topics/Textures/TextureQuad/data/berlin-1.jpg diff --git a/mode/examples/Topics/Textures/TextureSphere/TextureSphere.pde b/processing/mode/examples/Topics/Textures/TextureSphere/TextureSphere.pde similarity index 100% rename from mode/examples/Topics/Textures/TextureSphere/TextureSphere.pde rename to processing/mode/examples/Topics/Textures/TextureSphere/TextureSphere.pde diff --git a/mode/examples/Topics/Textures/TextureSphere/data/world32k.jpg b/processing/mode/examples/Topics/Textures/TextureSphere/data/world32k.jpg similarity index 100% rename from mode/examples/Topics/Textures/TextureSphere/data/world32k.jpg rename to processing/mode/examples/Topics/Textures/TextureSphere/data/world32k.jpg diff --git a/mode/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde b/processing/mode/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde similarity index 100% rename from mode/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde rename to processing/mode/examples/Topics/Textures/TextureTriangle/TextureTriangle.pde diff --git a/mode/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg b/processing/mode/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg similarity index 100% rename from mode/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg rename to processing/mode/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg diff --git a/mode/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde b/processing/mode/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde similarity index 100% rename from mode/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde rename to processing/mode/examples/Topics/Vectors/AccelerationWithVectors/AccelerationWithVectors.pde diff --git a/mode/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde b/processing/mode/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde similarity index 100% rename from mode/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde rename to processing/mode/examples/Topics/Vectors/AccelerationWithVectors/Mover.pde diff --git a/mode/examples/Topics/Vectors/BouncingBall/BouncingBall.pde b/processing/mode/examples/Topics/Vectors/BouncingBall/BouncingBall.pde similarity index 100% rename from mode/examples/Topics/Vectors/BouncingBall/BouncingBall.pde rename to processing/mode/examples/Topics/Vectors/BouncingBall/BouncingBall.pde diff --git a/mode/examples/Topics/Vectors/Normalize/Normalize.pde b/processing/mode/examples/Topics/Vectors/Normalize/Normalize.pde similarity index 100% rename from mode/examples/Topics/Vectors/Normalize/Normalize.pde rename to processing/mode/examples/Topics/Vectors/Normalize/Normalize.pde diff --git a/mode/examples/Topics/Vectors/VectorMath/VectorMath.pde b/processing/mode/examples/Topics/Vectors/VectorMath/VectorMath.pde similarity index 100% rename from mode/examples/Topics/Vectors/VectorMath/VectorMath.pde rename to processing/mode/examples/Topics/Vectors/VectorMath/VectorMath.pde diff --git a/mode/examples/Topics/Wallpapers/Circles/Circles.pde b/processing/mode/examples/Topics/Wallpapers/Circles/Circles.pde similarity index 100% rename from mode/examples/Topics/Wallpapers/Circles/Circles.pde rename to processing/mode/examples/Topics/Wallpapers/Circles/Circles.pde diff --git a/mode/examples/Topics/Wallpapers/Circles/code/sketch.properties b/processing/mode/examples/Topics/Wallpapers/Circles/code/sketch.properties similarity index 100% rename from mode/examples/Topics/Wallpapers/Circles/code/sketch.properties rename to processing/mode/examples/Topics/Wallpapers/Circles/code/sketch.properties diff --git a/mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde b/processing/mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde similarity index 100% rename from mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde rename to processing/mode/examples/Topics/Watchfaces/WatchFace/WatchFace.pde diff --git a/mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties b/processing/mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties similarity index 100% rename from mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties rename to processing/mode/examples/Topics/Watchfaces/WatchFace/code/sketch.properties 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 = 请填写以下信息,以便我们为您生成一个私密密钥。
    加粗的字段为必填项,不过您可能要考虑在下面的可选字段中填写一些内容,以避免潜在的问题。
    有关私密密钥的更多信息可以在此处找到。 +keystore_manager.reset_password = 重置密码 +keystore_manager.dialog.reset_keyboard_title = 重置密码 +keystore_manager.dialog.reset_keyboard_body_part1 = 您确定要重置密码吗? +keystore_manager.dialog.reset_keyboard_body_part2 = 为了进行此操作,我们将必须重置密钥库,这意味着您将无法上传使用新密钥库签名的应用程序更新到Google Play。

    我们会为旧密钥库创建一个备份。 +keystore_manager.warn.cannot_remove_keystore_title = Android密钥库 +keystore_manager.warn.cannot_remove_keystore_body = 无法删除密钥库 +keystore_manager.warn.password_missmatch_title = 密码 +keystore_manager.warn.password_missmatch_body = 密钥库密码不匹配 +keystore_manager.warn.short_password_title = 密码 +keystore_manager.warn.short_password_body = 密钥库密码应至少为6个字符 +keystore_manager.password_label = 密钥库密码: +keystore_manager.repeat_password_label = 重复输入密钥库密码: +keystore_manager.issuer_credentials_header = 密钥库颁发者凭据 +keystore_manager.common_name_label = 名称: +keystore_manager.organizational_unitl_label = 组织单位: +keystore_manager.organization_name_label = 组织名称: +keystore_manager.city_name_label = 城市或地区: +keystore_manager.state_name_label = 州省名: +keystore_manager.country_code_label = 国家代码(XX): + +# --------------------------------------- +# Manifest + +manifest.warn.cannot_handle_file_title = 处理%s时出错 +manifest.warn.cannot_handle_file_body = 在读取或写入%s时发生错误,这意味着很多东西可能无法正常工作。
    为了防止数据丢失,建议您使用“另存为”
    保存您的sketch的单独副本,然后重新启动Processing。 + +# --------------------------------------- +# Permissions + +permissions.dialog.label = Android应用程序必须明确请求权限
    进行诸如连接到互联网、写入文件或拨打电话等操作。
    在安装您的应用程序时,用户将被询问是否允许这样的访问。 +permissions.dialog.url = 有关权限的更多信息可以在此处找到。 + +# --------------------------------------- +# SDK Downloader + +sdk_downloader.error_cannot_find_platform_files = 找不到平台文件 +sdk_downloader.error_cannot_find_platform_tools = 找不到平台工具 +sdk_downloader.error_cannot_find_build_tools = 找不到构建工具 +sdk_downloader.error_cannot_find_tools = 找不到工具 +sdk_downloader.error_cannot_find_emulator = 找不到模拟器 +sdk_downloader.error.cannot_unpack_platform = 解压平台至“%s”时出错 +sdk_downloader.download_title = SDK下载 +sdk_downloader.download_sdk_label = 正在下载Android SDK… + +# --------------------------------------- +# System image downloader + +sys_image_downloader.dialog.select_image_title = 选择要下载的系统镜像类型… +sys_image_downloader.dialog.select_image_body = Android模拟器需要系统镜像才能运行。有两种系统镜像可用:

    1) ARM镜像——速度慢,但兼容所有计算机,没有额外配置需要。

    2) x86镜像——速度快,但仅兼容Intel CPU,可能需要额外的配置,请参阅此指南了解更多详细信息。 +sys_image_downloader.dialog.accel_images_title = 几点忠告… +sys_image_downloader.dialog.haxm_install_body = Processing将在模拟器中安装x86映像。这些映像很快,但也需要Intel的硬件加速执行管理器(Intel HAXM)。

    Processing现在将尝试运行HAXM安装程序,该程序可能会要求您输入管理员密码或其他权限。 +sys_image_downloader.dialog.kvm_config_body = 您选择在模拟器中运行x86映像。这很棒,但您需要在Linux上使用KVM包配置VM加速。

    请按照这些说明配置KVM。 +sys_image_downloader.dialog.ia32libs_title = 可能需要进行其他设置… +sys_image_downloader.dialog.ia32libs_body = 看起来您正在运行64位版本的Linux。为了
    在模拟器中创建SD卡,Processing需要
    ia32-libs兼容性包。在Ubuntu Linux上,您可以
    通过运行以下命令安装它:

    sudo apt-get install lib32stdc++6 +sys_image_downloader.option.x86_image = 使用x86镜像 +sys_image_downloader.option.arm_image = 使用ARM镜像 +sys_image_downloader.download_title = 系统镜像下载 +sys_image_downloader.download_watch_label = 下载手表系统镜像… +sys_image_downloader.download_phone_label = 下载手机系统镜像… + +# --------------------------------------- +# Download strings + +download_property.change_event_total = 总计 +download_property.change_event_downloaded = 已下载 +download_prompt.cancel = 取消下载 + +# --------------------------------------- +# SDK Updater tool + +sdk_updater.name_column = 包名称 +sdk_updater.version_column = 已安装版本 +sdk_updater.available_column = 可用更新 + +sdk_updater.query_message = 正在查询套件… + +sdk_updater.no_updates_message = 没有可用的更新 +sdk_updater.one_updates_message = 发现1个更新! +sdk_updater.many_updates_message = 发现“%d”个更新! + +sdk_updater.warning_failed_finding_package = 无法找到包“%s” +sdk_updater.warning_failed_computing_dependency_list = 无法计算完整的依赖关系列表。 + +sdk_updater.refresh_package_message = 正在刷新套件… +sdk_updater.download_package_message = 正在下载可用更新… +sdk_updater.download_canceled_message = 下载已取消 + +sdk_updater.update_button_label = 更新 +sdk_updater.cancel_button_label = 取消 +sdk_updater.close_button_label = 关闭 diff --git a/processing/mode/libraries/ar/README.md b/processing/mode/libraries/ar/README.md new file mode 100644 index 000000000..8dddfce92 --- /dev/null +++ b/processing/mode/libraries/ar/README.md @@ -0,0 +1,21 @@ +![Image](imgs/bg_1.png) + +# AR library for Processing-Android + +![Android](https://img.shields.io/badge/platform-Android-green.svg?longCache=true&style=for-the-badge) ![ARCore](https://img.shields.io/badge/ARCore-v1.2.0-blue.svg?longCache=true&style=for-the-badge) ![In Progress](https://img.shields.io/badge/in--progress-true-green.svg?longCache=true&style=for-the-badge)
    +This library includes ARCore renderer to create AR apps using Processing. + +## Steps to build: +* Make sure you have both [Processing](https://github.com/processing/processing) and [Processing-Android](https://github.com/processing/processing-android) built before you proceed.
    +* For Building [Processing-Android](https://github.com/processing/processing-android) refer [Wiki](https://github.com/processing/processing-android/wiki/Building-Processing-for-Android).
    +* Once built, clone [processing-ar](https://github.com/SyamSundarKirubakaran/processing-ar) into the `Libraries` Directory right next to `vr` Directory.
    +NOTE: Rename the cloned directory as `ar` and the name of the module to be `processing-ar`.
    +* Make sure to import `processing-ar` as a module in your IDE.
    +* Build it using the `ant` command through terminal and on Successful build, you'll see `ar.jar` file appear under `libraries/ar/library`.
    +* Once built, hit Run. You'll see `AR` appear under `Sketch -> Import Library... -> AR`.
    +* On clicking it, you'll get an import to the AR Library as `import processing.ar.*;`.
    + +## Working: +

    + +

    \ No newline at end of file diff --git a/processing/mode/libraries/ar/build.gradle b/processing/mode/libraries/ar/build.gradle new file mode 100644 index 000000000..5b47b90b4 --- /dev/null +++ b/processing/mode/libraries/ar/build.gradle @@ -0,0 +1,123 @@ +import java.nio.file.Files +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +plugins { + id 'java-library' + id 'maven-publish' +} + +dependencies { + compileOnly name: "android" + compileOnly "org.p5android:processing-core:${modeVersion}" + implementation "com.google.ar:core:${garVersion}" +} + +sourceSets { + main { + java.srcDir("../../../../libs/processing-ar/src/main/java/") + resources { + srcDir("../../../../libs/processing-ar/src/main/") + exclude "AndroidManifest.xml" + exclude "**/java/**" + } + } +} + +java { + withSourcesJar() +} + +tasks.named('jar') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} + +tasks.register("sourceJar", Jar) { + from sourceSets.main.allJava + archiveClassifier.set("sources") +} + +// 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 +} + +artifacts { +// archives javadocJar + archives sourceJar +} + +tasks.jar { + doLast { + ant.checksum file: archiveFile.get().asFile + } +} + +tasks.clean { + doFirst { + delete "dist" + delete "library/ar.jar" + } +} + +tasks.compileJava { + doFirst { + String[] deps = ["core.jar"] + File libFolder = file("library") + libFolder.mkdirs() + deps.each { String fn -> + Files.copy( + file("${rootDir}/build/libs/" + fn).toPath(), + file("library/" + fn).toPath(), + REPLACE_EXISTING + ) + } + } +} + +tasks.build { + doLast { + // Copying ar jar to library folder + File arJar = file("library/ar.jar") + arJar.mkdirs() + + // Need to check the existance of the files before using as the files + // will get generated only if Task ':mode:libraries:ar:jar' is not being skipped + // Task ':mode:libraries:ar:jar' will be skipped if source files are unchanged or jar task is UP-TO-DATE + def arJarFile = file("$buildDir/libs/ar.jar") + if (arJarFile.exists()) { + Files.copy(arJarFile.toPath(), arJar.toPath(), REPLACE_EXISTING) + } + + // Rename artifacts for Maven publishing + def processingArJar = file("$buildDir/libs/processing-ar-${arLibVersion}.jar") + if (arJarFile.exists()) { + Files.move(arJarFile.toPath(), processingArJar.toPath(), REPLACE_EXISTING) + } + + def processingArSourcesJar = file("$buildDir/libs/processing-ar-${arLibVersion}-sources.jar") + def arSourcesJar = file("$buildDir/libs/ar-sources.jar") + if (arSourcesJar.exists()) { + Files.move(arSourcesJar.toPath(), processingArSourcesJar.toPath(), REPLACE_EXISTING) + } + + def arMd5File = file("$buildDir/libs/ar.jar.MD5") + def processingArMd5File = file("$buildDir/libs/processing-ar-${arLibVersion}.jar.md5") + if (arMd5File.exists()) { + Files.move(arMd5File.toPath(), processingArMd5File.toPath(), REPLACE_EXISTING) + } + } +} + +ext { + libName = 'processing-ar' + libVersion = arLibVersion + libJar = "${buildDir}/libs/${libName}-${libVersion}.jar" + libSrc = "${buildDir}/libs/${libName}-${libVersion}-sources.jar" + libMd5 = "${buildDir}/libs/${libName}-${libVersion}-sources.jar.md5" + libDependencies = [[group: 'org.p5android', name: 'processing-core', version: modeVersion], + [group: 'com.google.ar', name: 'core', version: garVersion]] +} + +apply from: "${rootProject.projectDir}/scripts/publish-module.gradle" diff --git a/processing/mode/libraries/ar/examples/Cubes/Cubes.pde b/processing/mode/libraries/ar/examples/Cubes/Cubes.pde new file mode 100644 index 000000000..d5a61a518 --- /dev/null +++ b/processing/mode/libraries/ar/examples/Cubes/Cubes.pde @@ -0,0 +1,93 @@ +import processing.ar.*; + +ARTracker tracker; +ARAnchor touchAnchor; +ArrayList trackAnchors; +float angle; + +void setup() { + fullScreen(AR); + tracker = new ARTracker(this); + tracker.start(); + trackAnchors = new ArrayList(); +} + +void draw() { + // The AR Core session, frame and camera can be accessed through Processing's surface object + // to obtain the full information about the AR scene: +// ARSurface surface = (ARSurface) getSurface(); +// surface.camera.getPose(); +// surface.frame.getLightEstimate(); + + lights(); + + if (mousePressed) { + // Create new anchor at the current touch point + if (touchAnchor != null) touchAnchor.dispose(); + ARTrackable hit = tracker.get(mouseX, mouseY); + if (hit != null) touchAnchor = new ARAnchor(hit); + else touchAnchor = null; + } + + // Draw objects attached to each anchor + for (ARAnchor anchor : trackAnchors) { + if (anchor.isTracking()) drawBox(anchor, 255, 255, 255); + + // It is very important to dispose anchors once they are no longer tracked. + if (anchor.isStopped()) anchor.dispose(); + } + if (touchAnchor != null) drawBox(touchAnchor, 255, 0, 0); + + // Conveniency function in the tracker object to remove disposed anchors from a list + tracker.clearAnchors(trackAnchors); + + // Draw trackable planes + for (int i = 0; i < tracker.count(); i++) { + ARTrackable trackable = tracker.get(i); + if (!trackable.isTracking()) continue; + + pushMatrix(); + trackable.transform(); + if (mousePressed && trackable.isSelected(mouseX, mouseY)) { + fill(255, 0, 0, 100); + } else { + fill(255, 100); + } + beginShape(); + float[] points = trackable.getPolygon(); + for (int n = 0; n < points.length / 2; n++) { + float x = points[2 * n]; + float z = points[2 * n + 1]; + vertex(x, 0, z); + } + endShape(); + popMatrix(); + } + + angle += 0.1; +} + +void drawBox(ARAnchor anchor, int r, int g, int b) { + anchor.attach(); + fill(r, g, b); + rotateY(angle); + box(0.15f); + anchor.detach(); +} + +void trackableEvent(ARTrackable t) { + if (trackAnchors.size() < 10) { + float x0 = 0, y0 = 0; + if (t.isWallPlane()) { + // The new trackable is a wall, so adding the anchor 0.3 meters to its side + x0 = 0.3; + } else if (t.isFloorPlane()) { + // The new trackable is a floor plane, so adding the anchor 0.3 meters above it + y0 = 0.3; + } else { + // The new trackable is a floor plane, so adding the anchor 0.3 meters below it + y0 = -0.3; + } + trackAnchors.add(new ARAnchor(t, x0, y0, 0)); + } +} \ No newline at end of file diff --git a/processing/mode/libraries/ar/examples/Cubes/code/sketch.properties b/processing/mode/libraries/ar/examples/Cubes/code/sketch.properties new file mode 100644 index 000000000..db8eaa0b5 --- /dev/null +++ b/processing/mode/libraries/ar/examples/Cubes/code/sketch.properties @@ -0,0 +1 @@ +component=ar diff --git a/processing/mode/libraries/ar/examples/ImageMarkers/ImageMarkers.pde b/processing/mode/libraries/ar/examples/ImageMarkers/ImageMarkers.pde new file mode 100644 index 000000000..6b346c7f2 --- /dev/null +++ b/processing/mode/libraries/ar/examples/ImageMarkers/ImageMarkers.pde @@ -0,0 +1,43 @@ +import processing.ar.*; + +ARTracker tracker; +ARAnchor anchor; +PShape earth; + +void setup() { + fullScreen(AR); + + tracker = new ARTracker(this); + + PImage earthImg = loadImage("earth.jpg"); + tracker.start(); + + // Add the image to use as a marker to the AR tracker + tracker.addImage("earth", earthImg); + + // If you know the size (in meters) of the image in the physical space, + // you can specify it in the addImage(), this is optional but it would + // speed up the detection since the AR library will know the size of the + // marker beforehand + // tracker.addImage("earth", earthImg, 0.25); + + earth = createShape(SPHERE, 0.15); +} + +void draw() { + lights(); + + if (mousePressed) { + // Create new anchor at the current touch point + if (anchor != null) anchor.dispose(); + ARTrackable hit = tracker.get(mouseX, mouseY); + if (hit != null && hit.isImage() && hit.getName().equals("earth")) anchor = new ARAnchor(hit); + else anchor = null; + } + + if (anchor != null) { + anchor.attach(); + shape(earth); + anchor.detach(); + } +} \ No newline at end of file diff --git a/processing/mode/libraries/ar/examples/ImageMarkers/code/sketch.properties b/processing/mode/libraries/ar/examples/ImageMarkers/code/sketch.properties new file mode 100644 index 000000000..db8eaa0b5 --- /dev/null +++ b/processing/mode/libraries/ar/examples/ImageMarkers/code/sketch.properties @@ -0,0 +1 @@ +component=ar diff --git a/processing/mode/libraries/ar/examples/ImageMarkers/data/earth.jpg b/processing/mode/libraries/ar/examples/ImageMarkers/data/earth.jpg new file mode 100644 index 000000000..73ebe8431 Binary files /dev/null and b/processing/mode/libraries/ar/examples/ImageMarkers/data/earth.jpg differ diff --git a/processing/mode/libraries/ar/examples/ImportObj/ImportObj.pde b/processing/mode/libraries/ar/examples/ImportObj/ImportObj.pde new file mode 100644 index 000000000..f0412165e --- /dev/null +++ b/processing/mode/libraries/ar/examples/ImportObj/ImportObj.pde @@ -0,0 +1,31 @@ +import processing.ar.*; + +ARTracker tracker; +ARAnchor anchor; +PShape arObj; + +void setup() { + fullScreen(AR); + arObj = loadShape("model.obj"); + + tracker = new ARTracker(this); + tracker.start(); +} + +void draw() { + lights(); + + if (mousePressed) { + // Create new anchor at the current touch point + if (anchor != null) anchor.dispose(); + ARTrackable hit = tracker.get(mouseX, mouseY); + if (hit != null) anchor = new ARAnchor(hit); + else anchor = null; + } + + if (anchor != null) { + anchor.attach(); + shape(arObj); + anchor.detach(); + } +} diff --git a/processing/mode/libraries/ar/examples/ImportObj/code/sketch.properties b/processing/mode/libraries/ar/examples/ImportObj/code/sketch.properties new file mode 100644 index 000000000..db8eaa0b5 --- /dev/null +++ b/processing/mode/libraries/ar/examples/ImportObj/code/sketch.properties @@ -0,0 +1 @@ +component=ar diff --git a/processing/mode/libraries/ar/examples/ImportObj/data/grey.png b/processing/mode/libraries/ar/examples/ImportObj/data/grey.png new file mode 100644 index 000000000..b6d6f05e4 Binary files /dev/null and b/processing/mode/libraries/ar/examples/ImportObj/data/grey.png differ diff --git a/processing/mode/libraries/ar/examples/ImportObj/data/materials.mtl b/processing/mode/libraries/ar/examples/ImportObj/data/materials.mtl new file mode 100755 index 000000000..d0bf7bc00 --- /dev/null +++ b/processing/mode/libraries/ar/examples/ImportObj/data/materials.mtl @@ -0,0 +1,12 @@ +newmtl mat10 + Kd 0.30 0.69 0.31 + +newmtl mat9 + Kd 0.55 0.76 0.29 + +newmtl mat20 + Kd 0.47 0.33 0.28 + +newmtl mat21 + Kd 1.00 1.00 1.00 + diff --git a/processing/mode/libraries/ar/examples/ImportObj/data/model.obj b/processing/mode/libraries/ar/examples/ImportObj/data/model.obj new file mode 100755 index 000000000..b30f38dcd --- /dev/null +++ b/processing/mode/libraries/ar/examples/ImportObj/data/model.obj @@ -0,0 +1,949 @@ +mtllib materials.mtl +v 0.03500453 0.019117 0.02433401 +v -0.006614864 0.0148387 -0.01981938 +v 0.03608745 0.01436758 0.02320725 +v 0.008281738 -0.0277046 0.0253039 +v -0.002996296 0.01700664 -0.01070774 +v -0.02605835 0.00233531 0.005670011 +v -0.01863462 0.2030077 0.1667039 +v -0.01543677 0.0811317 0.09948444 +v 0.03643253 0.02604151 0.01268643 +v 0.01329854 0.01135957 -0.04139006 +v 0.01430747 0.006541491 -0.04051358 +v 0.03744149 0.02122343 0.01356286 +v -0.02249089 0.01276231 0.007519543 +v -0.02148193 0.007944226 0.008396029 +v 0.02063528 0.2003068 -0.1131554 +v 0.03034195 0.1211638 -0.04571742 +v 0.001753241 0.03735411 -0.02767992 +v 0.01017797 0.08989894 -0.05778307 +v 0.02250633 0.09819317 -0.04077369 +v 0.01443794 0.165741 -0.09460694 +v 0.01391521 0.04973161 0.003262162 +v 0.002539635 0.02941597 0.01170969 +v -0.01658016 0.02541399 0.0003730655 +v -0.06505209 0.1163319 -0.004948497 +v 0.01759383 -0.009375334 -0.01819491 +v 0.01072627 0.02040827 0.01247698 +v -0.03959695 0.07464659 -0.01419669 +v -0.0185844 -0.01070535 0.005561471 +v -0.05041924 0.06390738 0.001349032 +v 0.006013215 0.03756678 -0.005902886 +v 0.006297797 0.0006850958 -0.02858913 +v 0.00980702 -0.001161218 -0.02554345 +v 0.009522438 0.03572035 -0.002857208 +v -0.02055576 0.005186796 0.005080104 +v -0.01704657 0.003340483 0.008125842 +v -0.01841947 0.04425907 -0.06405324 +v -0.03397268 0.08564556 -0.1005961 +v -0.02476519 0.05102789 -0.0618062 +v 0.02960703 0.004537225 0.03208822 +v 0.01192492 -0.002894044 -0.007959068 +v 0.02648646 0.0009000301 0.03351396 +v -0.0108943 0.03334081 0.01692253 +v -0.01401487 0.02970362 0.01834822 +v 0.0001596808 0.05255258 0.02025765 +v 0.051799 0.1298349 0.04909253 +v 0.0005793571 0.01908755 0.01888919 +v 0.0499264 -0.01731193 -0.0006120801 +v 0.04912642 -0.0203712 0.00326097 +v 0.003881067 0.01530802 0.02209854 +v -0.006612957 -0.02721441 -0.02011198 +v -0.00741291 -0.03027368 -0.01623887 +v -0.002917379 -0.02342451 0.03035516 +v -0.1224723 0.1188886 -0.0985955 +v -0.1014801 0.09994781 -0.08556575 +v -0.1030209 0.1053934 0.1052268 +v -0.0212988 -0.01117647 -0.03490889 +v 0.0158276 0.02291071 -0.001223564 +v -0.07115999 0.05342448 0.04761094 +v -0.01541749 -0.022282 0.02439606 +v -0.07139879 0.05052888 0.06651038 +v -0.01759699 0.02451885 0.00341332 +v -0.005952865 0.01053989 -0.003992498 +v -0.02672911 -0.009570718 0.01931787 +v -0.02047238 -0.006453276 -0.02330488 +v -0.1268312 0.07678592 0.002777576 +v -0.1150678 0.0672816 0.009178877 +v -0.07114238 0.03764832 -0.006659627 +v -0.02572748 -0.02377486 -0.007589579 +v -0.01347169 0.01270366 0.01641744 +v 0.04988497 0.003462791 -0.04212153 +v -0.02844059 -0.002164483 -0.06671989 +v -0.02802613 -0.007144094 -0.06690043 +v 0.05029941 -0.0015167 -0.04230213 +v -0.07574981 -0.008532882 0.0003093481 +v -0.07533535 -0.01351237 0.000128746 +v -0.02666298 -0.00684154 0.06633401 +v -0.02624851 -0.01182103 0.06615347 +v 0.05098358 0.0005722046 0.04011029 +v 0.05139804 -0.004407287 0.03992969 +v -0.02176204 -0.04356194 -0.07239652 +v 0.05649534 -0.03909731 -0.04734689 +v -0.0695408 -0.04930222 -0.005644143 +v -0.02081233 -0.04838526 0.06066066 +v 0.0570823 -0.04207826 0.03488696 +v 0.07041439 0.01539612 -0.05716801 +v -0.03705421 0.007957578 -0.08661979 +v -0.03663483 0.002982736 -0.08689362 +v 0.07083377 0.01042128 -0.05744183 +v -0.04998913 0.00577414 -0.06675935 +v -0.0977459 -0.002287507 0.006567121 +v -0.09732652 -0.007262349 0.006293297 +v -0.08314592 -0.002056479 0.02473265 +v -0.04424265 -0.001441121 0.07313687 +v -0.02778679 -0.001180768 0.0936116 +v -0.02736741 -0.006155729 0.09333777 +v 0.07614198 0.00974834 0.05422109 +v 0.07656136 0.004773378 0.05394727 +v 0.0750888 0.01078677 0.03373891 +v 0.07135528 0.01446831 -0.03887033 +v -0.02881977 0.0001393557 0.0680449 +v 0.05481839 0.008709669 0.04043132 +v -0.03199118 -0.08194315 -0.09419835 +v -0.08995503 -0.08119118 0.001257837 +v -0.01710126 -0.08282828 0.0858857 +v 0.08588874 -0.0845921 0.04273254 +v 0.07668623 -0.08404505 -0.06856555 +v 0.04908967 0.01287305 -0.04398769 +v -0.03213352 0.007053494 -0.06265551 +v -0.07307526 -0.0002579689 0.00748086 +v -0.03908038 -0.01706243 -0.0657807 +v 0.04292423 -0.0108943 -0.05105788 +v 0.05283928 -0.01613939 0.03291172 +v -0.02927521 -0.02569211 0.06449127 +v -0.07646161 -0.02560854 0.006180525 +v 0.01355043 -0.02314949 0.04385048 +v 0.01901108 -0.008549571 -0.01473302 +v 0.01652125 -0.01269674 -0.0159986 +v 0.0110606 -0.02729666 0.0425849 +v -0.0290007 0.01061189 0.01693255 +v -0.03149053 0.00646472 0.01566696 +v -0.02181986 0.003832459 0.008502007 +v 0.1225296 0.1186905 0.01693249 +vn 0.6759462 0.3124644 -0.66743 +vn 0.3036638 -0.1538349 0.9402782 +vn -0.1288713 -0.6415519 -0.7561768 +vn 0.6317405 -0.4490114 -0.6318961 +vn 0.5168009 0.6132385 -0.5973738 +vn 0.6353032 -0.3483134 0.6892516 +vn -0.9574758 -0.1581962 0.2412763 +vn -0.7149026 -0.4934512 0.495399 +vn 0.513531 -0.404038 0.7569936 +vn -0.6899258 0.4684089 -0.5519016 +vn 0.2377314 0.9194574 -0.3131804 +vn -0.9323441 0.05290945 0.3576803 +vn 0.902027 0.1131086 -0.4165979 +vn -0.7814992 -0.2662952 -0.5642216 +vn -0.1205278 0.1531868 0.9808195 +vn -0.7951764 -0.1880778 -0.5764732 +vn 0.2017912 -0.9636137 0.1752966 +vn 0.9226931 -0.1570926 -0.3520787 +vn -0.1877113 0.5051754 0.8423552 +vn -0.828216 0.3726066 0.4185961 +vn 0.6965678 0.2683296 0.6654266 +vn -0.8662611 0.1966525 -0.4592596 +vn 0.9343936 -0.1576237 -0.3194739 +vn -0.3524163 -0.3926582 -0.8494835 +vn -0.4714933 0.5698856 0.6729968 +vn 0.9370085 -0.3383273 -0.08688932 +vn 0.8010244 -0.4923694 -0.3404884 +vn -0.5066848 0.5572503 0.6578318 +vn -0.730508 -0.2646336 -0.6295453 +vn 0.4703085 -0.6906323 -0.5493971 +vn 0.5243899 -0.5602476 -0.6412004 +vn -0.6460397 0.4476092 0.618287 +vn 0.7643175 0.5396877 -0.352925 +vn -0.5135508 -0.3115103 -0.7995167 +vn -0.2271142 -0.002504592 0.9738649 +vn -0.6906796 -0.2732151 -0.6695635 +vn 0.4608619 -0.5828981 0.6692057 +vn -0.6596208 -0.3198385 -0.6801499 +vn 0.3868736 0.2156837 0.8965542 +vn 0.7782366 0.5445973 -0.3126687 +vn 0.7123011 0.3717107 -0.595364 +vn -0.350459 -0.9234875 -0.156043 +vn -0.3618422 0.5517769 0.751407 +vn -0.6169685 -0.6768168 -0.4015832 +vn 0.7018433 -0.3692735 0.6091413 +vn 0.9352891 0.190618 -0.2981597 +vn -0.5437235 0.6330461 0.551015 +vn -0.7395383 -0.5798131 -0.3419064 +vn -0.7387139 -0.5767542 -0.3487927 +vn -0.7117295 -0.5995156 -0.3660905 +vn -0.2450886 -0.6918193 -0.6792037 +vn 0.7083459 -0.6808023 -0.1864253 +vn -0.577066 0.183116 -0.7959042 +vn -0.05425279 0.4044118 0.9129664 +vn -0.3655506 0.3593813 -0.858614 +vn -0.6241442 -0.7272121 0.285669 +vn 0.9166214 -0.1101973 -0.3842678 +vn -0.2199824 -0.04261146 0.9745728 +vn -0.4381971 -0.04221215 0.8978872 +vn 0.5168417 0.3057183 -0.7996317 +vn -0.4323599 -0.04563689 0.9005456 +vn -0.1379253 0.4258103 -0.8942384 +vn 0.6460316 0.5284206 0.5508313 +vn 0.3233464 -0.7739131 -0.5445234 +vn 0.9823619 -0.1736944 -0.0692488 +vn 0.3515399 -0.2111629 -0.9120472 +vn -0.1599931 -0.6118627 0.7746137 +vn 0.5969911 0.7998882 0.06148641 +vn -0.8055354 -0.5835604 0.1028103 +vn -0.5761724 0.2658848 0.7728718 +vn 0.3974828 0.776682 0.4886436 +vn -0.7156691 -0.6781162 0.1672608 +vn -0.07940822 0.7431037 -0.6644482 +vn -0.8092278 -0.5867478 -0.02962401 +vn 0.6566163 -0.02666638 0.7537534 +vn -0.8871716 -0.4543079 -0.08081483 +vn 0.7527174 -0.6295595 -0.1925396 +vn -0.8154821 -0.5704756 -0.09770723 +vn -0.4086105 -0.6716307 0.6180208 +vn -0.6949745 0.2989345 -0.6539487 +vn 0.6619559 0.163908 0.7314018 +vn 0.266736 0.6152108 -0.7418677 +vn -0.6199426 -0.7842435 -0.02516915 +vn 0.2866544 0.5899032 -0.7548798 +vn 0.8534204 -0.4676813 -0.2301036 +vn 0.3394441 0.6475117 -0.6822803 +vn -0.000973773 0.1296944 0.9915536 +vn 0.2884114 0.6099449 -0.7380963 +vn 0.3598991 0.7445212 0.5622818 +vn 0.3754486 0.790543 0.4838181 +vn 0.6365256 -0.4595098 -0.6194239 +vn 0.3754486 0.790543 0.4838181 +vn -0.639107 -0.6897317 0.3403125 +vn 0.8143744 -0.5001561 0.2943436 +vn -0.5803067 -0.4435949 -0.6829845 +vn 0.2952288 0.05915305 -0.9535937 +vn -0.8140077 -0.04675739 -0.5789691 +vn -0.7983131 -0.08805058 0.5957711 +vn 0.3206232 -0.007661377 0.9471759 +vn 0.9964691 0.08331603 -0.01038414 +vn -0.08289231 0.9959039 0.03611439 +vn 0.286491 0.1822246 -0.9405941 +vn -0.8123397 -0.04491663 -0.5814524 +vn -0.7837809 -0.2125169 0.5835445 +vn 0.3226649 -0.08544654 0.9426486 +vn 0.9876623 0.1565333 -0.004534003 +vn 0.06860351 -0.9969714 -0.03662946 +vn 0.2587442 0.07481664 -0.9630441 +vn -0.835243 -0.04022668 -0.5484077 +vn -0.7749525 -0.09967847 0.6241096 +vn 0.356296 -0.02137723 0.9341286 +vn 0.9951558 0.08646443 -0.04678641 +vn -0.08387454 0.9949704 0.05476191 +vn 0.2568304 0.09675303 -0.9616013 +vn -0.8347547 -0.04585963 -0.548709 +vn -0.7680125 -0.1630291 0.6193371 +vn 0.3605577 -0.07899904 0.9293854 +vn 0.9929608 0.1093502 -0.04551313 +vn -0.0185397 -0.9998224 -0.00338177 +vn 0.2300202 0.127853 -0.9647509 +vn 0.9940968 0.07122472 -0.08184498 +vn -0.8547598 -0.002095141 -0.5190196 +vn -0.7535963 -0.1191722 0.6464446 +vn 0.3856194 -0.03796748 0.9218765 +vn -0.08387487 0.9949704 0.05476191 +vn -0.08387746 0.9949701 0.05476293 +vn -0.08387492 0.9949704 0.054762 +vn -0.0838771 0.9949703 0.05476097 +vn 0.8370441 -0.3003807 0.4573058 +vn -0.2022253 -0.2306736 0.951785 +vn -0.9958286 0.05799839 0.07043803 +vn -0.3230657 0.1345664 -0.9367606 +vn 0.8053956 0.06682255 -0.588959 +vn 0.8852978 -0.1409901 0.4431361 +vn 0.7753266 -0.07137989 -0.6275139 +vn -0.1704701 -0.0781832 0.9822562 +vn -0.9686102 0.2132797 0.1276949 +vn -0.3726705 0.2958405 -0.8795426 +vn -0.08772954 0.9935086 0.07241636 +vn 0.8625041 -0.5040262 -0.04521547 +vn -0.3532428 0.4605826 -0.8142992 +vn -0.5092613 0.04344368 0.8595148 +vn 0.2014092 -0.6723981 0.7122606 +vn -0.4979688 -0.8294312 -0.2531227 +vn 0.7829186 -0.6168609 -0.08075375 +vn -0.2661578 0.3731634 0.8887683 +vn -0.3811645 0.5344062 -0.7544028 +vn -0.3251337 0.4688944 -0.8212346 +vt -0.1140787 0.03138284 +vt -0.1158554 0.02556534 +vt -0.1136141 0.03119799 +vt 0.09894149 0.1048713 +vt 0.1021685 0.109191 +vt 0.1016744 0.1091149 +vt -0.1066763 0.1035016 +vt -0.1121509 0.1055655 +vt -0.1115286 0.1024404 +vt -0.08475467 0.1383537 +vt -0.0901614 0.1356119 +vt -0.08420303 0.1333365 +vt -0.06537322 -0.03387522 +vt -0.06724139 -0.02905086 +vt -0.09099902 -0.03133029 +vt 0.07564665 0.128677 +vt 0.07249503 0.124302 +vt 0.09948863 0.1275901 +vt -0.005117498 0.1487897 +vt 0.006192557 0.1439615 +vt -0.01494416 0.1586518 +vt 0.08523902 -0.0517007 +vt 0.09608601 -0.04590664 +vt 0.08278457 -0.0473825 +vt 0.0740456 0.113275 +vt 0.08742387 0.1136336 +vt 0.1010457 0.1165095 +vt -0.08119667 0.1337613 +vt -0.0558333 0.1293723 +vt -0.05547386 0.1325383 +vt 0.006262578 0.003618848 +vt 0.01038755 0.0004965129 +vt 0.01137706 0.0003261221 +vt 0.0510156 0.1469026 +vt 0.05004591 0.1471631 +vt 0.04608426 0.1428577 +vt -0.0688381 0.1264659 +vt -0.07417805 0.1235963 +vt -0.07394136 0.1231558 +vt -0.06860141 0.1260255 +vt -0.1078465 0.1188627 +vt -0.1100746 0.1132248 +vt -0.1096096 0.1130411 +vt -0.1073815 0.1186789 +vt 0.1179777 0.102659 +vt 0.1160989 0.1084227 +vt 0.1156235 0.1082677 +vt 0.1175023 0.1025041 +vt -0.1057023 0.1199926 +vt -0.103169 0.1255001 +vt -0.1183577 0.1388551 +vt -0.01354274 0.07674651 +vt -0.01952282 0.07575219 +vt -0.01567168 0.07107045 +vt -0.05110518 0.1516622 +vt -0.04528297 0.1533509 +vt -0.05128602 0.162781 +vt 0.1083808 0.1214489 +vt 0.1108377 0.1159069 +vt 0.118157 0.1268701 +vt 0.06290381 0.1529554 +vt 0.06604792 0.1581991 +vt 0.06413454 0.1593991 +vt 0.1264942 0.130957 +vt 0.1174081 0.1225252 +vt 0.1132593 0.1180342 +vt 0.1123433 0.113189 +vt -0.06912652 0.1435414 +vt -0.06841803 0.1484213 +vt -0.07191654 0.1539707 +vt -0.07323746 0.1560659 +vt -0.04413793 0.1652177 +vt -0.05065675 0.1733763 +vt -0.04452227 0.154104 +vt -0.124141 0.1168212 +vt -0.1340527 0.124265 +vt -0.1310707 0.1216421 +vt 0.09981892 0.1402302 +vt 0.09372418 0.1338609 +vt 0.1062645 0.1424113 +vt -0.007361628 0.1738323 +vt -0.007709313 0.17138 +vt -0.005147806 0.1628786 +vt -0.0538327 0.161586 +vt -0.05581391 0.1605016 +vt -0.04927731 0.1539648 +vt 0.09250034 0.1317357 +vt 0.1043252 0.1442625 +vt 0.09186566 0.135595 +vt 0.09088932 0.1333186 +vt -0.1226757 0.1284759 +vt -0.1122508 0.114762 +vt -0.1163737 0.1228593 +vt -0.08792244 0.137607 +vt -0.08221005 0.1305407 +vt -0.08053256 0.1320529 +vt -0.08900896 0.1344408 +vt -0.08996293 0.1367266 +vt -0.09593607 0.1432101 +vt 0.09943064 0.1496814 +vt 0.1019548 0.1527476 +vt 0.0930742 0.1472529 +vt -0.01989863 -0.1092991 +vt -0.01381554 -0.09547608 +vt -0.01781833 -0.09712763 +vt -0.1225598 0.1129164 +vt -0.1139305 0.1103271 +vt -0.1124988 0.1144137 +vt 0.1284472 0.09043346 +vt 0.1162811 0.0925452 +vt 0.115033 0.08839886 +vt -0.1183999 0.1133674 +vt -0.1187976 0.1155082 +vt -0.1237072 0.1147289 +vt 0.05978798 0.1068567 +vt 0.05546509 0.1071068 +vt 0.05740996 0.103238 +vt -0.1209545 0.1119765 +vt -0.1204887 0.1098495 +vt -0.1123944 0.1091671 +vt 0.1132004 0.1118751 +vt 0.1213011 0.1124761 +vt 0.1266491 0.1136676 +vt 0.01676467 -0.1112485 +vt 0.01793306 -0.1064167 +vt 0.0187613 -0.09627871 +vt -0.11463 0.006658147 +vt -0.1112072 0.004005964 +vt -0.1109009 0.0044012 +vt -0.1143238 0.00705338 +vt -0.08027916 0.03211533 +vt -0.07883324 0.02803375 +vt -0.07836194 0.02820071 +vt -0.07980787 0.03228229 +vt 0.10148 0.1225743 +vt 0.1026396 0.1267463 +vt 0.1021579 0.1268802 +vt 0.1009982 0.1227082 +vt -0.113922 0.0599463 +vt -0.1136528 0.06426805 +vt -0.1196823 0.06541675 +vt 0.06852727 0.1349547 +vt 0.06428875 0.1340687 +vt 0.06717528 0.130841 +vt -0.031122 0.136667 +vt -0.02851421 0.1401238 +vt -0.03805421 0.1462688 +vt 0.08087214 0.137729 +vt 0.08012602 0.1334637 +vt 0.09210182 0.1393621 +vt -0.1228887 0.06915759 +vt -0.1220789 0.06966314 +vt -0.1272685 0.0721065 +vt -0.1220974 0.07083166 +vt -0.1229096 0.07033004 +vt -0.1171047 0.06465271 +vt -0.1166995 0.0635231 +vt -0.1226809 0.06901416 +vt -0.1271384 0.07184418 +vt -0.1225632 0.1000327 +vt -0.1170617 0.09840968 +vt -0.1109856 0.09754105 +vt -0.03230288 0.1458602 +vt -0.03669528 0.1452095 +vt -0.03228601 0.1453605 +vt -0.08946843 0.1235516 +vt -0.09427954 0.1223765 +vt -0.09411226 0.1219053 +vt 0.1168864 0.1136613 +vt 0.1120057 0.1154441 +vt 0.1118342 0.1149745 +vt 0.1167149 0.1131916 +vt -0.08923022 0.102894 +vt -0.08441637 0.1040579 +vt -0.09072792 0.1045616 +vt -0.02206336 -0.06198291 +vt -0.026459 -0.06236074 +vt -0.02417939 -0.06672869 +vt -0.05463599 0.1456433 +vt -0.0503474 0.1467942 +vt -0.04910248 0.1595716 +vt 0.1189074 0.08985645 +vt 0.1159428 0.08829741 +vt 0.1177477 0.08793834 +vt 0.1171631 0.08354659 +vt 0.1199852 0.08535087 +vt 0.123382 0.09447052 +vt -0.1099102 0.04050694 +vt -0.1141292 0.03747376 +vt -0.1123682 0.03800825 +vt 0.1142366 0.08509803 +vt 0.1171581 0.08316149 +vt 0.1234379 0.09405048 +vt -0.09879679 0.08813052 +vt -0.09228849 0.0808953 +vt -0.08597927 0.08036333 +vt 0.1120607 0.09141384 +vt 0.1117586 0.09101542 +vt 0.1165892 0.08735266 +vt -0.0806265 0.1231025 +vt -0.084486 0.1184276 +vt -0.08410042 0.1181093 +vt -0.08024094 0.1227841 +vt 0.003482211 0.1583301 +vt 0.008283757 0.1566894 +vt 0.009117588 0.1606198 +vt 0.003706832 0.1578865 +vt -0.1132746 0.1046951 +vt -0.110367 0.1100145 +vt -0.1334541 0.1032534 +vt 0.07486738 0.03010331 +vt 0.07010847 0.03385869 +vt 0.06923567 0.02785967 +vt 0.08733691 -0.001883278 +vt 0.107071 -0.006697561 +vt 0.08547853 0.003908656 +vt -0.1046513 -0.0164464 +vt -0.1242836 -0.02133137 +vt -0.1016751 -0.02055635 +vt 0.1212808 0.1178688 +vt 0.1042064 0.12052 +vt 0.1016979 0.117381 +vt 0.1256555 0.0683793 +vt 0.1226187 0.0690648 +vt 0.1030349 0.06861331 +vt -0.06885661 -0.05426354 +vt -0.08479238 -0.06094307 +vt -0.0872846 -0.06280877 +vt -0.06427599 0.0429099 +vt -0.04557212 0.04992529 +vt -0.05010713 0.05394819 +vt -0.1186936 0.00875746 +vt -0.1111613 0.01484358 +vt -0.1148017 0.01969101 +vt 0.1133721 0.1177334 +vt 0.09872365 0.1281269 +vt 0.09655823 0.1224647 +vt -0.1210305 0.057626 +vt -0.121323 0.05951564 +vt -0.1272598 0.05359038 +vt -0.03441339 0.1511454 +vt -0.03898577 0.147165 +vt -0.03325248 0.1451954 +vt -0.1214571 0.02656759 +vt -0.1206144 0.02485112 +vt -0.1133698 0.03189421 +vt 0.05701512 -0.0197142 +vt 0.0656883 -0.0248974 +vt 0.07260296 -0.02759513 +vt -0.0905035 0.1244898 +vt -0.08244815 0.1268278 +vt -0.07161251 0.1309845 +vt 0.1098242 0.1199025 +vt 0.1079822 0.1205848 +vt 0.1073509 0.1169247 +vt -0.07463787 0.01149246 +vt -0.07068079 0.01041753 +vt -0.07288663 0.01238216 +vt -0.09607417 -0.01917072 +vt -0.09462651 -0.01839539 +vt -0.0896282 -0.007019095 +vt -0.08036827 0.006541973 +vt -0.07875977 0.01191062 +vt -0.08519926 0.00165505 +vt -0.03451624 0.1565128 +vt -0.03692249 0.1547995 +vt -0.03572354 0.1527238 +vt -0.06357461 -0.005884686 +vt -0.06871022 -0.01052367 +vt -0.07308383 -0.01582387 +vt 0.1154477 0.1019501 +vt 0.1264731 0.09787957 +vt 0.1167904 0.1041863 +vt -0.07543775 0.008823099 +vt -0.07690215 0.003413375 +vt -0.07148749 0.007723369 +vt 0.1106047 0.07933239 +vt 0.1089076 0.0787183 +vt 0.1182114 0.07186498 +vt 0.1143609 0.06150217 +vt 0.1159351 0.06103433 +vt 0.1070294 0.0692399 +vt -0.0856042 0.133481 +vt -0.08607899 0.1360457 +vt -0.08776338 0.1366938 +vt 0.1143609 0.06150217 +vt 0.1159351 0.06103433 +vt 0.1070294 0.0692399 +vt -0.00711296 -0.08452687 +vt -0.006638455 -0.07278366 +vt -0.009549673 -0.07189343 +vt 0.02740077 0.1438553 +vt 0.03042767 0.1435304 +vt 0.02961959 0.1471556 +vt -0.1268686 0.09593622 +vt -0.1149407 0.1007597 +vt -0.1164133 0.1026511 +vt -0.1132122 0.08596116 +vt -0.116635 0.07847779 +vt -0.1161803 0.07826982 +vt -0.1127575 0.08575319 +vt -0.09220141 0.1340483 +vt -0.0955446 0.1265291 +vt -0.09508772 0.1263259 +vt -0.09174453 0.1338452 +vt 0.1076475 0.09159471 +vt 0.1012101 0.09672074 +vt 0.1008986 0.0963296 +vt 0.107336 0.09120356 +vt 0.1088697 0.1063079 +vt 0.1060496 0.1140386 +vt 0.1055798 0.1138672 +vt 0.1083999 0.1061365 +vt 0.04792935 0.1433837 +vt 0.04180641 0.1488816 +vt 0.04147236 0.1485095 +vt 0.04759529 0.1430117 +vt 0.03883927 0.07155949 +vt 0.03899961 0.06333206 +vt 0.04687391 0.06094213 +vt 0.05158016 0.0676925 +vt 0.04661448 0.07425439 +vt -0.1090102 0.0720264 +vt -0.1118069 0.06428732 +vt -0.1082356 0.06319106 +vt -0.1053391 0.07089337 +vt -0.091727 0.1338432 +vt -0.09507115 0.1263244 +vt -0.09153756 0.1253288 +vt -0.0881338 0.1328208 +vt 0.1156497 0.06152174 +vt 0.1103468 0.06781421 +vt 0.1076163 0.06526272 +vt 0.1129695 0.059013 +vt 0.1053661 0.1045086 +vt 0.1025635 0.1122455 +vt 0.09884425 0.1112788 +vt 0.1017466 0.1035787 +vt 0.05987245 0.1339858 +vt 0.05440415 0.1401351 +vt 0.05130423 0.1378654 +vt 0.05677184 0.1317156 +vt -0.04742923 0.07296612 +vt -0.052449 0.06644552 +vt -0.04779873 0.05965646 +vt -0.03990494 0.06198118 +vt -0.03967657 0.07020701 +vt -0.1126079 0.08761457 +vt -0.1170812 0.07738168 +vt -0.116623 0.07718141 +vt -0.1121497 0.0874143 +vt -0.09040387 0.137115 +vt -0.09136991 0.1349397 +vt -0.09493659 0.1269083 +vt -0.09447964 0.1267054 +vt -0.08994691 0.1369121 +vt 0.1126091 0.0847635 +vt 0.1108731 0.08631867 +vt 0.1062476 0.09046261 +vt 0.104291 0.09221547 +vt 0.1039573 0.09184305 +vt 0.1122754 0.08439109 +vt 0.1087505 0.1067333 +vt 0.1050009 0.1172529 +vt 0.1045299 0.1170851 +vt 0.1082795 0.1065654 +vt 0.04346379 0.1439831 +vt 0.04188982 0.1453021 +vt 0.03631011 0.1499778 +vt 0.03490401 0.1511562 +vt 0.03458287 0.1507729 +vt 0.04314264 0.1435999 +vt 0.03859253 0.07477754 +vt 0.03847141 0.06361028 +vt 0.04096083 0.06277146 +vt 0.04090803 0.06440245 +vt 0.04062173 0.07324729 +vt 0.04055234 0.07539091 +vt -0.1115371 0.08516809 +vt -0.1158737 0.07487652 +vt -0.1081331 0.0712769 +vt -0.09044557 0.1366012 +vt -0.09494228 0.1263786 +vt -0.08806984 0.1235113 +vt 0.116101 0.07012583 +vt 0.1085203 0.07832673 +vt 0.1028228 0.07304142 +vt 0.1059965 0.1057554 +vt 0.1022465 0.1162749 +vt 0.09359471 0.1136038 +vt 0.04671876 0.1411094 +vt 0.03839004 0.1485495 +vt 0.04023906 0.1347846 +vt -0.0485208 0.06271283 +vt -0.05463352 0.05336634 +vt -0.04763341 0.04466456 +vt -0.03719438 0.04863306 +vt -0.03774281 0.05978751 +vt -0.1063763 0.06940357 +vt -0.1014021 0.07940255 +vt -0.1101423 0.08320101 +vt 0.02503191 0.1526401 +vt 0.01901537 0.1452495 +vt 0.02837731 0.1391605 +vt -0.08299437 0.1268454 +vt -0.07715007 0.1363621 +vt -0.08486463 0.140017 +vt 0.101952 0.07948565 +vt 0.1099468 0.07168785 +vt 0.1152929 0.07687157 +vt 0.09472505 0.1153084 +vt 0.09966098 0.1052905 +vt 0.1071245 0.1074564 +vt 0.05429678 0.06709726 +vt 0.0557166 0.06900758 +vt 0.04925064 0.07811327 +vt 0.04749985 0.07756533 +vt 0.04874702 0.07564453 +vt 0.05329662 0.06863762 +vt 0.04096082 0.06277189 +vt 0.04684603 0.06078886 +vt 0.04811948 0.0623384 +vt 0.04090803 0.06440288 +vt 0.04874703 0.07564454 +vt 0.04749986 0.07756533 +vt 0.04055234 0.07539096 +vt 0.04062172 0.07324734 +vt 0.04684583 0.06078869 +vt 0.04905448 0.06004447 +vt 0.05429659 0.06709749 +vt 0.05329644 0.06863783 +vt 0.04811928 0.06233824 +vt 0.06042005 0.1400615 +vt 0.05567345 0.1466915 +vt 0.05380367 0.1449886 +vt 0.1090244 0.06430319 +vt 0.1065122 0.07227091 +vt 0.1045174 0.07067419 +vt -0.01340094 0.1599647 +vt -0.02074384 0.1557398 +vt -0.01893001 0.1538718 +vt -0.1014335 0.1117869 +vt -0.1043637 0.1034366 +vt -0.1019694 0.1024032 +vt -0.09895404 0.1022399 +vt -0.1050736 0.09790201 +vt -0.09772499 0.09993985 +vt 0.06604317 0.1456356 +vt 0.07035102 0.1387124 +vt 0.07234798 0.1403156 +vt -0.1009395 0.1124417 +vt -0.09987329 0.1101134 +vt -0.09348343 0.1140423 +vt 0.1101286 0.08789753 +vt 0.1132709 0.08015662 +vt 0.1151113 0.08189123 +vt 0.002846764 0.1586051 +vt 0.01075032 0.1616549 +vt 0.009274639 0.1637408 +vt -0.09130703 0.1005571 +vt -0.08848813 0.1089456 +vt -0.09089162 0.1099469 +vt 0.05002477 0.06340988 +vt 0.05546781 0.06948122 +vt 0.05122348 0.07667714 +vt 0.04300268 0.07463127 +vt 0.04290868 0.0657823 +vt -0.006475641 0.1533231 +vt -0.0114986 0.1567173 +vt -0.01177854 0.156303 +vt -0.006755578 0.1529089 +vt -0.0747896 0.09971711 +vt -0.07908086 0.09543516 +vt -0.07872769 0.09508123 +vt -0.07443643 0.09936318 +vt 0.1166597 0.09208065 +vt 0.1108894 0.09393932 +vt 0.1107361 0.0934634 +vt 0.1165064 0.09160472 +vt 0.05920173 0.06907564 +vt 0.05513092 0.07356769 +vt 0.05805777 0.06968994 +vt -0.09210218 0.03360453 +vt -0.09663808 0.03762642 +vt -0.0978532 0.03168727 +vt -0.02150865 0.1506603 +vt -0.01634552 0.1474835 +vt -0.01356576 0.1653571 +vt 0.1074875 0.1151102 +vt 0.1133728 0.1136564 +vt 0.1132144 0.1322682 +vt -0.06883705 0.09612168 +vt -0.07584256 0.1132075 +vt -0.06999642 0.09553699 +vt -0.08491834 0.1101264 +vt -0.07818113 0.09293307 +vt -0.07504927 0.09664721 +o group2002521463 +g mesh2002521463 +usemtl mat10 +f 3/3/1 2/2/1 1/1/1 +f 3/6/2 1/5/2 4/4/2 +f 6/9/3 5/8/3 4/7/3 +f 4/12/4 2/11/4 3/10/4 +f 7/15/5 1/14/5 5/13/5 +f 7/18/6 4/17/6 1/16/6 +f 7/21/7 6/20/7 8/19/7 +f 4/24/8 8/23/8 6/22/8 +f 7/27/9 8/26/9 4/25/9 +f 5/30/10 6/29/10 7/28/10 +f 2/33/11 5/32/11 1/31/11 +f 4/36/12 5/35/12 2/34/12 +o group1119943561 +g mesh1119943561 +usemtl mat10 +f 12/40/13 11/39/13 10/38/13 9/37/13 +f 11/44/14 14/43/14 13/42/14 10/41/14 +f 14/48/15 12/47/15 9/46/15 13/45/15 +f 15/51/16 10/50/16 13/49/16 +f 14/54/17 11/53/17 12/52/17 +f 16/57/18 9/56/18 10/55/18 +f 16/60/19 13/59/19 9/58/19 +f 19/63/20 18/62/20 17/61/20 +f 13/67/21 17/66/21 18/65/21 15/64/21 +f 16/71/22 19/70/22 17/69/22 13/68/22 +f 10/74/23 15/73/23 16/72/23 +f 20/77/24 15/76/24 18/75/24 +f 20/80/25 21/79/25 16/78/25 +f 22/83/26 19/82/26 16/81/26 +f 22/86/27 18/85/27 19/84/27 +f 22/90/28 21/89/28 20/88/28 23/87/28 +f 18/93/29 23/92/29 20/91/29 +f 22/96/30 23/95/30 18/94/30 +f 16/99/31 21/98/31 22/97/31 +f 16/102/32 15/101/32 20/100/32 +o group634111619 +g mesh634111619 +usemtl mat10 +f 26/105/33 25/104/33 24/103/33 +f 25/108/34 28/107/34 27/106/34 +f 28/111/35 26/110/35 24/109/35 +f 24/114/36 27/113/36 29/112/36 +f 28/117/37 25/116/37 26/115/37 +f 28/120/38 29/119/38 27/118/38 +f 24/123/39 29/122/39 28/121/39 +f 25/126/40 27/125/40 24/124/40 +o group1265393450 +g mesh1265393450 +usemtl mat9 +f 33/130/41 32/129/41 31/128/41 30/127/41 +f 32/134/42 35/133/42 34/132/42 31/131/42 +f 35/138/43 33/137/43 30/136/43 34/135/43 +f 36/141/44 31/140/44 34/139/44 +f 35/144/45 32/143/45 33/142/45 +f 37/147/46 30/146/46 31/145/46 +f 37/150/47 34/149/47 30/148/47 +f 37/153/48 36/152/48 38/151/48 +f 34/156/49 38/155/49 36/154/49 +f 37/159/50 38/158/50 34/157/50 +f 31/162/51 36/161/51 37/160/51 +o group792360013 +g mesh792360013 +usemtl mat9 +f 41/165/52 40/164/52 39/163/52 +f 43/168/53 42/167/53 40/166/53 +f 43/172/54 41/171/54 39/170/54 42/169/54 +f 44/175/55 40/174/55 42/173/55 +f 43/178/56 40/177/56 41/176/56 +f 45/181/57 39/180/57 40/179/57 +f 42/184/58 46/183/58 44/182/58 +f 45/187/59 44/186/59 46/185/59 +f 46/190/60 42/189/60 39/188/60 +f 45/193/61 46/192/61 39/191/61 +f 40/196/62 44/195/62 45/194/62 +o group481990970 +g mesh481990970 +usemtl mat10 +f 49/199/63 48/198/63 47/197/63 +f 48/203/64 51/202/64 50/201/64 47/200/64 +f 51/207/65 49/206/65 52/205/65 50/204/65 +f 53/210/66 47/209/66 50/208/66 +f 51/213/67 48/212/67 49/211/67 +f 47/216/68 53/215/68 49/214/68 +f 52/219/69 53/218/69 50/217/69 +f 52/222/70 49/221/70 54/220/70 +f 52/225/71 54/224/71 53/223/71 +f 53/228/72 54/227/72 49/226/72 +o group1935184771 +g mesh1935184771 +usemtl mat9 +f 57/231/73 56/230/73 55/229/73 +f 56/234/74 59/233/74 58/232/74 +f 59/237/75 57/236/75 55/235/75 +f 55/240/76 58/239/76 60/238/76 +f 59/243/77 56/242/77 57/241/77 +f 59/246/78 60/245/78 58/244/78 +f 55/249/79 60/248/79 59/247/79 +f 56/252/80 58/251/80 55/250/80 +o group2043820846 +g mesh2043820846 +usemtl mat10 +f 63/255/81 62/254/81 61/253/81 +f 62/258/82 64/257/82 61/256/82 +f 64/261/83 66/260/83 65/259/83 +f 65/264/84 61/263/84 67/262/84 +f 68/267/85 64/266/85 62/265/85 +f 65/270/86 67/269/86 64/268/86 +f 69/273/87 66/272/87 63/271/87 +f 64/276/88 67/275/88 61/274/88 +f 66/279/89 69/278/89 61/277/89 +f 61/282/90 65/281/90 66/280/90 +f 61/285/91 69/284/91 63/283/91 +f 61/288/92 65/287/92 66/286/92 +f 68/291/93 63/290/93 66/289/93 +f 62/294/94 63/293/94 68/292/94 +f 64/297/95 68/296/95 66/295/95 +o group971385351 +g mesh971385351 +usemtl mat20 +f 73/301/96 72/300/96 71/299/96 70/298/96 +f 72/305/97 75/304/97 74/303/97 71/302/97 +f 75/309/98 77/308/98 76/307/98 74/306/98 +f 77/313/99 79/312/99 78/311/99 76/310/99 +f 79/317/100 73/316/100 70/315/100 78/314/100 +f 70/322/101 71/321/101 74/320/101 76/319/101 78/318/101 +f 81/326/102 80/325/102 72/324/102 73/323/102 +f 80/330/103 82/329/103 75/328/103 72/327/103 +f 82/334/104 83/333/104 77/332/104 75/331/104 +f 83/338/105 84/337/105 79/336/105 77/335/105 +f 84/342/106 81/341/106 73/340/106 79/339/106 +f 84/347/107 83/346/107 82/345/107 80/344/107 81/343/107 +o group7854860 +g mesh7854860 +usemtl mat21 +f 88/351/108 87/350/108 86/349/108 85/348/108 +f 87/356/109 91/355/109 90/354/109 89/353/109 86/352/109 +f 91/362/110 95/361/110 94/360/110 93/359/110 92/358/110 90/357/110 +f 95/366/111 97/365/111 96/364/111 94/363/111 +f 97/372/112 88/371/112 85/370/112 99/369/112 98/368/112 96/367/112 +f 98/378/113 101/377/113 100/376/113 93/375/113 94/374/113 96/373/113 +f 102/381/114 87/380/114 88/379/114 +f 103/384/115 91/383/115 87/382/115 +f 104/387/116 95/386/116 91/385/116 +f 105/390/117 97/389/117 95/388/117 +f 105/393/118 88/392/118 97/391/118 +f 105/398/119 104/397/119 103/396/119 102/395/119 106/394/119 +f 88/401/120 106/400/120 102/399/120 +f 105/404/121 106/403/121 88/402/121 +f 87/407/122 102/406/122 103/405/122 +f 91/410/123 103/409/123 104/408/123 +f 95/413/124 104/412/124 105/411/124 +f 108/419/125 107/418/125 99/417/125 85/416/125 86/415/125 89/414/125 +f 100/423/126 109/422/126 92/421/126 93/420/126 +f 101/427/127 98/426/127 99/425/127 107/424/127 +f 109/432/128 108/431/128 89/430/128 90/429/128 92/428/128 +f 110/435/129 108/434/129 109/433/129 +f 111/438/130 107/437/130 108/436/130 +f 112/441/131 101/440/131 107/439/131 +f 113/444/132 100/443/132 101/442/132 +f 113/447/133 109/446/133 100/445/133 +f 109/450/134 114/449/134 110/448/134 +f 113/453/135 114/452/135 109/451/135 +f 108/456/136 110/455/136 111/454/136 +f 107/459/137 111/458/137 112/457/137 +f 101/462/138 112/461/138 113/460/138 +f 113/467/139 112/466/139 111/465/139 110/464/139 114/463/139 +o group1104563999 +g mesh1104563999 +usemtl mat10 +f 118/471/140 117/470/140 116/469/140 115/468/140 +f 117/475/141 120/474/141 119/473/141 116/472/141 +f 120/479/142 118/478/142 115/477/142 119/476/142 +f 121/482/143 116/481/143 119/480/143 +f 120/485/144 117/484/144 118/483/144 +f 122/488/145 115/487/145 116/486/145 +f 122/491/146 119/490/146 115/489/146 +f 119/494/147 122/493/147 121/492/147 +f 116/497/148 121/496/148 122/495/148 diff --git a/processing/mode/libraries/ar/examples/Spheres/Spheres.pde b/processing/mode/libraries/ar/examples/Spheres/Spheres.pde new file mode 100644 index 000000000..f04b91f0e --- /dev/null +++ b/processing/mode/libraries/ar/examples/Spheres/Spheres.pde @@ -0,0 +1,62 @@ +import processing.ar.*; + +ARTracker tracker; +ARAnchor anchor; +PShape arObj; +float angle; + +void setup() { + fullScreen(AR); + + noStroke(); + + tracker = new ARTracker(this); + tracker.start(); +} + +void draw() { + lights(); + + if (mousePressed) { + // Create new anchor at the current touch point + if (anchor != null) anchor.dispose(); + ARTrackable hit = tracker.get(mouseX, mouseY); + if (hit != null) anchor = new ARAnchor(hit); + else anchor = null; + } + + if (anchor != null) { + anchor.attach(); + fill(217, 121, 255); + sphere(0.1); + rotateY(angle); + translate(0, 0, 0.3); + sphere(0.05); + angle += 0.1; + anchor.detach(); + } + + // Draw trackable planes + for (int i = 0; i < tracker.count(); i++) { + ARTrackable trackable = tracker.get(i); + if (!trackable.isTracking()) continue; + + pushMatrix(); + trackable.transform(); + if (mousePressed && trackable.isSelected(mouseX, mouseY)) { + fill(255, 0, 0, 100); + } else { + fill(255, 100); + } + + beginShape(QUADS); + float lx = trackable.lengthX(); + float lz = trackable.lengthZ(); + vertex(-lx/2, 0, -lz/2); + vertex(-lx/2, 0, +lz/2); + vertex(+lx/2, 0, +lz/2); + vertex(+lx/2, 0, -lz/2); + endShape(); + popMatrix(); + } +} diff --git a/processing/mode/libraries/ar/examples/Spheres/code/sketch.properties b/processing/mode/libraries/ar/examples/Spheres/code/sketch.properties new file mode 100644 index 000000000..7bae3ea6c --- /dev/null +++ b/processing/mode/libraries/ar/examples/Spheres/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=ar +mode.id=processing.mode.android.AndroidMode diff --git a/processing/mode/libraries/ar/library.properties b/processing/mode/libraries/ar/library.properties new file mode 100644 index 000000000..e5f844ae3 --- /dev/null +++ b/processing/mode/libraries/ar/library.properties @@ -0,0 +1,10 @@ +name = AR +authorList = The Processing Foundation, Syam Sundar K +url = https://android.processing.org +category = 3D +sentence = Renderer to develop AR apps +paragraph = +version = 13 +prettyVersion = 4.2.1 +minRevision = 249 +maxRevision = 1269 \ No newline at end of file diff --git a/mode/libraries/vr/README.md b/processing/mode/libraries/vr/README.md similarity index 100% rename from mode/libraries/vr/README.md rename to processing/mode/libraries/vr/README.md diff --git a/processing/mode/libraries/vr/build.gradle b/processing/mode/libraries/vr/build.gradle new file mode 100644 index 000000000..b223c4910 --- /dev/null +++ b/processing/mode/libraries/vr/build.gradle @@ -0,0 +1,132 @@ +import java.nio.file.Files +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING + +plugins { + id 'java-library' + id 'maven-publish' +} + +dependencies { + compileOnly name: "android" + compileOnly "org.p5android:processing-core:${modeVersion}" + +// commenting due to issue #718 +// implementationAar "com.google.vr:sdk-audio:${gvrVersion}" +// implementationAar "com.google.vr:sdk-base:${gvrVersion}" + +// fix for Issue #718 + implementation fileTree(dir: "../../../../libs/google-vr/", include: ["*.aar"]) +} + +sourceSets { + main { + java.srcDir("../../../../libs/processing-vr/src/main/java/") + resources { + srcDir("../../../../libs/processing-vr/src/main/") + exclude "AndroidManifest.xml" + exclude "**/java/**" + } + } +} + +java { + withSourcesJar() +} + +tasks.named('jar') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} + +tasks.register("sourceJar", Jar) { + from sourceSets.main.allJava + archiveClassifier.set("sources") +} + +// 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 +} + +artifacts { +// archives javadocJar + archives sourceJar +} + +tasks.jar { + doLast { + ant.checksum file: archiveFile.get().asFile + } +} + +tasks.clean { + doFirst { + delete "dist" + delete "library/vr.jar" + } +} + +tasks.compileJava { + doFirst { + String[] deps = ["sdk-audio.jar", + "sdk-base.jar", + "sdk-common.jar"] + File libFolder = file("library") + libFolder.mkdirs() + deps.each { String fn -> + Files.copy( + file("${rootDir}/build/libs/" + fn).toPath(), + file("library/" + fn).toPath(), + REPLACE_EXISTING + ) + } + } +} + +tasks.build { + doLast { + // Copying vr jar to library folder + File vrJar = file("library/vr.jar") + vrJar.mkdirs() + + // Need to check the existance of the files before using as the files + // will get generated only if Task ':mode:libraries:vr:jar' is not being skipped + // Task ':mode:libraries:vr:jar' will be skipped if source files are unchanged or jar task is UP-TO-DATE + def vrJarFile = file("$buildDir/libs/vr.jar") + if (vrJarFile.exists()) { + Files.copy(vrJarFile.toPath(), vrJar.toPath(), REPLACE_EXISTING) + } + + // Renaming artifacts for maven publishing + def processingVrJar = file("$buildDir/libs/processing-vr-${vrLibVersion}.jar") + if (vrJarFile.exists()) { + Files.move(vrJarFile.toPath(), processingVrJar.toPath(), REPLACE_EXISTING) + } + + def processingVrSourcesJar = file("$buildDir/libs/processing-vr-${vrLibVersion}-sources.jar") + def vrSourcesJar = file("$buildDir/libs/vr-sources.jar") + if (vrSourcesJar.exists()) { + Files.move(vrSourcesJar.toPath(), processingVrSourcesJar.toPath(), REPLACE_EXISTING) + } + + def vrMd5File = file("$buildDir/libs/vr.jar.MD5") + def processingVrMd5File = file("$buildDir/libs/processing-vr-${vrLibVersion}.jar.md5") + if (vrMd5File.exists()) { + Files.move(vrMd5File.toPath(), processingVrMd5File.toPath(), REPLACE_EXISTING) + } + } +} + +ext { + libName = 'processing-vr' + libVersion = vrLibVersion + libJar = "${buildDir}/libs/${libName}-${libVersion}.jar" + libSrc = "${buildDir}/libs/${libName}-${libVersion}-sources.jar" + libMd5 = "${buildDir}/libs/${libName}-${libVersion}-sources.jar.md5" + libDependencies = [[group: 'org.p5android', name: 'processing-core', version: modeVersion], + [group: 'com.google.vr', name: 'sdk-base', version: gvrVersion], + [group: 'com.google.vr', name: 'sdk-audio', version: gvrVersion]] +} + +apply from: "${rootProject.projectDir}/scripts/publish-module.gradle" diff --git a/mode/libraries/vr/examples/Cube/Cube.pde b/processing/mode/libraries/vr/examples/Cube/Cube.pde similarity index 89% rename from mode/libraries/vr/examples/Cube/Cube.pde rename to processing/mode/libraries/vr/examples/Cube/Cube.pde index 8daccd1b0..7ecd2cb78 100644 --- a/mode/libraries/vr/examples/Cube/Cube.pde +++ b/processing/mode/libraries/vr/examples/Cube/Cube.pde @@ -1,7 +1,7 @@ import processing.vr.*; void setup() { - fullScreen(STEREO); + fullScreen(VR); } void draw() { diff --git a/processing/mode/libraries/vr/examples/Cube/code/sketch.properties b/processing/mode/libraries/vr/examples/Cube/code/sketch.properties new file mode 100644 index 000000000..8b38b89c7 --- /dev/null +++ b/processing/mode/libraries/vr/examples/Cube/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode \ No newline at end of file diff --git a/processing/mode/libraries/vr/examples/GenerateRay/GenerateRay.pde b/processing/mode/libraries/vr/examples/GenerateRay/GenerateRay.pde new file mode 100644 index 000000000..e9fcac44d --- /dev/null +++ b/processing/mode/libraries/vr/examples/GenerateRay/GenerateRay.pde @@ -0,0 +1,59 @@ +import processing.vr.*; + +PVector origin, direction; +float[] randomx = new float[5]; +float[] randomy = new float[5]; +float[] randomz = new float[5]; +VRCamera cam; + +void setup() { + fullScreen(VR); + cameraUp(); + cam = new VRCamera(this); + + for (int i = 0; i < 5; ++i) { + randomx[i] = random(-500, 500); + randomy[i] = random(-500, 500); + randomz[i] = random(-100, 100); + } + noStroke(); + + origin = new PVector(randomx[0], randomy[0], randomz[0]); + direction = new PVector(); +} + +void draw() { + background(200, 0, 150); + lights(); + + cam.setPosition(0, 0, 400); + push(); + translate(randomx[0], randomy[0], randomz[0]); + fill(0, 255, 0); + sphere(30); + + int r = floor(random(1, 5)); + float rx = randomx[r] - randomx[0]; + float ry = randomy[r] - randomy[0]; + float rz = randomz[r] - randomz[0]; + stroke(0); + strokeWeight(1 * displayDensity); + line(0, 0, 0, rx, ry, rz); + noStroke(); + + direction.set(rx, ry, rz); + direction.normalize(); + + pop(); + + for (int i = 1; i < 5; ++i) { + push(); + translate(randomx[i], randomy[i], randomz[i]); + fill(255, 0, 0); + if (intersectsSphere(70, origin, direction)) { + fill(0, 0, 255); + } + sphere(70); + pop(); + } +} \ No newline at end of file diff --git a/mode/libraries/vr/examples/drawAim/code/sketch.properties b/processing/mode/libraries/vr/examples/GenerateRay/code/sketch.properties similarity index 100% rename from mode/libraries/vr/examples/drawAim/code/sketch.properties rename to processing/mode/libraries/vr/examples/GenerateRay/code/sketch.properties diff --git a/processing/mode/libraries/vr/examples/IntersectsBox/IntersectsBox.pde b/processing/mode/libraries/vr/examples/IntersectsBox/IntersectsBox.pde new file mode 100644 index 000000000..566cb7a72 --- /dev/null +++ b/processing/mode/libraries/vr/examples/IntersectsBox/IntersectsBox.pde @@ -0,0 +1,27 @@ +import processing.vr.*; + +VRCamera cam; +float rotSpeed = 0.3; +float rotAngle = 0; + +void setup() { + fullScreen(VR); + cameraUp(); + cam = new VRCamera(this); +} + +void draw() { + background(200, 0, 150); + + cam.setPosition(0, 0, 200); + push(); + rotateZ(radians(rotAngle)); + translate(100, 0, 0); + fill(255, 0, 0); + if (intersectsBox(50, 0, 0)) { + rotAngle += rotSpeed; + fill(0, 0, 255); + } + box(50); + pop(); +} \ No newline at end of file diff --git a/processing/mode/libraries/vr/examples/IntersectsBox/code/sketch.properties b/processing/mode/libraries/vr/examples/IntersectsBox/code/sketch.properties new file mode 100644 index 000000000..0828c1ecf --- /dev/null +++ b/processing/mode/libraries/vr/examples/IntersectsBox/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode diff --git a/processing/mode/libraries/vr/examples/IntersectsPlane/IntersectsPlane.pde b/processing/mode/libraries/vr/examples/IntersectsPlane/IntersectsPlane.pde new file mode 100644 index 000000000..a1a6f2701 --- /dev/null +++ b/processing/mode/libraries/vr/examples/IntersectsPlane/IntersectsPlane.pde @@ -0,0 +1,39 @@ +import processing.vr.*; + +VRCamera cam; +float x, y; + +void setup() { + fullScreen(VR); + cameraUp(); + cam = new VRCamera(this); +} + +void draw() { + background(200, 0, 150); + + cam.setPosition(0, 0, 400); + fill(255, 170, 238); + plane(400, 400); + fill(255, 0, 0); + translate(x, y, 0); + PVector offset; + if (intersectsBox(60, 0, 0)) { + translate(-x, -y, 0); + fill(0, 0, 255); + offset = intersectsPlane(0, 0); + x = offset.x; + y = offset.y; + } + translate(x, y, 0); + box(60); +} + +void plane(float w, float d) { + beginShape(QUADS); + vertex(-w/2, -d/2); + vertex(+w/2, -d/2); + vertex(+w/2, +d/2); + vertex(-w/2, +d/2); + endShape(); +} \ No newline at end of file diff --git a/processing/mode/libraries/vr/examples/IntersectsPlane/code/sketch.properties b/processing/mode/libraries/vr/examples/IntersectsPlane/code/sketch.properties new file mode 100644 index 000000000..0828c1ecf --- /dev/null +++ b/processing/mode/libraries/vr/examples/IntersectsPlane/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode diff --git a/processing/mode/libraries/vr/examples/IntersectsSphere/IntersectsSphere.pde b/processing/mode/libraries/vr/examples/IntersectsSphere/IntersectsSphere.pde new file mode 100644 index 000000000..041b91ed4 --- /dev/null +++ b/processing/mode/libraries/vr/examples/IntersectsSphere/IntersectsSphere.pde @@ -0,0 +1,42 @@ +import processing.vr.*; + +float[] randomx = new float[5]; +float[] randomy = new float[5]; +VRCamera cam; + +void setup() { + fullScreen(VR); + cameraUp(); + cam = new VRCamera(this); + + for (int i = 0; i < 5; ++i) { + randomx[i] = random(-500, 500); + randomy[i] = random(-500, 500); + } +} + +void draw() { + background(200, 0, 150); + + lights(); + noStroke(); + cam.setPosition(0, 0, 400); + for (int i = 0; i < 5; ++i) { + push(); + translate(randomx[i], randomy[i]); + fill(255, 0, 0); + if (intersectsSphere(70, 0, 0)) { + fill(0, 0, 255); + } + sphere(70); + pop(); + } + + cam.sticky(); + strokeWeight(5 * displayDensity); + stroke(0, 0, 255); + noFill(); + translate(0, 0, 200); + circle(0, 0, 50); + cam.noSticky(); +} \ No newline at end of file diff --git a/processing/mode/libraries/vr/examples/IntersectsSphere/code/sketch.properties b/processing/mode/libraries/vr/examples/IntersectsSphere/code/sketch.properties new file mode 100644 index 000000000..0828c1ecf --- /dev/null +++ b/processing/mode/libraries/vr/examples/IntersectsSphere/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode diff --git a/mode/libraries/vr/examples/Mono/Mono.pde b/processing/mode/libraries/vr/examples/Mono/Mono.pde similarity index 100% rename from mode/libraries/vr/examples/Mono/Mono.pde rename to processing/mode/libraries/vr/examples/Mono/Mono.pde diff --git a/processing/mode/libraries/vr/examples/Mono/code/sketch.properties b/processing/mode/libraries/vr/examples/Mono/code/sketch.properties new file mode 100644 index 000000000..8b38b89c7 --- /dev/null +++ b/processing/mode/libraries/vr/examples/Mono/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode \ No newline at end of file diff --git a/processing/mode/libraries/vr/examples/Stereoscopy/Stereoscopy.pde b/processing/mode/libraries/vr/examples/Stereoscopy/Stereoscopy.pde new file mode 100644 index 000000000..8b3f12511 --- /dev/null +++ b/processing/mode/libraries/vr/examples/Stereoscopy/Stereoscopy.pde @@ -0,0 +1,88 @@ +// Stereoscopy in VR: +// https://github.com/processing/processing-android/issues/593 +// By Javier Marco Rubio (https://github.com/jmarco2000) + +import processing.vr.*; + +int eyedist = 50; + +VRCamera cam; + +float angulo=0; + +float cx, cz; +float px, pz; +float ang; +float orbita = 400; +float vorbita = 0.01; + +// eye matrix +PMatrix3D eyeMat = new PMatrix3D(); + +boolean ciclo = false; //test witch eye is beign draw +float orientacion; + +void setup() { + fullScreen(VR); + cameraUp(); + rectMode(CENTER); + cam = new VRCamera(this); + cam.setNear(10); + cam.setFar(2500); + + //posicion planeta + //centro + cx=width/2; + cz=-200; + ang=0; +} + +void draw() { + ciclo = !ciclo; + + background(0); + lights(); + if (!ciclo) { + getEyeMatrix(eyeMat); + orientacion = acos(eyeMat.m00); + if (eyeMat.m02 < 0) orientacion =- orientacion; + println(degrees(orientacion)); + } + if (ciclo) { + cam.setPosition(+(eyedist/2) * cos(orientacion), 100, 500 + (eyedist/2) * sin(orientacion)); + } else { + cam.setPosition(-(eyedist/2) * cos(orientacion), 100, 500 - (eyedist/2) * sin(orientacion)); + } + + pushMatrix(); + px = cx + cos(ang) * orbita; + pz = cz + sin(ang) * orbita; + translate(px, 100, pz); + ang = ang + vorbita; + rotateY(1.25); + rotateX(-0.4); + rotateZ(angulo); //angulo=angulo+0.01; + noStroke(); + fill(251, 100, 10); + box(100); + popMatrix(); + + pushMatrix(); + translate(width/2, 100, cz); + rotateY(1.25); + rotateX(-0.4); + rotateZ(angulo); //angulo=angulo+0.01; + noStroke(); + fill(0, 200, 10); + sphere(70); + popMatrix(); +} + +void mousePressed() { + vorbita = 0; +} + +void mouseReleased() { + vorbita = 0.01; +} + diff --git a/processing/mode/libraries/vr/examples/Stereoscopy/code/sketch.properties b/processing/mode/libraries/vr/examples/Stereoscopy/code/sketch.properties new file mode 100644 index 000000000..8b38b89c7 --- /dev/null +++ b/processing/mode/libraries/vr/examples/Stereoscopy/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode \ No newline at end of file diff --git a/processing/mode/libraries/vr/examples/drawAim/code/sketch.properties b/processing/mode/libraries/vr/examples/drawAim/code/sketch.properties new file mode 100644 index 000000000..0828c1ecf --- /dev/null +++ b/processing/mode/libraries/vr/examples/drawAim/code/sketch.properties @@ -0,0 +1,3 @@ +mode=Android +component=vr +mode.id=processing.mode.android.AndroidMode diff --git a/mode/libraries/vr/examples/drawAim/drawAim.pde b/processing/mode/libraries/vr/examples/drawAim/drawAim.pde similarity index 90% rename from mode/libraries/vr/examples/drawAim/drawAim.pde rename to processing/mode/libraries/vr/examples/drawAim/drawAim.pde index 28630a519..eaae4e797 100644 --- a/mode/libraries/vr/examples/drawAim/drawAim.pde +++ b/processing/mode/libraries/vr/examples/drawAim/drawAim.pde @@ -1,7 +1,10 @@ import processing.vr.*; +VRCamera cam; + void setup() { - fullScreen(STEREO); + fullScreen(VR); + cam = new VRCamera(this); } void calculate() { @@ -57,8 +60,9 @@ void draw() { popMatrix(); // Use eye coordinates at 100 units from the camera position:; - eye(); + cam.sticky(); stroke(255, 200); strokeWeight(50); - point(0, 0, 100); + point(0, 0, 100); + cam.noSticky(); } \ No newline at end of file diff --git a/mode/libraries/vr/library.properties b/processing/mode/libraries/vr/library.properties similarity index 50% rename from mode/libraries/vr/library.properties rename to processing/mode/libraries/vr/library.properties index a8334f4dd..7c92c2cff 100644 --- a/mode/libraries/vr/library.properties +++ b/processing/mode/libraries/vr/library.properties @@ -1,10 +1,10 @@ name = VR authorList = Processing Foundation -url = http://android.processing.org +url = https://android.processing.org category = 3D sentence = Renderer to develop VR apps paragraph = -version = 9 -prettyVersion = 1.0.0 -minRevision = 248 -maxRevision = 0 \ No newline at end of file +version = 13 +prettyVersion = 4.2.1 +minRevision = 249 +maxRevision = 1269 \ No newline at end of file diff --git a/processing/mode/mode.properties b/processing/mode/mode.properties new file mode 100644 index 000000000..a03cb0ece --- /dev/null +++ b/processing/mode/mode.properties @@ -0,0 +1,10 @@ +name = Android Mode for Processing 4 +authorList = [The Processing Foundation](https://processingfoundation.org/) +url = https://android.processing.org +sentence = This mode lets you use Processing to create Android apps +paragraph = +imports=processing.mode.java.JavaMode +version = 412 +prettyVersion = 4.6.0 +minRevision = 1283 +maxRevision = 0 \ No newline at end of file diff --git a/processing/mode/mode/JavaMode.jar b/processing/mode/mode/JavaMode.jar new file mode 100644 index 000000000..04c55cc15 Binary files /dev/null and b/processing/mode/mode/JavaMode.jar differ diff --git a/processing/mode/mode/core.jar b/processing/mode/mode/core.jar new file mode 100644 index 000000000..e7b82a144 Binary files /dev/null and b/processing/mode/mode/core.jar differ diff --git a/processing/mode/mode/gradlew.zip b/processing/mode/mode/gradlew.zip new file mode 100644 index 000000000..5f7697c8a Binary files /dev/null and b/processing/mode/mode/gradlew.zip differ diff --git a/processing/mode/mode/istack-commons-runtime.jar b/processing/mode/mode/istack-commons-runtime.jar new file mode 100644 index 000000000..b91ea56ba Binary files /dev/null and b/processing/mode/mode/istack-commons-runtime.jar differ diff --git a/processing/mode/mode/javax.activation-api.jar b/processing/mode/mode/javax.activation-api.jar new file mode 100644 index 000000000..986c36509 Binary files /dev/null and b/processing/mode/mode/javax.activation-api.jar differ diff --git a/processing/mode/mode/jaxb-api.jar b/processing/mode/mode/jaxb-api.jar new file mode 100644 index 000000000..456586547 Binary files /dev/null and b/processing/mode/mode/jaxb-api.jar differ diff --git a/processing/mode/mode/jaxb-jxc.jar b/processing/mode/mode/jaxb-jxc.jar new file mode 100644 index 000000000..039e6cb2d Binary files /dev/null and b/processing/mode/mode/jaxb-jxc.jar differ diff --git a/processing/mode/mode/jaxb-runtime.jar b/processing/mode/mode/jaxb-runtime.jar new file mode 100644 index 000000000..0b9ef67c4 Binary files /dev/null and b/processing/mode/mode/jaxb-runtime.jar differ diff --git a/processing/mode/mode/jaxb-xjc.jar b/processing/mode/mode/jaxb-xjc.jar new file mode 100644 index 000000000..aab6be555 Binary files /dev/null and b/processing/mode/mode/jaxb-xjc.jar differ diff --git a/processing/mode/mode/jdi.jar b/processing/mode/mode/jdi.jar new file mode 100644 index 000000000..397e9e4da Binary files /dev/null and b/processing/mode/mode/jdi.jar differ diff --git a/processing/mode/mode/jdimodel.jar b/processing/mode/mode/jdimodel.jar new file mode 100644 index 000000000..3e272ce72 Binary files /dev/null and b/processing/mode/mode/jdimodel.jar differ diff --git a/processing/mode/mode/org.eclipse.core.contenttype.jar b/processing/mode/mode/org.eclipse.core.contenttype.jar new file mode 100644 index 000000000..2d8f772d1 Binary files /dev/null and b/processing/mode/mode/org.eclipse.core.contenttype.jar differ diff --git a/processing/mode/mode/org.eclipse.core.jobs.jar b/processing/mode/mode/org.eclipse.core.jobs.jar new file mode 100644 index 000000000..579555f49 Binary files /dev/null and b/processing/mode/mode/org.eclipse.core.jobs.jar differ diff --git a/processing/mode/mode/org.eclipse.core.resources.jar b/processing/mode/mode/org.eclipse.core.resources.jar new file mode 100644 index 000000000..dd04e3eef Binary files /dev/null and b/processing/mode/mode/org.eclipse.core.resources.jar differ diff --git a/processing/mode/mode/org.eclipse.core.runtime.jar b/processing/mode/mode/org.eclipse.core.runtime.jar new file mode 100644 index 000000000..4b46a1262 Binary files /dev/null and b/processing/mode/mode/org.eclipse.core.runtime.jar differ diff --git a/processing/mode/mode/org.eclipse.equinox.common.jar b/processing/mode/mode/org.eclipse.equinox.common.jar new file mode 100644 index 000000000..c2a745e12 Binary files /dev/null and b/processing/mode/mode/org.eclipse.equinox.common.jar differ diff --git a/processing/mode/mode/org.eclipse.equinox.preferences.jar b/processing/mode/mode/org.eclipse.equinox.preferences.jar new file mode 100644 index 000000000..d6cb801c6 Binary files /dev/null and b/processing/mode/mode/org.eclipse.equinox.preferences.jar differ diff --git a/processing/mode/mode/org.eclipse.jdt.core.jar b/processing/mode/mode/org.eclipse.jdt.core.jar new file mode 100644 index 000000000..2b675bc84 Binary files /dev/null and b/processing/mode/mode/org.eclipse.jdt.core.jar differ diff --git a/processing/mode/mode/org.eclipse.osgi.jar b/processing/mode/mode/org.eclipse.osgi.jar new file mode 100644 index 000000000..91f36da60 Binary files /dev/null and b/processing/mode/mode/org.eclipse.osgi.jar differ diff --git a/processing/mode/mode/org.eclipse.text.jar b/processing/mode/mode/org.eclipse.text.jar new file mode 100644 index 000000000..e1263c239 Binary files /dev/null and b/processing/mode/mode/org.eclipse.text.jar differ diff --git a/processing/mode/mode/pde.jar b/processing/mode/mode/pde.jar new file mode 100644 index 000000000..0eba179a6 Binary files /dev/null and b/processing/mode/mode/pde.jar differ diff --git a/processing/mode/resources/device-art-resources/device-art.xml b/processing/mode/resources/device-art-resources/device-art.xml new file mode 100644 index 000000000..b67fa2033 --- /dev/null +++ b/processing/mode/resources/device-art-resources/device-art.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/processing/mode/resources/device-art-resources/pixel_3/layout b/processing/mode/resources/device-art-resources/pixel_3/layout new file mode 100644 index 000000000..4615fb58d --- /dev/null +++ b/processing/mode/resources/device-art-resources/pixel_3/layout @@ -0,0 +1,38 @@ +parts { + device { + display { + width 1080 + height 2160 + x 0 + y 0 + } + } + portrait { + background { + image port_back.webp + } + foreground { + mask round_corners.webp + } + onion { + image port_fore.webp + } + } +} +layouts { + portrait { + width 1194 + height 2532 + event EV_SW:0:1 + part1 { + name portrait + x 0 + y 0 + } + part2 { + name device + x 54 + y 196 + } + } +} diff --git a/processing/mode/resources/device-art-resources/pixel_3/port_back.webp b/processing/mode/resources/device-art-resources/pixel_3/port_back.webp new file mode 100644 index 000000000..6037b7963 Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_3/port_back.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_3/round_corners.webp b/processing/mode/resources/device-art-resources/pixel_3/round_corners.webp new file mode 100644 index 000000000..9dad9033a Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_3/round_corners.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_6/back.webp b/processing/mode/resources/device-art-resources/pixel_6/back.webp new file mode 100644 index 000000000..a41256802 Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_6/back.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_6/layout b/processing/mode/resources/device-art-resources/pixel_6/layout new file mode 100644 index 000000000..ae638d344 --- /dev/null +++ b/processing/mode/resources/device-art-resources/pixel_6/layout @@ -0,0 +1,36 @@ +parts { + device { + display { + width 1080 + height 2400 + x 0 + y 0 + } + } + portrait { + background { + image back.webp + } + foreground { + mask mask.webp + cutout hole + } + } +} +layouts { + portrait { + width 1209 + height 2553 + event EV_SW:0:1 + part1 { + name portrait + x 0 + y 0 + } + part2 { + name device + x 60 + y 69 + } + } +} diff --git a/processing/mode/resources/device-art-resources/pixel_6/mask.webp b/processing/mode/resources/device-art-resources/pixel_6/mask.webp new file mode 100644 index 000000000..c658b192d Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_6/mask.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_c/land_back.webp b/processing/mode/resources/device-art-resources/pixel_c/land_back.webp new file mode 100644 index 000000000..4cec52875 Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_c/land_back.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_c/land_fore.webp b/processing/mode/resources/device-art-resources/pixel_c/land_fore.webp new file mode 100644 index 000000000..66a0b9df0 Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_c/land_fore.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_c/land_shadow.webp b/processing/mode/resources/device-art-resources/pixel_c/land_shadow.webp new file mode 100644 index 000000000..15acd8679 Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_c/land_shadow.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_c/layout b/processing/mode/resources/device-art-resources/pixel_c/layout new file mode 100644 index 000000000..7378ba8c5 --- /dev/null +++ b/processing/mode/resources/device-art-resources/pixel_c/layout @@ -0,0 +1,59 @@ +parts { + device { + display { + width 1800 + height 2560 + x 0 + y 0 + } + } + portrait { + background { + image port_back.webp + } + onion { + image port_fore.webp + } + } + landscape { + background { + image land_back.webp + } + onion { + image land_fore.webp + } + } +} +layouts { + portrait { + width 2307 + height 2971 + event EV_SW:0:1 + part1 { + name portrait + x 0 + y 0 + } + part2 { + name device + x 259 + y 181 + } + } + landscape { + width 3096 + height 2215 + event EV_SW:0:0 + part1 { + name landscape + x 0 + y 0 + } + part2 { + name device + x 269 + y 1988 + rotation 3 + } + } +} diff --git a/processing/mode/resources/device-art-resources/pixel_c/port_back.webp b/processing/mode/resources/device-art-resources/pixel_c/port_back.webp new file mode 100644 index 000000000..ee9e3eaea Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_c/port_back.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_c/port_fore.webp b/processing/mode/resources/device-art-resources/pixel_c/port_fore.webp new file mode 100644 index 000000000..f2e6b7ca5 Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_c/port_fore.webp differ diff --git a/processing/mode/resources/device-art-resources/pixel_c/port_shadow.webp b/processing/mode/resources/device-art-resources/pixel_c/port_shadow.webp new file mode 100644 index 000000000..e7c1e74bf Binary files /dev/null and b/processing/mode/resources/device-art-resources/pixel_c/port_shadow.webp differ diff --git a/processing/mode/resources/device-art-resources/wearos_small_round/device_bezel.png b/processing/mode/resources/device-art-resources/wearos_small_round/device_bezel.png new file mode 100644 index 000000000..5eccf08ce Binary files /dev/null and b/processing/mode/resources/device-art-resources/wearos_small_round/device_bezel.png differ diff --git a/processing/mode/resources/device-art-resources/wearos_small_round/hardware.ini b/processing/mode/resources/device-art-resources/wearos_small_round/hardware.ini new file mode 100644 index 000000000..3f8ab78b8 --- /dev/null +++ b/processing/mode/resources/device-art-resources/wearos_small_round/hardware.ini @@ -0,0 +1,3 @@ +# skin-specific hardware values +hw.rotaryInput=yes +hw.sensors.heart_rate=yes diff --git a/processing/mode/resources/device-art-resources/wearos_small_round/layout b/processing/mode/resources/device-art-resources/wearos_small_round/layout new file mode 100644 index 000000000..6c45bac29 --- /dev/null +++ b/processing/mode/resources/device-art-resources/wearos_small_round/layout @@ -0,0 +1,46 @@ +parts { + portrait { + background { + image device_bezel.png + } + } + + device { + display { + width 384 + height 384 + x 0 + y 0 + } + } +} + +layouts { + portrait { + width 456 + height 456 + color 0x1f1f1f + event EV_SW:0:1 + + part1 { + name portrait + x 0 + y 0 + } + + part2 { + name device + x 36 + y 36 + } + } +} + +keyboard { + charmap qwerty2 +} + +network { + speed full + delay none +} diff --git a/processing/mode/resources/device-art-resources/wearos_square/device_bezel.png b/processing/mode/resources/device-art-resources/wearos_square/device_bezel.png new file mode 100644 index 000000000..01c5cc146 Binary files /dev/null and b/processing/mode/resources/device-art-resources/wearos_square/device_bezel.png differ diff --git a/processing/mode/resources/device-art-resources/wearos_square/hardware.ini b/processing/mode/resources/device-art-resources/wearos_square/hardware.ini new file mode 100644 index 000000000..3f8ab78b8 --- /dev/null +++ b/processing/mode/resources/device-art-resources/wearos_square/hardware.ini @@ -0,0 +1,3 @@ +# skin-specific hardware values +hw.rotaryInput=yes +hw.sensors.heart_rate=yes diff --git a/processing/mode/resources/device-art-resources/wearos_square/layout b/processing/mode/resources/device-art-resources/wearos_square/layout new file mode 100644 index 000000000..d1f9278d1 --- /dev/null +++ b/processing/mode/resources/device-art-resources/wearos_square/layout @@ -0,0 +1,46 @@ +parts { + portrait { + background { + image device_bezel.png + } + } + + device { + display { + width 360 + height 360 + x 0 + y 0 + } + } +} + +layouts { + portrait { + width 432 + height 432 + color 0x1f1f1f + event EV_SW:0:1 + + part1 { + name portrait + x 18 + y 18 + } + + part2 { + name device + x 36 + y 36 + } + } +} + +keyboard { + charmap qwerty2 +} + +network { + speed full + delay none +} diff --git a/processing/mode/scripts/permissions.py b/processing/mode/scripts/permissions.py new file mode 100644 index 000000000..4e8053864 --- /dev/null +++ b/processing/mode/scripts/permissions.py @@ -0,0 +1,84 @@ +import sys, re + +from urllib.request import urlopen +from bs4 import BeautifulSoup + +def get_soup(url): + page = urlopen(url) + soup = BeautifulSoup(page, features="lxml") + return soup + +def parse_all(soup): + print('Parsing all permissions...') + table = soup.find('table', { 'id': 'constants', 'class' : 'responsive constants' }) + entries = table.find_all('tr') + str_list = '' + for entry in entries: + if not entry or not entry.attrs: continue + info = entry.find('td', {'width':'100%'}) + if info: + name = info.find('code').find('a').contents[0] + pieces = [] + deprecated = False + for piece in info.find('p').contents: + piece_str = re.sub('\s+', ' ', str(piece)).strip() + if '' in piece_str: + piece_str = piece.find('a').contents[0].strip() + if '' in piece_str and 'This constant was deprecated' in piece_str: + deprecated = True + pieces += [piece_str] + if name and pieces and not deprecated: + desc = ' '.join(pieces).strip().replace('"', '\\"') + str_list += (',' if str_list else '') + '\n "' + name + '", "' + desc + '"' + str_list = 'static final String[] listing = {' + str_list + '\n };\n' + return str_list + +def replace_all(source, str_list): + print('Replacing old permissions...') + idx0 = source.find('static final String[] listing = {') + idx1 = source[idx0:].find(' };') + return source[:idx0] + str_list + source[idx0+idx1+5:] + +def parse_danger(soup): + print('Parsing dangerous permissions...') + entries = soup.find_all(lambda tag:tag.name == "div" and + len(tag.attrs) == 1 and + "data-version-added" in tag.attrs) + str_list = '' + for entry in entries: + name = entry.find('h3').contents[0] + items = entry.find_all('p') + for item in items: + text = item.getText().strip() + if 'Protection level:' in text and 'dangerous' in text: + str_list += (',' if str_list else '') + '\n "' + name + '"' + str_list = 'static final String[] dangerous = {' + str_list + '\n };\n' + return str_list + +def replace_danger(source, str_list): + print('Replacing dangerous permissions...') + idx0 = source.find('static final String[] dangerous = {') + idx1 = source[idx0:].find(' };') + return source[:idx0] + str_list + source[idx0+idx1+5:] + +java_file = '../src/processing/mode/android/Permissions.java' +ref_url = 'https://developer.android.com/reference/android/Manifest.permission.html' + +print('Reading Android reference...') +soup = get_soup(ref_url) + +print('Reading Permissions.java...') +with open(java_file, 'r') as f: + source = f.read() + +all_list = parse_all(soup) +source = replace_all(source, all_list) + +danger_list = parse_danger(soup) +source = replace_danger(source, danger_list) + +print('Writing Permissions.java...') +with open(java_file, 'w') as f: + f.write(source) + +print('Done.') \ No newline at end of file diff --git a/processing/mode/scripts/requirements.txt b/processing/mode/scripts/requirements.txt new file mode 100644 index 000000000..6f83e94d2 --- /dev/null +++ b/processing/mode/scripts/requirements.txt @@ -0,0 +1,2 @@ +beautifulsoup4 +lxml diff --git a/mode/src/processing/mode/android/AVD.java b/processing/mode/src/processing/mode/android/AVD.java similarity index 69% rename from mode/src/processing/mode/android/AVD.java rename to processing/mode/src/processing/mode/android/AVD.java index ceb7b8f5e..7674bc6b6 100644 --- a/mode/src/processing/mode/android/AVD.java +++ b/processing/mode/src/processing/mode/android/AVD.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-16 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -24,7 +24,6 @@ import processing.app.Base; import processing.app.Platform; import processing.app.Preferences; -import processing.app.exec.LineProcessor; import processing.app.exec.StreamPump; import processing.core.PApplet; @@ -39,51 +38,19 @@ public class AVD { final static private int WEAR = 1; final static public String DEFAULT_ABI = "x86"; - - public static final String TARGET_SDK_ARM = "24"; public final static String DEFAULT_PHONE_PORT = "5566"; public final static String DEFAULT_WEAR_PORT = "5576"; - - static private final String AVD_CREATE_TITLE = - "Could not create the AVD"; - - static private final String AVD_CREATE_MESSAGE = - "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.)"; - - private static final String COMMAND_LINE_TUT_URL = - "https://developer.android.com/studio/command-line/avdmanager.html"; - - static private final String AVD_LOAD_TITLE = - "Could not load the AVD."; - static private final String AVD_LOAD_MESSAGE = - "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, check this online tutorial for more info."; private static final String GETTING_START_TUT_URL = - "http://android.processing.org/tutorials/getting_started/index.html"; - - static private final String AVD_TARGET_TITLE = - "The SDK is not properly instaled"; - static private final String AVD_TARGET_MESSAGE = - "Please re-read the installation instructions for Processing
    " + - "found in this online tutorial."; - - static final String DEFAULT_SDCARD_SIZE = "64M"; - - static final String DEVICE_DEFINITION = "Nexus One"; - static final String DEVICE_SKIN = "480x800"; - - static final String DEVICE_WEAR_DEFINITION = "wear_square_280_280dpi"; - static final String DEVICE_WEAR_SKIN = "280x280"; - + "https://android.processing.org/tutorials/getting_started/index.html"; + + static final String DEVICE_DEFINITION = "pixel_6"; + static final String DEVICE_SKIN = "pixel_6"; + + static final String DEVICE_WEAR_DEFINITION = "wearos_square"; + static final String DEVICE_WEAR_SKIN = "wearos_square"; + /** Name of this avd. */ protected String name; @@ -126,18 +93,22 @@ static public String getName(boolean wear) { return AVD.phoneAVD.name; } } - + + static public String getTargetSDK(boolean wear, String abi) { + if (abi.equals("arm")) { + // The ARM images using Google APIs are too slow, so use the + // older Android (AOSP) images. + // TODO check if we can move to the regular ARM images... + return AndroidBuild.TARGET_WEAR_SDK_ARM; + } else if (abi.equals("arm64-v8a")) { + return wear ? AndroidBuild.TARGET_WEAR_SDK : AndroidBuild.TARGET_SDK; + } else { // x86 + return wear ? AndroidBuild.TARGET_WEAR_SDK : AndroidBuild.TARGET_SDK; + } + } static public String getPreferredPlatform(boolean wear, String abi) { - if (wear) { - return AndroidBuild.TARGET_PLATFORM; - } else if (abi.equals("arm")) { - // The ARM images using Google APIs are too slow, so use the - // older Android (AOSP) images. - return "android-" + TARGET_SDK_ARM; - } else { - return AndroidBuild.TARGET_PLATFORM; - } + return "android-" + getTargetSDK(wear, abi); } static public String getPreferredPort(boolean wear) { @@ -162,10 +133,9 @@ static public String getPreferredPort(boolean wear) { static protected String getPreferredTag(boolean wear, String abi) { if (wear) { return "android-wear"; - } else if (abi.equals("arm")) { - // The ARM images using Google APIs are too slow, so use the - // older Android (AOSP) images. - return "default"; +// } else if (abi.contains("arm")) { +// // The ARM images are located in the default folder. No, apparently ARM images are in google_apis too +// return "default"; } else { return "google_apis"; } @@ -188,8 +158,8 @@ static protected void list(final AndroidSDK sdk) throws IOException { try { avdList = new ArrayList(); badList = new ArrayList(); - ProcessBuilder pb = - new ProcessBuilder(sdk.getAvdManagerPath(), "list", "avd"); + File avdManager = sdk.getAVDManagerTool(); + ProcessBuilder pb = new ProcessBuilder(avdManager.getCanonicalPath(), "list", "avd"); Map env = pb.environment(); env.clear(); env.put("JAVA_HOME", Platform.getJavaHome().getCanonicalPath()); @@ -314,19 +284,20 @@ protected void refreshImages(final AndroidSDK sdk) throws IOException { protected void getImages(final ArrayList images, final AndroidSDK sdk, final String imageAbi) throws IOException { - boolean wear = type == WEAR; + final boolean wear = type == WEAR; final String imagePlatform = getPreferredPlatform(wear, imageAbi); final String imageTag = getPreferredTag(wear, imageAbi); + final File avdManager = sdk.getAVDManagerTool(); final String[] cmd = new String[] { - sdk.getAvdManagerPath(), + avdManager.getCanonicalPath(), "create", "avd", "-n", "dummy", "-k", "dummy" - }; - - // Dummy avdmanager creation command to get the list of installed images - // TODO : Find a better way to get the list of installed images + }; + + // Dummy avdmanager creation command to get the list of installed images, + // so far this is the only method available get that list. ProcessBuilder pb = new ProcessBuilder(cmd); if (Base.DEBUG) { @@ -340,23 +311,20 @@ protected void getImages(final ArrayList images, final AndroidSDK sdk, try { process = pb.start(); + StringWriter outWriter = new StringWriter(); + new StreamPump(process.getInputStream(), "out: ").addTarget(outWriter).start(); + process.waitFor(); - StreamPump output = new StreamPump(process.getInputStream(), "out: "); - output.addTarget(new LineProcessor() { - @Override - public void processLine(String line) { -// System.out.println("dummy output ---> " + line); - if (images != null && - line.contains(";" + imagePlatform) && - line.contains(";" + imageTag) && - line.contains(";" + imageAbi)) { -// System.out.println(" added image!"); - images.add(line); - } + String[] lines = PApplet.split(outWriter.toString(), '\n'); + for (String line : lines) { + if (images != null && + line.contains(";" + imagePlatform) && + line.contains(";" + imageTag) && + line.contains(";" + imageAbi)) { + images.add(line); } - }).start(); + } - process.waitFor(); } catch (final InterruptedException ie) { ie.printStackTrace(); } finally { @@ -382,6 +350,16 @@ protected String getSdkId() throws IOException { return "null"; } + protected void copyDeviceSkins(final AndroidSDK sdk, final AndroidMode mode) { + File skinsFolder = new File(sdk.getFolder(), "skins"); + if (!skinsFolder.exists()) { + // The skins in this folder come from Android Studio, on Mac they are in the folder: + // /Applications/Android Studio.app/Contents/plugins/android/resources/device-art-resources + // Apparently the skins are not available as a SDK download. + File artFolder = new File(mode.getResourcesFolder(), "device-art-resources"); + AndroidUtil.copyDir(artFolder, skinsFolder); + } + } protected boolean create(final AndroidSDK sdk) throws IOException { File sketchbookFolder = processing.app.Base.getSketchbookFolder(); @@ -389,15 +367,16 @@ protected boolean create(final AndroidSDK sdk) throws IOException { if (!androidFolder.exists()) androidFolder.mkdir(); File avdPath = new File(androidFolder, "avd/" + name); + File avdManager = sdk.getAVDManagerTool(); final String[] cmd = new String[] { - sdk.getAvdManagerPath(), + avdManager.getCanonicalPath(), "create", "avd", "-n", name, "-k", getSdkId(), - "-c", DEFAULT_SDCARD_SIZE, - "-d", device, "-p", avdPath.getAbsolutePath(), - "-f" + "-d", device, + "--skin", skin, + "--force" }; ProcessBuilder pb = new ProcessBuilder(cmd); @@ -407,9 +386,8 @@ protected boolean create(final AndroidSDK sdk) throws IOException { } // avdmanager create avd -n "Wear-Processing-0254" -k "system-images;android-25;google_apis;x86" -c 64M - // Set the list to null so that exists() will check again - avdList = null; +// avdList = null; Map env = pb.environment(); env.clear(); @@ -420,13 +398,13 @@ protected boolean create(final AndroidSDK sdk) throws IOException { process = pb.start(); // Passes 'no' to "Do you wish to create a custom hardware profile [no]" - OutputStream os = process.getOutputStream(); - PrintWriter pw = new PrintWriter(new OutputStreamWriter(os)); - pw.println("no"); - pw.flush(); - pw.close(); - os.flush(); - os.close(); +// OutputStream os = process.getOutputStream(); +// PrintWriter pw = new PrintWriter(new OutputStreamWriter(os)); +// pw.println("no"); +// pw.flush(); +// pw.close(); +// os.flush(); +// os.close(); StringWriter outWriter = new StringWriter(); new StreamPump(process.getInputStream(), "out: ").addTarget(outWriter).start(); @@ -435,26 +413,26 @@ protected boolean create(final AndroidSDK sdk) throws IOException { if (process.exitValue() == 0) { // Add skin to AVD's config file - File configFile = new File(avdPath, "config.ini"); - if (configFile.exists()) { - try (PrintWriter output = new PrintWriter(new FileWriter(configFile.getAbsolutePath(), true))) { - output.printf("%s\r\n", "skin.name=" + skin); - } - catch (Exception e) {} - } +// File configFile = new File(avdPath, "config.ini"); +// if (configFile.exists()) { +// try (PrintWriter output = new PrintWriter(new FileWriter(configFile.getAbsolutePath(), true))) { +// output.printf("%s\r\n", "skin.name=" + skin); +// } +// catch (Exception e) {} +// } return true; } if (outWriter.toString().contains("Package path is not valid")) { // They didn't install the Google APIs - AndroidUtil.showMessage(AVD_TARGET_TITLE, AVD_TARGET_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_avd.error.sdk_wrong_install_title"), + AndroidMode.getTextString("android_avd.error.sdk_wrong_install_body", GETTING_START_TUT_URL)); } else { // Just generally not working - AndroidUtil.showMessage(AVD_CREATE_TITLE, - String.format(AVD_CREATE_MESSAGE, AndroidBuild.TARGET_SDK)); + AndroidUtil.showMessage(AndroidMode.getTextString("android_avd.error.cannot_create_avd_title"), + AndroidMode.getTextString("android_avd.error.cannot_create_avd_body", AndroidBuild.TARGET_SDK)); } System.err.println(outWriter.toString()); - //System.err.println(createAvdResult); } catch (final InterruptedException ie) { ie.printStackTrace(); } finally { @@ -473,7 +451,8 @@ static public boolean ensureProperAVD(final Frame window, final AndroidMode mode return true; } if (avd.badness()) { - AndroidUtil.showMessage(AVD_LOAD_TITLE, AVD_LOAD_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_avd.error.cannot_load_avd_title"), + AndroidMode.getTextString("android_avd.error.cannot_load_avd_body")); return false; } if (!avd.hasImages(sdk)) { @@ -482,20 +461,21 @@ static public boolean ensureProperAVD(final Frame window, final AndroidMode mode // ABI again. AVD other = wear ? phoneAVD : watchAVD; boolean ask = !other.hasImages(sdk); - boolean res = AndroidSDK.locateSysImage(window, mode, wear, ask); + boolean res = AndroidSDK.requestSysImage(window, mode, wear, ask); if (!res) { return false; } else { avd.refreshImages(sdk); } } + avd.copyDeviceSkins(sdk, mode); if (avd.create(sdk)) { return true; } } catch (final Exception e) { e.printStackTrace(); - AndroidUtil.showMessage(AVD_CREATE_TITLE, - String.format(AVD_CREATE_MESSAGE, AndroidBuild.TARGET_SDK)); + AndroidUtil.showMessage(AndroidMode.getTextString("android_avd.error.cannot_create_avd_title"), + AndroidMode.getTextString("android_avd.error.cannot_create_avd_body", AndroidBuild.TARGET_SDK)); } return false; } diff --git a/mode/src/processing/mode/android/AndroidBuild.java b/processing/mode/src/processing/mode/android/AndroidBuild.java similarity index 52% rename from mode/src/processing/mode/android/AndroidBuild.java rename to processing/mode/src/processing/mode/android/AndroidBuild.java index 01bcf8804..78a16f8a3 100644 --- a/mode/src/processing/mode/android/AndroidBuild.java +++ b/processing/mode/src/processing/mode/android/AndroidBuild.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2009-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -23,9 +23,9 @@ package processing.mode.android; import org.gradle.tooling.*; + import processing.app.Base; import processing.app.Library; -import processing.app.Messages; import processing.app.Platform; import processing.app.Preferences; import processing.app.Sketch; @@ -33,10 +33,16 @@ import processing.app.Util; import processing.core.PApplet; import processing.mode.java.JavaBuild; -import processing.mode.java.preproc.SurfaceInfo; +import processing.mode.java.preproc.PdePreprocessor; import java.io.*; import java.util.HashMap; +import java.util.Properties; + +import java.util.*; +import processing.app.SketchCode; +import processing.mode.java.preproc.PreprocessorResult; + /** * Class with all the infrastructure needed to build a sketch in the Android @@ -46,101 +52,76 @@ * package ready to upload to the Play Store. */ class AndroidBuild extends JavaBuild { - static public final int APP = 0; - static public final int WALLPAPER = 1; - static public final int WATCHFACE = 2; - static public final int VR = 3; - + static public final int APP = 0; + static public final int WALLPAPER = 1; + static public final int WATCHFACE = 2; + static public final int VR = 3; + static public final int AR = 4; + // Minimum SDK's API levels required for each component: - static public final String MIN_SDK_APP = "17"; // Android 4.2 - static public final String MIN_SDK_WALLPAPER = "17"; // Android 4.2 - static public final String MIN_SDK_VR = "19"; // Android 4.4 - static public final String MIN_SDK_WATCHFACE = "25"; // Android 7.1.1 - - // Target SDK is stored in the preferences file. - static public String TARGET_SDK; - static public String TARGET_PLATFORM; - static { - TARGET_SDK = Preferences.get("android.sdk.target"); - if (TARGET_SDK == null || PApplet.parseInt(TARGET_SDK) < 26) { - TARGET_SDK = "26"; - Preferences.set("android.sdk.target", TARGET_SDK); - } - TARGET_PLATFORM = "android-" + TARGET_SDK; - } - - // Versions of support, play services, wear and VR in use, also stored in - // preferences file so they can be changed without having to rebuilt/reinstall - // the mode. - static public String SUPPORT_VER; - static { - SUPPORT_VER = Preferences.get("android.sdk.support"); - if (SUPPORT_VER == null || !versionCheck(SUPPORT_VER, "26.0.2")) { - SUPPORT_VER = "26.0.2"; - Preferences.set("android.sdk.support", SUPPORT_VER); - } - } - + static public String MIN_SDK_APP; + static public String MIN_SDK_WALLPAPER; + static public String MIN_SDK_VR; + static public String MIN_SDK_AR; + static public String MIN_SDK_WATCHFACE; + + // Versions of all required dependencies + static public String TARGET_SDK; + static public String TARGET_WEAR_SDK_ARM; + static public String TARGET_WEAR_SDK; + static public String GRADLE_PLUGIN_VER; + static public String APPCOMPAT_VER; + static public String V4LEGACY_VER; static public String PLAY_SERVICES_VER; - static { - PLAY_SERVICES_VER = Preferences.get("android.sdk.play_services"); - if (PLAY_SERVICES_VER == null || !versionCheck(PLAY_SERVICES_VER, "11.0.4")) { - PLAY_SERVICES_VER = "11.0.4"; - Preferences.set("android.sdk.play_services", PLAY_SERVICES_VER); - } - } - static public String WEAR_VER; - static { - WEAR_VER = Preferences.get("android.sdk.wear"); - if (WEAR_VER == null || !versionCheck(WEAR_VER, "2.1.0")) { - WEAR_VER = "2.1.0"; - Preferences.set("android.sdk.wear", WEAR_VER); - } - } - static public String GVR_VER; - static { - GVR_VER = Preferences.get("android.sdk.gvr"); - if (GVR_VER == null || !versionCheck(GVR_VER, "1.150.0")) { - GVR_VER = "1.150.0"; - Preferences.set("android.sdk.gvr", GVR_VER); - } - } + static public String GAR_VER; // Main activity or service static private final String APP_ACTIVITY_TEMPLATE = "AppActivity.java.tmpl"; static private final String WALLPAPER_SERVICE_TEMPLATE = "WallpaperService.java.tmpl"; static private final String WATCHFACE_SERVICE_TEMPLATE = "WatchFaceService.java.tmpl"; static private final String VR_ACTIVITY_TEMPLATE = "VRActivity.java.tmpl"; + static private final String AR_ACTIVITY_TEMPLATE = "ARActivity.java.tmpl"; // Additional resources static private final String LAYOUT_ACTIVITY_TEMPLATE = "LayoutActivity.xml.tmpl"; static private final String STYLES_FRAGMENT_TEMPLATE = "StylesFragment.xml.tmpl"; static private final String STYLES_VR_TEMPLATE = "StylesVR.xml.tmpl"; + static private final String STYLES_AR_TEMPLATE = "StylesAR.xml.tmpl"; static private final String XML_WALLPAPER_TEMPLATE = "XMLWallpaper.xml.tmpl"; static private final String STRINGS_WALLPAPER_TEMPLATE = "StringsWallpaper.xml.tmpl"; static private final String XML_WATCHFACE_TEMPLATE = "XMLWatchFace.xml.tmpl"; // Gradle build files - static private final String GRADLE_SETTINGS_TEMPLATE = "Settings.gradle.tmpl"; + static private final String GRADLE_SETTINGS_TEMPLATE = "Settings.gradle.tmpl"; + static private final String GRADLE_PROPERTIES_TEMPLATE = "Properties.gradle.tmpl"; + static private final String EXPORTED_GRADLE_PROPERTIES_TEMPLATE = "ExpProperties.gradle.tmpl"; + static private final String LOCAL_PROPERTIES_TEMPLATE = "Properties.local.tmpl"; static private final String TOP_GRADLE_BUILD_TEMPLATE = "TopBuild.gradle.tmpl"; static private final String APP_GRADLE_BUILD_ECJ_TEMPLATE = "AppBuildECJ.gradle.tmpl"; static private final String APP_GRADLE_BUILD_TEMPLATE = "AppBuild.gradle.tmpl"; static private final String VR_GRADLE_BUILD_ECJ_TEMPLATE = "VRBuildECJ.gradle.tmpl"; - static private final String VR_GRADLE_BUILD_TEMPLATE = "VRBuild.gradle.tmpl"; + static private final String VR_GRADLE_BUILD_TEMPLATE = "VRBuild.gradle.tmpl"; + static private final String AR_GRADLE_BUILD_ECJ_TEMPLATE = "ARBuildECJ.gradle.tmpl"; + static private final String AR_GRADLE_BUILD_TEMPLATE = "ARBuild.gradle.tmpl"; static private final String WEAR_GRADLE_BUILD_ECJ_TEMPLATE = "WearBuildECJ.gradle.tmpl"; static private final String WEAR_GRADLE_BUILD_TEMPLATE = "WearBuild.gradle.tmpl"; - // Icon files - static final String ICON_36 = "icon-36.png"; - static final String ICON_48 = "icon-48.png"; - static final String ICON_72 = "icon-72.png"; - static final String ICON_96 = "icon-96.png"; - static final String ICON_144 = "icon-144.png"; - static final String ICON_192 = "icon-192.png"; - static final String WATCHFACE_ICON_CIRCULAR = "preview_circular.png"; - static final String WATCHFACE_ICON_RECTANGULAR = "preview_rectangular.png"; + // Launcher and watch face icon files + static final String[] SKETCH_LAUNCHER_ICONS = {"launcher_36.png", "launcher_48.png", + "launcher_72.png", "launcher_96.png", + "launcher_144.png", "launcher_192.png"}; + static final String[] SKETCH_OLD_LAUNCHER_ICONS = {"icon-36.png", "icon-48.png", + "icon-72.png", "icon-96.png", + "icon-144.png", "icon-192.png"}; + static final String[] BUILD_LAUNCHER_ICONS = {"mipmap-ldpi/ic_launcher.png", "mipmap-mdpi/ic_launcher.png", + "mipmap-hdpi/ic_launcher.png", "mipmap-xhdpi/ic_launcher.png", + "mipmap-xxhdpi/ic_launcher.png", "mipmap-xxxhdpi/ic_launcher.png"}; + static final String[] SKETCH_WATCHFACE_ICONS = {"preview_circular.png", + "preview_rectangular.png"}; + static final String[] BUILD_WATCHFACE_ICONS = {"drawable-nodpi/preview_circular.png", + "drawable-nodpi/preview_rectangular.png"}; private int appComponent = APP; @@ -169,7 +150,7 @@ class AndroidBuild extends JavaBuild { * Constructor. * @param sketch the sketch to be built * @param mode reference to the mode - * @param appComp component (regular handheld app, wallpaper, watch face, VR) + * @param appComp component (regular handheld app, wallpaper, watch face, VR, AR) * @param emu build to run in emulator or on device if false */ public AndroidBuild(Sketch sketch, AndroidMode mode, int comp) { @@ -207,14 +188,28 @@ public boolean usesOpenGL() { public String getPathForAPK() { - String suffix = target.equals("release") ? "release_unsigned" : "debug"; + String suffix = target.equals("release") ? "release" : "debug"; String apkName = getPathToAPK() + sketch.getName().toLowerCase() + "_" + suffix + ".apk"; final File apkFile = new File(tmpFolder, apkName); if (!apkFile.exists()) { return null; } return apkFile.getAbsolutePath(); - } + } + + /** + * Build into temporary folders for building bundles (needed for the Windows 8.3 bugs in the Android SDK) + * @param target "debug" or "release" + * @throws SketchException + * @throws IOException + */ + public File buildBundle(String target, String password) throws IOException, SketchException { + this.target = target; + File folder = createProject(true, password); + if (folder == null) return null; + if (!gradleBuildBundle()) return null; + return folder; + } /** @@ -223,24 +218,24 @@ public String getPathForAPK() { * @throws SketchException * @throws IOException */ - public File build(String target) throws IOException, SketchException { + public File build(String target, String password) throws IOException, SketchException { this.target = target; - File folder = createProject(true); + File folder = createProject(true, password); if (folder == null) return null; - if (!gradleBuild()) return null; + if (!gradleBuildPackage()) return null; return folder; } /** - * Create an Gradle Android project folder, and run the preprocessor on the - * sketch. Creates the top and app modules in the case of regular, VR, and + * Create an Gradle Android project folder, and run the preprocessor on the + * sketch. Creates the top and app modules in the case of regular, VR, AR and * wallpapers, and top, mobile and wear modules in the case of watch faces. */ - protected File createProject(boolean external) + protected File createProject(boolean external, String password) throws IOException, SketchException { tmpFolder = createTempBuildFolder(sketch); - System.out.println("Build folder: " + tmpFolder.getAbsolutePath()); + System.out.println(AndroidMode.getTextString("android_build.error.build_folder", tmpFolder.getAbsolutePath())); // Create the 'src' folder with the preprocessed code. srcFolder = new File(tmpFolder, module + "/src/main/java"); @@ -250,27 +245,74 @@ protected File createProject(boolean external) } manifest = new Manifest(sketch, appComponent, mode.getFolder(), false); - manifest.setSdkTarget(TARGET_SDK); - - // build the preproc and get to work - AndroidPreprocessor preproc = new AndroidPreprocessor(sketch, getPackageName()); - // On Android, this init will throw a SketchException if there's a problem with size() - SurfaceInfo info = preproc.initSketchSize(sketch.getMainProgram()); - preproc.initSketchSmooth(sketch.getMainProgram()); - sketchClassName = preprocess(srcFolder, getPackageName(), preproc, false); - if (sketchClassName != null) { - renderer = info.getRenderer(); - writeMainClass(srcFolder, renderer, external); - createTopModule("':" + module +"'"); - createAppModule(module); + // build the preproc and get to work + String pckgName = getPackageName(); + PdePreprocessor preprocessor = PdePreprocessor.builderFor(sketch.getName()).setDestinationPackage(pckgName).build(); + PreprocessorResult result = preprocess(srcFolder, pckgName, preprocessor, false); + if (result != null) { + sketchClassName = result.getClassName(); + if (sketchClassName != null) { + renderer = result.getSketchRenderer(); + if (renderer == null) { + renderer = "JAVA2D"; + } + writeMainClass(srcFolder, external); + createTopModule("':" + module + "'", password); + createAppModule(module); + } } return tmpFolder; } + + + protected boolean gradleBuildBundle() throws SketchException { + ProjectConnection connection = GradleConnector.newConnector() + .forProjectDirectory(tmpFolder) + .connect(); + + boolean success = false; + BuildLauncher build = connection.newBuild(); + build.setStandardOutput(System.out); + build.setStandardError(System.err); + + try { + if (target.equals("debug")) build.forTasks("bundleDebug"); + else build.forTasks("bundleRelease"); + build.run(); + renameAAB(); + success = true; + } catch (org.gradle.tooling.UnsupportedVersionException e) { + e.printStackTrace(); + success = false; + } catch (org.gradle.tooling.BuildException e) { + e.printStackTrace(); + success = false; + } catch (org.gradle.tooling.BuildCancelledException e) { + e.printStackTrace(); + success = false; + } catch (org.gradle.tooling.GradleConnectionException e) { + e.printStackTrace(); + success = false; + } catch (Exception e) { + e.printStackTrace(); + success = false; + } finally { + connection.close(); + } + + try { + removeKeyPassword(); + } catch (IOException e) { + e.printStackTrace(); + } + + return success; + } - protected boolean gradleBuild() throws SketchException { + protected boolean gradleBuildPackage() throws SketchException { ProjectConnection connection = GradleConnector.newConnector() .forProjectDirectory(tmpFolder) .connect(); @@ -305,6 +347,12 @@ protected boolean gradleBuild() throws SketchException { connection.close(); } + try { + removeKeyPassword(); + } catch (IOException e) { + e.printStackTrace(); + } + return success; } @@ -313,21 +361,60 @@ protected boolean gradleBuild() throws SketchException { // Gradle modules - private void createTopModule(String projectModules) - throws IOException { + private void createTopModule(String projectModules, String keyPassword) + throws IOException { + HashMap replaceMap = new HashMap(); + File buildTemplate = mode.getContentFile("templates/" + TOP_GRADLE_BUILD_TEMPLATE); File buildlFile = new File(tmpFolder, "build.gradle"); - Util.copyFile(buildTemplate, buildlFile); - - writeLocalProps(new File(tmpFolder, "local.properties")); - AndroidUtil.writeFile(new File(tmpFolder, "gradle.properties"), - new String[]{"org.gradle.jvmargs=-Xmx1536m"}); + replaceMap.put("@@gradle_plugin_version@@", GRADLE_PLUGIN_VER); + AndroidUtil.createFileFromTemplate(buildTemplate, buildlFile, replaceMap); + +// File gradlePropsTemplate = mode.getContentFile("templates/" + GRADLE_PROPERTIES_TEMPLATE); +// File gradlePropsFile = new File(tmpFolder, "gradle.properties"); +// Util.copyFile(gradlePropsTemplate, gradlePropsFile); + File gradlePropsTemplate; + if (exportProject) { + gradlePropsTemplate = mode.getContentFile("templates/" + EXPORTED_GRADLE_PROPERTIES_TEMPLATE); + } else { + gradlePropsTemplate = mode.getContentFile("templates/" + GRADLE_PROPERTIES_TEMPLATE); + } + File gradlePropsFile = new File(tmpFolder, "gradle.properties"); + String javaHome = Platform.getJavaHome().getAbsolutePath(); + replaceMap.clear(); + replaceMap.put("@@java_home@@", javaHome); + if (!keyPassword.equals("") && AndroidKeyStore.getKeyStore() != null) { + replaceMap.put("@@keystore_file@@", AndroidKeyStore.getKeyStore().getAbsolutePath().replace('\\', '/')); + replaceMap.put("@@key_alias@@", AndroidKeyStore.ALIAS_STRING); + replaceMap.put("@@key_password@@", keyPassword); + } + AndroidUtil.createFileFromTemplate(gradlePropsTemplate, gradlePropsFile, replaceMap); - File settingsTemplate = mode.getContentFile("templates/" + GRADLE_SETTINGS_TEMPLATE); + File settingsTemplate = mode.getContentFile("templates/" + GRADLE_SETTINGS_TEMPLATE); File settingsFile = new File(tmpFolder, "settings.gradle"); - HashMap replaceMap = new HashMap(); - replaceMap.put("@@project_modules@@", projectModules); - AndroidUtil.createFileFromTemplate(settingsTemplate, settingsFile, replaceMap); + replaceMap.clear(); + if (getAppComponent() == VR) { + // The local google-vr has to be added to the settings to fix Issue #718 + replaceMap.put("@@project_modules@@", projectModules + ", ':app:libs:google-vr'"); + } else { + replaceMap.put("@@project_modules@@", projectModules); + } + + AndroidUtil.createFileFromTemplate(settingsTemplate, settingsFile, replaceMap); + + File localPropsTemplate = mode.getContentFile("templates/" + LOCAL_PROPERTIES_TEMPLATE); + File localPropsFile = new File(tmpFolder, "local.properties"); + replaceMap.clear(); + final String sdkPath = sdk.getFolder().getAbsolutePath(); + if (Platform.isWindows()) { + // Windows needs backslashes escaped, or it will also accept forward + // slashes in the build file. We're using the forward slashes since this + // path gets concatenated with a lot of others that use forwards anyway. + replaceMap.put("@@sdk_path@@", sdkPath.replace('\\', '/')); + } else { + replaceMap.put("@@sdk_path@@", sdkPath); + } + AndroidUtil.createFileFromTemplate(localPropsTemplate, localPropsFile, replaceMap); } @@ -337,7 +424,10 @@ private void createAppModule(String moduleName) String minSdk; String tmplFile; - if (appComponent == VR) { + if (appComponent == AR) { + minSdk = MIN_SDK_AR; + tmplFile = exportProject ? AR_GRADLE_BUILD_TEMPLATE : AR_GRADLE_BUILD_ECJ_TEMPLATE; + } else if (appComponent == VR) { minSdk = MIN_SDK_VR; tmplFile = exportProject ? VR_GRADLE_BUILD_TEMPLATE : VR_GRADLE_BUILD_ECJ_TEMPLATE; } else if (appComponent == WATCHFACE) { @@ -348,18 +438,25 @@ private void createAppModule(String moduleName) tmplFile = exportProject ? APP_GRADLE_BUILD_TEMPLATE : APP_GRADLE_BUILD_ECJ_TEMPLATE; } + String modePath = new File(mode.getFolder(), "mode").getPath().replace('\\', '/'); + String toolPath = Base.getToolsFolder().getPath().replace('\\', '/'); + String platformPath = sdk.getTargetPlatform(TARGET_SDK).getPath().replace('\\', '/'); + File appBuildTemplate = mode.getContentFile("templates/" + tmplFile); File appBuildFile = new File(moduleFolder, "build.gradle"); HashMap replaceMap = new HashMap(); - replaceMap.put("@@tools_folder@@", Base.getToolsFolder().getPath().replace('\\', '/')); - replaceMap.put("@@target_platform@@", sdk.getTargetPlatform().getPath().replace('\\', '/')); + replaceMap.put("@@mode_folder@@", modePath); + replaceMap.put("@@tools_folder@@", toolPath); + replaceMap.put("@@target_platform@@", platformPath); replaceMap.put("@@package_name@@", getPackageName()); replaceMap.put("@@min_sdk@@", minSdk); replaceMap.put("@@target_sdk@@", TARGET_SDK); - replaceMap.put("@@support_version@@", SUPPORT_VER); + replaceMap.put("@@appcompat_version@@", APPCOMPAT_VER); + replaceMap.put("@@v4legacy_version@@", V4LEGACY_VER); replaceMap.put("@@play_services_version@@", PLAY_SERVICES_VER); replaceMap.put("@@wear_version@@", WEAR_VER); replaceMap.put("@@gvr_version@@", GVR_VER); + replaceMap.put("@@gar_version@@", GAR_VER); replaceMap.put("@@version_code@@", manifest.getVersionCode()); replaceMap.put("@@version_name@@", manifest.getVersionName()); AndroidUtil.createFileFromTemplate(appBuildTemplate, appBuildFile, replaceMap); @@ -381,18 +478,13 @@ private void createAppModule(String moduleName) // Copy any imported libraries (their libs and assets), // and anything in the code folder contents to the project. - copyImportedLibs(libsFolder, assetsFolder); + copyImportedLibs(libsFolder, mainFolder, assetsFolder); copyCodeFolder(libsFolder); - // Copy any system libraries needed by the project -// copyWearLib(libsFolder); -// copySupportLibs(libsFolder); -// if (getAppComponent() == APP) { -// copyAppCompatLib(libsFolder); -// } -// if (getAppComponent() == VR) { -// copyGVRLibs(libsFolder); -// } + if (getAppComponent() == VR) { + // Need to call this to fix Issue #718 + copyGVRLibs(libsFolder); + } // Copy the data folder (if one exists) to the project's 'assets' folder final File sketchDataFolder = sketch.getDataFolder(); @@ -413,8 +505,7 @@ private void createAppModule(String moduleName) // Templates - private void writeMainClass(final File srcDirectory, - final String renderer, final boolean external) { + private void writeMainClass(final File srcDirectory, final boolean external) { int comp = getAppComponent(); String[] permissions = manifest.getPermissions(); if (comp == APP) { @@ -429,6 +520,8 @@ private void writeMainClass(final File srcDirectory, } } else if (comp == VR) { writeVRActivity(srcDirectory, permissions, external); + } else if (comp == AR) { + writeARActivity(srcDirectory, permissions, external); } } @@ -504,6 +597,19 @@ private void writeVRActivity(final File srcDirectory, String[] permissions, AndroidUtil.createFileFromTemplate(javaTemplate, javaFile, replaceMap); } + private void writeARActivity(final File srcDirectory, String[] permissions, + final boolean external) { + File javaTemplate = mode.getContentFile("templates/" + AR_ACTIVITY_TEMPLATE); + File javaFile = new File(new File(srcDirectory, getPackageName().replace(".", "/")), "MainActivity.java"); + + HashMap replaceMap = new HashMap(); + replaceMap.put("@@package_name@@", getPackageName()); + replaceMap.put("@@sketch_class_name@@", sketchClassName); + replaceMap.put("@@external@@", external ? "sketch.setExternal(true);" : ""); + + AndroidUtil.createFileFromTemplate(javaTemplate, javaFile, replaceMap); + } + private void writeResLayoutMainActivity(final File layoutFolder) { File xmlTemplate = mode.getContentFile("templates/" + LAYOUT_ACTIVITY_TEMPLATE); @@ -528,6 +634,13 @@ private void writeResStylesVR(final File valuesFolder) { File xmlFile = new File(valuesFolder, "styles.xml"); AndroidUtil.createFileFromTemplate(xmlTemplate, xmlFile); } + + + private void writeResStylesAR(final File valuesFolder) { + File xmlTemplate = mode.getContentFile("templates/" + STYLES_AR_TEMPLATE); + File xmlFile = new File(valuesFolder, "styles.xml"); + AndroidUtil.createFileFromTemplate(xmlTemplate, xmlFile); + } private void writeResXMLWallpaper(final File xmlFolder) { @@ -555,22 +668,6 @@ private void writeResXMLWatchFace(final File xmlFolder) { } - private void writeLocalProps(final File file) { - final PrintWriter writer = PApplet.createWriter(file); - final String sdkPath = sdk.getSdkFolder().getAbsolutePath(); - if (Platform.isWindows()) { - // Windows needs backslashes escaped, or it will also accept forward - // slashes in the build file. We're using the forward slashes since this - // path gets concatenated with a lot of others that use forwards anyway. - writer.println("sdk.dir=" + sdkPath.replace('\\', '/')); - } else { - writer.println("sdk.dir=" + sdkPath); - } - writer.flush(); - writer.close(); - } - - private void writeRes(File resFolder) throws SketchException { File layoutFolder = AndroidUtil.createPath(resFolder, "layout"); writeResLayoutMainActivity(layoutFolder); @@ -590,13 +687,16 @@ private void writeRes(File resFolder) throws SketchException { } else if (comp == VR) { File valuesFolder = AndroidUtil.createPath(resFolder, "values"); writeResStylesVR(valuesFolder); - } + } else if (comp == AR) { + File valuesFolder = AndroidUtil.createPath(resFolder, "values"); + writeResStylesAR(valuesFolder); + } File sketchFolder = sketch.getFolder(); - writeAppIconFiles(sketchFolder, resFolder); + writeLauncherIconFiles(sketchFolder, resFolder); if (comp == WATCHFACE) { // Need the preview icons for watch faces. - writeWatchIconFiles(sketchFolder, resFolder); + writeWatchFaceIconFiles(sketchFolder, resFolder); } } @@ -605,74 +705,38 @@ private void writeRes(File resFolder) throws SketchException { // Icons - private void writeAppIconFiles(File sketchFolder, File resFolder) { - File localIcon36 = new File(sketchFolder, ICON_36); - File localIcon48 = new File(sketchFolder, ICON_48); - File localIcon72 = new File(sketchFolder, ICON_72); - File localIcon96 = new File(sketchFolder, ICON_96); - File localIcon144 = new File(sketchFolder, ICON_144); - File localIcon192 = new File(sketchFolder, ICON_192); - - File buildIcon48 = new File(resFolder, "drawable/icon.png"); - File buildIcon36 = new File(resFolder, "drawable-ldpi/icon.png"); - File buildIcon72 = new File(resFolder, "drawable-hdpi/icon.png"); - File buildIcon96 = new File(resFolder, "drawable-xhdpi/icon.png"); - File buildIcon144 = new File(resFolder, "drawable-xxhdpi/icon.png"); - File buildIcon192 = new File(resFolder, "drawable-xxxhdpi/icon.png"); - - if (!localIcon36.exists() && !localIcon48.exists() && - !localIcon72.exists() && !localIcon96.exists() && - !localIcon144.exists() && !localIcon192.exists()) { - try { - // if no icons are in the sketch folder, then copy all the defaults - copyIcon(mode.getContentFile("icons/" + ICON_36), buildIcon36); - copyIcon(mode.getContentFile("icons/" + ICON_48), buildIcon48); - copyIcon(mode.getContentFile("icons/" + ICON_72), buildIcon72); - copyIcon(mode.getContentFile("icons/" + ICON_96), buildIcon96); - copyIcon(mode.getContentFile("icons/" + ICON_144), buildIcon144); - copyIcon(mode.getContentFile("icons/" + ICON_192), buildIcon192); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - // if at least one of the icons already exists, then use that across the board - try { - if (localIcon36.exists()) copyIcon(localIcon36, buildIcon36); - if (localIcon48.exists()) copyIcon(localIcon48, buildIcon48); - if (localIcon72.exists()) copyIcon(localIcon72, buildIcon72); - if (localIcon96.exists()) copyIcon(localIcon96, buildIcon96); - if (localIcon144.exists()) copyIcon(localIcon144, buildIcon144); - if (localIcon192.exists()) copyIcon(localIcon192, buildIcon192); - } catch (IOException e) { - System.err.println("Problem while copying app icons."); - e.printStackTrace(); - } - } + private void writeLauncherIconFiles(File sketchFolder, File resFolder) { + writeIconFiles(sketchFolder, resFolder, SKETCH_LAUNCHER_ICONS, SKETCH_OLD_LAUNCHER_ICONS, BUILD_LAUNCHER_ICONS); } - private void writeWatchIconFiles(File sketchFolder, File resFolder) { - copyWatchIcon(new File(sketchFolder, WATCHFACE_ICON_CIRCULAR), - new File(resFolder, "drawable/preview_circular.png"), - mode.getContentFile("icons/" + WATCHFACE_ICON_CIRCULAR)); - copyWatchIcon(new File(sketchFolder, WATCHFACE_ICON_RECTANGULAR), - new File(resFolder, "drawable/preview_rectangular.png"), - mode.getContentFile("icons/" + WATCHFACE_ICON_RECTANGULAR)); + private void writeWatchFaceIconFiles(File sketchFolder, File resFolder) { + writeIconFiles(sketchFolder, resFolder, SKETCH_WATCHFACE_ICONS, null, BUILD_WATCHFACE_ICONS); } - private void copyWatchIcon(File srcFile, File destFile, File defFile) { - if (!srcFile.exists()) { + private void writeIconFiles(File sketchFolder, File resFolder, + String[] sketchIconNames, String[] oldIconNames, String[] buildIconNames) { + File[] localIcons = AndroidUtil.getFileList(sketchFolder, sketchIconNames, oldIconNames); + File[] buildIcons = AndroidUtil.getFileList(resFolder, buildIconNames); + if (AndroidUtil.noFileExists(localIcons)) { + // If no icons are in the sketch folder, then copy all the defaults + File[] defaultIcons = AndroidUtil.getFileList(mode, "icons/", sketchIconNames); try { - copyIcon(defFile, destFile); + for (int i = 0; i < localIcons.length; i++) { + copyIcon(defaultIcons[i], buildIcons[i]); + } } catch (IOException e) { e.printStackTrace(); - } + } } else { + // If at least one of the icons already exists, then use that across the board try { - copyIcon(srcFile, destFile); + for (int i = 0; i < localIcons.length; i++) { + if (localIcons[i].exists()) copyIcon(localIcons[i], buildIcons[i]); + } } catch (IOException e) { - System.err.println("Problem while copying watch face icon."); + System.err.println(AndroidMode.getTextString("android_build.error.cannot_copy_icons")); e.printStackTrace(); } } @@ -684,73 +748,11 @@ private void copyIcon(File srcFile, File destFile) throws IOException { if (parent.exists() || parent.mkdirs()) { Util.copyFile(srcFile, destFile); } else { - System.err.println("Could not create \"" + destFile.getParentFile() + "\" folder."); + System.err.println(AndroidMode.getTextString("android_build.error.cannot_create_icon_folder", destFile.getParentFile())); } } - - - // --------------------------------------------------------------------------- - // Dependencies - - -// private void copyWearLib(File libsFolder) throws IOException { - // The wear aar is needed even when the app is not a watch face, because on - // devices with android < 5 the dependencies of the PWatchFace* classes - // cannot be resolved. -// copyAARFileFromSDK(sdk.getWearableFolder() + "/$VER", "wearable-$VER.aar", WEAR_VER, libsFolder); -// } - - -// private void copySupportLibs(File libsFolder) throws IOException { -// copyAARFileFromSDK(sdk.getSupportLibrary() + "/support-core-utils/$VER", "support-core-utils-$VER.aar", SUPPORT_VER, libsFolder); -// copyAARFileFromSDK(sdk.getSupportLibrary() + "/support-compat/$VER", "support-compat-$VER.aar", SUPPORT_VER, libsFolder); -// copyAARFileFromSDK(sdk.getSupportLibrary() + "/support-fragment/$VER", "support-fragment-$VER.aar", SUPPORT_VER, libsFolder); -// copyAARFileFromSDK(sdk.getSupportLibrary() + "/support-vector-drawable/$VER", "support-vector-drawable-$VER.aar", SUPPORT_VER, libsFolder); -// } - - -// private void copyAppCompatLib(File libsFolder) throws IOException { -// copyAARFileFromSDK(sdk.getSupportLibrary() + "/appcompat-v7/$VER", "appcompat-v7-$VER.aar", SUPPORT_VER, libsFolder); -// } - - -// private void copyGVRLibs(File libsFolder) throws IOException { -// copyAARFileFromMode("/libraries/vr/gvrsdk/$VER", "sdk-base-$VER.aar", GVR_VER, libsFolder); -// copyAARFileFromMode("/libraries/vr/gvrsdk/$VER", "sdk-common-$VER.aar", GVR_VER, libsFolder); -// copyAARFileFromMode("/libraries/vr/gvrsdk/$VER", "sdk-audio-$VER.aar", GVR_VER, libsFolder); -// } - - /* - private void copyAARFileFromSDK(String srcFolder, String filename, String version, File destFolder) - throws IOException { - String fn = filename.replace("$VER", version); - File srcFile = new File(srcFolder.replace("$VER", version), fn); - File destFile = new File(destFolder, fn); - if (srcFile.exists()) { - Util.copyFile(srcFile, destFile); - } else { - // If the AAR file does not exist in the installed SDK, gradle should be able to download it, and so - // we don't to anything besides printing a warning. - System.out.println("Warning: cannot find AAR package " + fn + " in installed SDK, gradle will try to download."); - } - } - private void copyAARFileFromMode(String srcFolder, String filename, String version, File destFolder) - throws IOException { - String fn = filename.replace("$VER", version); - File srcFile = mode.getContentFile(srcFolder.replace("$VER", version) + "/" + fn); - File destFile = new File(destFolder, fn); - if (srcFile.exists()) { - Util.copyFile(srcFile, destFile); - } else { - // If the AAR file does not exist in the mode, gradle should be able to download it, and so - // we don't to anything besides printing a warning. - System.out.println("Warning: cannot find AAR package " + fn + " in Android mode, gradle will try to download"); - } - } - */ - // --------------------------------------------------------------------------- // Export project @@ -759,14 +761,29 @@ public File exportProject() throws IOException, SketchException { target = "debug"; exportProject = true; - File projectFolder = createProject(false); + File projectFolder = createProject(false, ""); exportProject = false; File exportFolder = createExportFolder("android"); Util.copyDir(projectFolder, exportFolder); installGradlew(exportFolder); return exportFolder; - } + } + + + // --------------------------------------------------------------------------- + // Export bundle + + + public File exportBundle(String keyStorePassword) throws Exception { + File projectFolder = buildBundle("release", keyStorePassword); + if (projectFolder == null) return null; + + // Final export folder + File exportFolder = createExportFolder("buildBundle"); + Util.copyDir(new File(projectFolder, getPathToAAB()), exportFolder); + return exportFolder; + } // --------------------------------------------------------------------------- @@ -774,67 +791,14 @@ public File exportProject() throws IOException, SketchException { public File exportPackage(String keyStorePassword) throws Exception { - File projectFolder = build("release"); + File projectFolder = build("release", keyStorePassword); if (projectFolder == null) return null; - File signedPackage = signPackage(projectFolder, keyStorePassword); - if (signedPackage == null) return null; - // Final export folder - File exportFolder = createExportFolder("build"); + File exportFolder = createExportFolder("buildPackage"); Util.copyDir(new File(projectFolder, getPathToAPK()), exportFolder); return exportFolder; } - - - private File signPackage(File projectFolder, String keyStorePassword) throws Exception { - File keyStore = AndroidKeyStore.getKeyStore(); - if (keyStore == null) return null; - - File unsignedPackage = new File(projectFolder, - getPathToAPK() + sketch.getName().toLowerCase() + "_release_unsigned.apk"); - if (!unsignedPackage.exists()) return null; - File signedPackage = new File(projectFolder, - getPathToAPK() + sketch.getName().toLowerCase() + "_release_signed.apk"); - - JarSigner.signJar(unsignedPackage, signedPackage, - AndroidKeyStore.ALIAS_STRING, keyStorePassword, - keyStore.getAbsolutePath(), keyStorePassword); - - File alignedPackage = zipalignPackage(signedPackage, projectFolder); - return alignedPackage; - } - - - private File zipalignPackage(File signedPackage, File projectFolder) - throws IOException, InterruptedException { - File zipAlign = sdk.getZipAlignTool(); - if (zipAlign == null || !zipAlign.exists()) { - Messages.showWarning("Cannot find zipaling...", - "The zipalign build tool needed to prepare the export package is missing.\n" + - "Make sure that your Android SDK was downloaded correctly."); - return null; - } - - File alignedPackage = new File(projectFolder, - getPathToAPK() + sketch.getName().toLowerCase() + "_release_signed_aligned.apk"); - - String[] args = { - zipAlign.getAbsolutePath(), "-v", "-f", "4", - signedPackage.getAbsolutePath(), alignedPackage.getAbsolutePath() - }; - - Process alignProcess = Runtime.getRuntime().exec(args); - // Need to consume output for the process to finish, as discussed here - // https://stackoverflow.com/questions/5483830/process-waitfor-never-returns - // Using StreamPump as in other parts of the mode does not seem to work for some reason - BufferedReader reader = new BufferedReader(new InputStreamReader(alignProcess.getInputStream())); - while ((reader.readLine()) != null) {} - alignProcess.waitFor(); - - if (alignedPackage.exists()) return alignedPackage; - return null; - } //--------------------------------------------------------------------------- @@ -867,48 +831,83 @@ protected boolean ignorableImport(String pkg) { * For each library, copy .jar and .zip files to the 'libs' folder, * and copy anything else to the 'assets' folder. */ - private void copyImportedLibs(final File libsFolder, + private void copyImportedLibs(final File libsFolder, + final File mainFolder, final File assetsFolder) throws IOException { for (Library library : getImportedLibraries()) { - // add each item from the library folder / export list to the output - for (File exportFile : library.getAndroidExports()) { - String exportName = exportFile.getName(); + // Add each item from the library folder / export list to the output + for (File exportFile : library.getApplicationExports("armeabi")) { + copyImportedLib(libsFolder, mainFolder, assetsFolder, exportFile); + } + for (File exportFile : library.getApplicationExports("armeabi-v7a")) { + copyImportedLib(libsFolder, mainFolder, assetsFolder, exportFile); + } + for (File exportFile : library.getApplicationExports("x86")) { + copyImportedLib(libsFolder, mainFolder, assetsFolder, exportFile); + } + for (File exportFile : library.getApplicationExports("arm64-v8a")) { + copyImportedLib(libsFolder, mainFolder, assetsFolder, exportFile); + } + for (File exportFile : library.getApplicationExports("x86_64")) { + copyImportedLib(libsFolder, mainFolder, assetsFolder, exportFile); + } + } + } + + private void copyImportedLib(final File libsFolder, + final File mainFolder, + final File assetsFolder, + final File exportFile) throws IOException { + String exportName = exportFile.getName(); - // Skip the GVR jars, because the gradle will resolve the dependencies - if (appComponent == VR && exportName.toLowerCase().startsWith("sdk-")) continue; - - if (!exportFile.exists()) { - System.err.println(exportFile.getName() + - " is mentioned in export.txt, but it's " + - "a big fat lie and does not exist."); - } else if (exportFile.isDirectory()) { - // Copy native library folders to the correct location - if (exportName.equals("armeabi") || - exportName.equals("armeabi-v7a") || - exportName.equals("x86")) { - Util.copyDir(exportFile, new File(libsFolder, exportName)); - } else { - // Copy any other directory to the assets folder - Util.copyDir(exportFile, new File(assetsFolder, exportName)); - } - } else if (exportName.toLowerCase().endsWith(".zip")) { - // As of r4 of the Android SDK, it looks like .zip files - // are ignored in the libs folder, so rename to .jar - System.err.println(".zip files are not allowed in Android libraries."); - System.err.println("Please rename " + exportFile.getName() + " to be a .jar file."); - String jarName = exportName.substring(0, exportName.length() - 4) + ".jar"; - Util.copyFile(exportFile, new File(libsFolder, jarName)); + // Skip the GVR and ARCore jars, because gradle will resolve the dependencies + if (appComponent == VR && exportName.toLowerCase().startsWith("sdk")) return; + if (appComponent == AR && exportName.toLowerCase().startsWith("core")) return; + + if (!exportFile.exists()) { + System.err.println(AndroidMode.getTextString("android_build.error.export_file_does_not_exist", exportFile.getName())); + } else if (exportFile.isDirectory()) { + // Copy native library folders to the correct location + if (exportName.equals("armeabi") || + exportName.equals("armeabi-v7a") || + exportName.equals("x86") || + exportName.equals("arm64-v8a") || + exportName.equals("x86_64")) + { + Util.copyDir(exportFile, new File(libsFolder, exportName)); + } + // Copy jni libraries (.so files) to the correct location + else if (exportName.equals("jniLibs")) { + Util.copyDir(exportFile, new File(mainFolder, exportName)); + } + else { + // Copy any other directory to the assets folder + Util.copyDir(exportFile, new File(assetsFolder, exportName)); + } + } else if (exportName.toLowerCase().endsWith(".zip")) { + // As of r4 of the Android SDK, it looks like .zip files + // are ignored in the libs folder, so rename to .jar + System.err.println(AndroidMode.getTextString("android_build.error.zip_files_not_allowed", exportFile.getName())); + String jarName = exportName.substring(0, exportName.length() - 4) + ".jar"; + Util.copyFile(exportFile, new File(libsFolder, jarName)); - } else if (exportName.toLowerCase().endsWith(".jar")) { - Util.copyFile(exportFile, new File(libsFolder, exportName)); + } else if (exportName.toLowerCase().endsWith(".jar")) { + Util.copyFile(exportFile, new File(libsFolder, exportName)); - } else { - Util.copyFile(exportFile, new File(assetsFolder, exportName)); - } - } + } else { + Util.copyFile(exportFile, new File(assetsFolder, exportName)); } } + /** + * Copy the dummy Gradle project containing aar files from Google VR, + * so they can be imported locally from the project + */ + private void copyGVRLibs(final File libsFolder) throws IOException { + File srcFolder = new File(mode.getFolder(), "libraries/vr/libs/google-vr"); + File dstFolder = new File(libsFolder, "google-vr"); + Util.copyDir(srcFolder, dstFolder); + } private void copyCodeFolder(final File libsFolder) throws IOException { // Copy files from the 'code' directory into the 'libs' folder @@ -925,15 +924,32 @@ private void copyCodeFolder(final File libsFolder) throws IOException { } } } - } + } + + private void renameAAB() { + String suffix = target.equals("release") ? "release" : "debug"; + String aabName = getPathToAAB() + module + "-" + suffix + ".aab"; + final File aabFile = new File(tmpFolder, aabName); + if (aabFile.exists()) { + String suffixNew = target.equals("release") ? "release" : "debug"; + String aabNameNew = getPathToAAB() + + sketch.getName().toLowerCase() + "_" + suffixNew + ".aab"; + final File aabFileNew = new File(tmpFolder, aabNameNew); + aabFile.renameTo(aabFileNew); + } + } + + private String getPathToAAB() { + return module + "/build/outputs/bundle/" + target + "/"; + } private void renameAPK() { - String suffix = target.equals("release") ? "release-unsigned" : "debug"; + String suffix = target.equals("release") ? "release" : "debug"; String apkName = getPathToAPK() + module + "-" + suffix + ".apk"; final File apkFile = new File(tmpFolder, apkName); if (apkFile.exists()) { - String suffixNew = target.equals("release") ? "release_unsigned" : "debug"; + String suffixNew = target.equals("release") ? "release" : "debug"; String apkNameNew = getPathToAPK() + sketch.getName().toLowerCase() + "_" + suffixNew + ".apk"; final File apkFileNew = new File(tmpFolder, apkNameNew); @@ -941,6 +957,13 @@ private void renameAPK() { } } + + private void removeKeyPassword() throws IOException { + File gradlePropsTemplate = mode.getContentFile("templates/" + GRADLE_PROPERTIES_TEMPLATE); + File gradlePropsFile = new File(tmpFolder, "gradle.properties"); + Util.copyFile(gradlePropsTemplate, gradlePropsFile); + } + private String getPathToAPK() { return module + "/build/outputs/apk/" + target + "/"; @@ -960,7 +983,7 @@ private String getPathToAPK() { private File createTempBuildFolder(final Sketch sketch) throws IOException { final File tmp = File.createTempFile("android", "sketch"); if (!(tmp.delete() && tmp.mkdir())) { - throw new IOException("Cannot create temp dir " + tmp + " to build android sketch"); + throw new IOException(AndroidMode.getTextString("android_build.error.cannot_create_build_folder", tmp)); } return tmp; } @@ -968,7 +991,7 @@ private File createTempBuildFolder(final Sketch sketch) throws IOException { private void installGradlew(File exportFolder) throws IOException { File gradlewFile = mode.getContentFile("mode/gradlew.zip"); - AndroidUtil.extractFolder(gradlewFile, exportFolder, false); + AndroidUtil.extractFolder(gradlewFile, exportFolder); if (Platform.isMacOS() || Platform.isLinux()) { File execFile = new File(exportFolder, "gradlew"); execFile.setExecutable(true); @@ -981,6 +1004,99 @@ private File createExportFolder(String name) throws IOException { } + static public void initVersions(File file) { + InputStream input; + try { + input = new FileInputStream(file); + Properties props = new Properties(); + props.load(input); + + MIN_SDK_APP = props.getProperty("android-min-app"); + MIN_SDK_WALLPAPER = props.getProperty("android-min-wallpaper"); + MIN_SDK_VR = props.getProperty("android-min-vr"); + MIN_SDK_AR = props.getProperty("android-min-ar"); + MIN_SDK_WATCHFACE = props.getProperty("android-min-wear"); + + // Versions strings of all dependencies are stored in a preferences file so they can be changed by the + // user without having to rebuild/reinstall the mode. + + GRADLE_PLUGIN_VER = Preferences.get("android.gradle_plugin"); + String defGradlePluginVersion = props.getProperty("android-gradle-plugin"); + if (GRADLE_PLUGIN_VER == null || PApplet.parseInt(GRADLE_PLUGIN_VER) != PApplet.parseInt(defGradlePluginVersion)) { + GRADLE_PLUGIN_VER = defGradlePluginVersion; + Preferences.set("android.gradle_plugin", GRADLE_PLUGIN_VER); + } + + TARGET_SDK = Preferences.get("android.sdk.target"); + String defTargetSDK = props.getProperty("android-platform"); + if (TARGET_SDK == null || PApplet.parseInt(TARGET_SDK) != PApplet.parseInt(defTargetSDK)) { + TARGET_SDK = defTargetSDK; + Preferences.set("android.sdk.target", TARGET_SDK); + } + + TARGET_WEAR_SDK_ARM = Preferences.get("android.sdk.target.wear_arm"); + String defTargetWearSDKArm = props.getProperty("android-platform-wear-arm"); + if (TARGET_WEAR_SDK_ARM == null || PApplet.parseInt(TARGET_WEAR_SDK_ARM) != PApplet.parseInt(defTargetWearSDKArm)) { + TARGET_WEAR_SDK_ARM = defTargetWearSDKArm; + Preferences.set("android.sdk.target.wear_arm", TARGET_WEAR_SDK_ARM); + } + + TARGET_WEAR_SDK = Preferences.get("android.sdk.target.wear"); + String defTargetWearSDK = props.getProperty("android-platform-wear"); + if (TARGET_WEAR_SDK == null || PApplet.parseInt(TARGET_WEAR_SDK) != PApplet.parseInt(defTargetWearSDK)) { + TARGET_WEAR_SDK = defTargetWearSDK; + Preferences.set("android.sdk.target.wear", TARGET_WEAR_SDK); + } + + APPCOMPAT_VER = Preferences.get("android.sdk.appcompat"); + String defAppCompatVer = props.getProperty("androidx.appcompat%appcompat"); + if (APPCOMPAT_VER == null || !versionCheck(APPCOMPAT_VER, defAppCompatVer)) { + APPCOMPAT_VER = defAppCompatVer; + Preferences.set("android.sdk.appcompat", APPCOMPAT_VER); + } + + V4LEGACY_VER = Preferences.get("android.sdk.v4legacy"); + String defV4LegacyVer = props.getProperty("androidx.legacy%legacy-support-v4"); + if (V4LEGACY_VER == null || !versionCheck(V4LEGACY_VER, defV4LegacyVer)) { + V4LEGACY_VER = defV4LegacyVer; + Preferences.set("android.sdk.v4legacy", V4LEGACY_VER); + } + + PLAY_SERVICES_VER = Preferences.get("android.sdk.play_services"); + String defPlayServicesVer = props.getProperty("com.google.android.gms%play-services-wearable"); + if (PLAY_SERVICES_VER == null || !versionCheck(PLAY_SERVICES_VER, defPlayServicesVer)) { + PLAY_SERVICES_VER = defPlayServicesVer; + Preferences.set("android.sdk.play_services", PLAY_SERVICES_VER); + } + + WEAR_VER = Preferences.get("android.sdk.wear"); + String defWearVer = props.getProperty("com.google.android.support%wearable"); + if (WEAR_VER == null || !versionCheck(WEAR_VER, defWearVer)) { + WEAR_VER = defWearVer; + Preferences.set("android.sdk.wear", WEAR_VER); + } + + GVR_VER = Preferences.get("android.sdk.gvr"); + String defVRVer = props.getProperty("com.google.vr"); + if (GVR_VER == null || !versionCheck(GVR_VER, defVRVer)) { + GVR_VER = defVRVer; + Preferences.set("android.sdk.gvr", GVR_VER); + } + + GAR_VER = Preferences.get("android.sdk.ar"); + String defARVer = props.getProperty("com.google.ar"); + if (GAR_VER == null || !versionCheck(GAR_VER, defARVer)) { + GAR_VER = defARVer; + Preferences.set("android.sdk.ar", GAR_VER); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + static private boolean versionCheck(String currentVersion, String minVersion) { String[] currentPieces = currentVersion.split("\\."); String[] minPieces = minVersion.split("\\."); @@ -1018,4 +1134,4 @@ static private boolean versionCheck(String currentVersion, String minVersion) { return false; } -} \ No newline at end of file +} diff --git a/processing/mode/src/processing/mode/android/AndroidDebugger.java b/processing/mode/src/processing/mode/android/AndroidDebugger.java new file mode 100644 index 000000000..f68766cca --- /dev/null +++ b/processing/mode/src/processing/mode/android/AndroidDebugger.java @@ -0,0 +1,411 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2018-21 The Processing Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package processing.mode.android; + +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.event.Event; +import com.sun.jdi.request.ClassPrepareRequest; +import com.sun.jdi.request.EventRequestManager; +import com.sun.jdi.request.StepRequest; +import processing.app.Language; +import processing.app.Messages; +import processing.mode.java.debug.*; + +import javax.swing.*; +import java.awt.*; +import java.io.IOException; + +// Developed by Manav Jain as part of GSoC 2018 +public class AndroidDebugger extends Debugger { + /// editor window, acting as main view + protected AndroidEditor editor; + protected AndroidRunner runtime; + protected AndroidMode androidMode; + + protected boolean isEnabled; + + + private String pkgName = ""; + private String sketchClassName = ""; + + public AndroidDebugger(AndroidEditor editor, AndroidMode androidMode) { + super(editor); + this.editor = editor; + this.androidMode = androidMode; + } + + public boolean isEnabled() { + return isEnabled; + } + + public void toggleDebug() { + isEnabled = !isEnabled; + inspector.setVisible(enabled); + if (isEnabled) { + debugItem.setText(Language.text("menu.debug.disable")); + } else { + debugItem.setText(Language.text("menu.debug.enable")); + } + + for (Component item : debugMenu.getMenuComponents()) { + if (item instanceof JMenuItem && item != debugItem) { + item.setEnabled(isEnabled); + } + } + + } + + @Override + public AndroidEditor getEditor() { + return editor; + } + + public synchronized void startDebug(AndroidRunner runner, Device device) { + //stopDebug(); // stop any running sessions + if (isStarted()) { + return; // do nothing + } + + inspector.reset(); + + // make the inspector instance visible on which tree nodes would be reflected + inspector.setVisible(true); + + runtime = runner; + pkgName = runner.build.getPackageName(); + sketchClassName = runner.build.getSketchClassName(); + + mainClassName = pkgName + "." + sketchClassName; + + try { + int port = 8000 + (int) (Math.random() * 1000); + device.forwardPort(port); + + // connect + System.out.println(AndroidMode.getTextString("android_debugger.info.attaching_debugger")); + VirtualMachine vm = runner.connectVirtualMachine(port); + System.out.println(AndroidMode.getTextString("android_debugger.info.debugger_attached")); + + // start receiving vm events + VMEventReader eventThread = new VMEventReader(vm.eventQueue(), vmEventListener); + eventThread.start(); + + // watch for loaded classes + addClassWatch(vm); + + // resume the vm + vm.resume(); + + } catch (IOException e) { + Messages.log(AndroidMode.getTextString("android_debugger.error.debugger_exception", e.getMessage())); + // Retry + startDebug(runner, device); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override public synchronized void vmEvent(EventSet es) { + VirtualMachine vm = vm(); + if (vm != null && vm != es.virtualMachine()) { + // This is no longer VM we are interested in, + // we already cleaned up and run different VM now. + return; + } + for (Event e : es) { + // System.out.println("VM Event: " + e); + if (e instanceof VMStartEvent) { +// System.out.println("start"); + + } else if (e instanceof ClassPrepareEvent) { + vmClassPrepareEvent((ClassPrepareEvent) e); + + } else if (e instanceof BreakpointEvent) { + vmBreakPointEvent((BreakpointEvent) e); + + } else if (e instanceof StepEvent) { + vmStepEvent(((StepEvent) e)); + + } else if (e instanceof VMDisconnectEvent) { + stopDebug(); + + } else if (e instanceof VMDeathEvent) { + started = false; + editor.statusEmpty(); + } + } + } + + private void vmClassPrepareEvent(ClassPrepareEvent ce) { + ReferenceType rt = ce.referenceType(); + currentThread = ce.thread(); + paused = true; // for now we're paused + + if (rt.name().equals(mainClassName)) { + //printType(rt); + mainClass = rt; + classes.add(rt); +// log("main class load: " + rt.name()); + started = true; // now that main class is loaded, we're started + } else { + classes.add(rt); // save loaded classes +// log("class load: {0}" + rt.name()); + } + + // notify listeners + for (ClassLoadListener listener : classLoadListeners) { + if (listener != null) { + listener.classLoaded(rt); + } + } + paused = false; // resuming now + runtime.vm().resume(); + } + + private void vmBreakPointEvent(BreakpointEvent be) { + currentThread = be.thread(); // save this thread + updateVariableInspector(currentThread); // this is already on the EDT + final LineID newCurrentLine = locationToLineID(be.location()); + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { + editor.setCurrentLine(newCurrentLine); + editor.deactivateStep(); + editor.deactivateContinue(); + } + }); + + // hit a breakpoint during a step, need to cancel the step. + if (requestedStep != null) { + runtime.vm().eventRequestManager().deleteEventRequest(requestedStep); + requestedStep = null; + } + + // fix canvas update issue + // TODO: is this a good solution? + resumeOtherThreads(currentThread); + + paused = true; + editor.statusHalted(); + } + + private void vmStepEvent(StepEvent se) { + currentThread = se.thread(); + + //printSourceLocation(currentThread); + updateVariableInspector(currentThread); // this is already on the EDT + final LineID newCurrentLine = locationToLineID(se.location()); + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + editor.setCurrentLine(newCurrentLine); + editor.deactivateStep(); + editor.deactivateContinue(); + } + }); + + // delete the steprequest that triggered this step so new ones can be placed (only one per thread) + EventRequestManager mgr = runtime.vm().eventRequestManager(); + mgr.deleteEventRequest(se.request()); + requestedStep = null; // mark that there is no step request pending + paused = true; + editor.statusHalted(); + + // disallow stepping into invisible lines + if (!locationIsVisible(se.location())) { + // TODO: this leads to stepping, should it run on the EDT? + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + stepOutIntoViewOrContinue(); + } + }); + } + } + + + @Override public synchronized void continueDebug() { + editor.activateContinue(); + inspector.lock(); + //editor.clearSelection(); + //clearHighlight(); + editor.clearCurrentLine(); + if (!isStarted()) { + startDebug(); + } else if (isPaused()) { + runtime.vm().resume(); + paused = false; + editor.statusBusy(); + } + } + + + + @Override protected void step(int stepDepth) { + if (!isStarted()) { + startDebug(); + } else if (isPaused()) { + inspector.lock(); + editor.activateStep(); + + // use global to mark that there is a step request pending + requestedStep = runtime.vm().eventRequestManager().createStepRequest(currentThread, StepRequest.STEP_LINE, stepDepth); + requestedStep.addCountFilter(1); // valid for one step only + requestedStep.enable(); + paused = false; + runtime.vm().resume(); + editor.statusBusy(); + } + } + + + + @Override public synchronized void stopDebug() { + inspector.lock(); + if (runtime != null) { + + for (LineBreakpoint bp : breakpoints) { + bp.detach(); + } + + runtime.close(); + runtime = null; + //build = null; + classes.clear(); + // need to clear highlight here because, VMDisconnectedEvent seems to be unreliable. TODO: likely synchronization problem + editor.clearCurrentLine(); + } + stopTrackingLineChanges(); + started = false; + + // editor.deactivateDebug(); + editor.deactivateContinue(); + editor.deactivateStep(); + + editor.statusEmpty(); + } + + + /** + * Watch all classes ({@value sketchClassName}) variable + */ + private void addClassWatch(VirtualMachine vm) { + EventRequestManager erm = vm.eventRequestManager(); + ClassPrepareRequest classPrepareRequest = erm.createClassPrepareRequest(); + classPrepareRequest.addClassFilter(mainClassName); + classPrepareRequest.setEnabled(true); + } + + @Override + public VirtualMachine vm() { + if (runtime != null) { + return runtime.vm(); + } + return null; + } + + @Override public synchronized boolean isStarted() { + return started && runtime != null && runtime.vm() != null; + } + + /** + * Get the breakpoint on a certain line, if set. + * + * @param line the line to get the breakpoint from + * @return the breakpoint, or null if no breakpoint is set on the specified + * line. + */ + LineBreakpoint breakpointOnLine(LineID line) { + for (LineBreakpoint bp : breakpoints) { + if (bp.isOnLine(line)) { + return bp; + } + } + return null; + } + + synchronized public void toggleBreakpoint(int lineIdx) { + LineID line = editor.getLineIDInCurrentTab(lineIdx); + int index = line.lineIdx(); + if (hasBreakpoint(line)) { + removeBreakpoint(index); + } else { + // Make sure the line contains actual code before setting the break + // https://github.com/processing/processing/issues/3765 + if (editor.getLineText(index).trim().length() != 0) { + setBreakpoint(index); + } + } + } + + /** + * Set a breakpoint on a line in the current tab. + * + * @param lineIdx the line index (0-based) of the current tab to set the + * breakpoint on + */ + synchronized void setBreakpoint(int lineIdx) { + setBreakpoint(editor.getLineIDInCurrentTab(lineIdx)); + } + + synchronized public void setBreakpoint(LineID line) { + // do nothing if we are kinda busy + if (isStarted() && !isPaused()) { + return; + } + // do nothing if there already is a breakpoint on this line + if (hasBreakpoint(line)) { + return; + } + breakpoints.add(new AndroidLineBreakpoint(line, this)); + } + + /** + * Remove a breakpoint from the current line (if set). + */ + synchronized void removeBreakpoint() { + removeBreakpoint(editor.getCurrentLineID().lineIdx()); + } + + /** + * Remove a breakpoint from a line in the current tab. + * + * @param lineIdx the line index (0-based) in the current tab to remove the + * breakpoint from + */ + void removeBreakpoint(int lineIdx) { + // do nothing if we are kinda busy + if (isBusy()) { + return; + } + + LineBreakpoint bp = breakpointOnLine(editor.getLineIDInCurrentTab(lineIdx)); + if (bp != null) { + bp.remove(); + breakpoints.remove(bp); + } + } + + public String getPackageName() { + return pkgName; + } +} diff --git a/mode/src/processing/mode/android/AndroidEditor.java b/processing/mode/src/processing/mode/android/AndroidEditor.java similarity index 66% rename from mode/src/processing/mode/android/AndroidEditor.java rename to processing/mode/src/processing/mode/android/AndroidEditor.java index 7741d66b7..d45cb4ad9 100644 --- a/mode/src/processing/mode/android/AndroidEditor.java +++ b/processing/mode/src/processing/mode/android/AndroidEditor.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2009-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ import processing.app.Base; import processing.app.Mode; +import processing.app.Language; import processing.app.Platform; import processing.app.Settings; import processing.app.SketchException; @@ -33,6 +34,8 @@ import processing.app.ui.EditorToolbar; import processing.app.ui.Toolkit; import processing.mode.java.JavaEditor; +import processing.mode.java.debug.Debugger; +import processing.mode.java.debug.LineID; import processing.mode.java.preproc.PdePreprocessor; import javax.swing.*; @@ -46,7 +49,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.TimerTask; @@ -59,6 +62,9 @@ public class AndroidEditor extends JavaEditor { private JMenu androidMenu; private int appComponent; + + protected JMenu debugMenu; + private AndroidDebugger debugger; private Settings settings; private AndroidMode androidMode; @@ -69,6 +75,7 @@ public class AndroidEditor extends JavaEditor { private JCheckBoxMenuItem wallpaperItem; private JCheckBoxMenuItem watchfaceItem; private JCheckBoxMenuItem vrItem; + private JCheckBoxMenuItem arItem; protected AndroidEditor(Base base, String path, EditorState state, Mode mode) throws EditorException { @@ -77,18 +84,19 @@ protected AndroidEditor(Base base, String path, EditorState state, androidMode = (AndroidMode) mode; androidMode.resetUserSelection(); androidMode.checkSDK(this); - + + androidTools = loadAndroidTools(); addToolsToMenu(); loadModeSettings(); - } - - @Override - public PdePreprocessor createPreprocessor(final String sketchName) { - return new AndroidPreprocessor(sketchName); } +// @Override +// public PdePreprocessor createPreprocessor(final String sketchName) { +// return new AndroidPreprocessor(sketchName); +// } + public EditorToolbar createToolbar() { return new AndroidToolbar(this, base); @@ -114,15 +122,25 @@ public boolean handleSaveAs() { public JMenu buildFileMenu() { - String exportPkgTitle = AndroidToolbar.getTitle(AndroidToolbar.EXPORT, false); - JMenuItem exportPackage = Toolkit.newJMenuItem(exportPkgTitle, 'E'); + String exportPackageTitle = AndroidToolbar.getTitle(AndroidToolbar.EXPORT_PACKAGE); + JMenuItem exportPackage = Toolkit.newJMenuItemShift(exportPackageTitle, 'X'); exportPackage.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { handleExportPackage(); } }); - String exportProjectTitle = AndroidToolbar.getTitle(AndroidToolbar.EXPORT, true); + + String exportBundleTitle = AndroidToolbar.getTitle(AndroidToolbar.EXPORT_BUNDLE); + JMenuItem exportBundle = Toolkit.newJMenuItemShift(exportBundleTitle, 'B'); + exportBundle.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleExportBundle(); + } + }); + + + String exportProjectTitle = AndroidToolbar.getTitle(AndroidToolbar.EXPORT_PROJECT); JMenuItem exportProject = Toolkit.newJMenuItemShift(exportProjectTitle, 'E'); exportProject.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -130,40 +148,43 @@ public void actionPerformed(ActionEvent e) { } }); - return buildFileMenu(new JMenuItem[] { exportPackage, exportProject}); + return buildFileMenu(new JMenuItem[] {exportPackage, exportBundle, exportProject}); } public JMenu buildSketchMenu() { - JMenuItem runItem = Toolkit.newJMenuItem(AndroidToolbar.getTitle(AndroidToolbar.RUN, false), 'R'); + JMenuItem runItem = Toolkit.newJMenuItem(AndroidToolbar.getTitle(AndroidToolbar.RUN_ON_DEVICE), 'R'); runItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { handleRunDevice(); } }); - JMenuItem presentItem = Toolkit.newJMenuItemShift(AndroidToolbar.getTitle(AndroidToolbar.RUN, true), 'R'); + JMenuItem presentItem = Toolkit.newJMenuItemShift(AndroidToolbar.getTitle(AndroidToolbar.RUN_IN_EMULATOR), 'R'); presentItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { handleRunEmulator(); } }); - JMenuItem stopItem = new JMenuItem(AndroidToolbar.getTitle(AndroidToolbar.STOP, false)); + JMenuItem stopItem = new JMenuItem(AndroidToolbar.getTitle(AndroidToolbar.STOP)); stopItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { handleStop(); } }); - return buildSketchMenu(new JMenuItem[] { runItem, presentItem, stopItem }); + return buildSketchMenu(new JMenuItem[] { buildDebugMenu(), runItem, presentItem, stopItem }); +// return buildSketchMenu(new JMenuItem[] { runItem, presentItem, stopItem }); } public JMenu buildModeMenu() { - androidMenu = new JMenu("Android"); + super.buildModeMenu(); + + androidMenu = new JMenu(AndroidMode.getTextString("menu.android")); JMenuItem item; - item = new JMenuItem("Sketch Permissions"); + item = new JMenuItem(AndroidMode.getTextString("menu.android.sketch_permissions")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new Permissions(sketch, appComponent, androidMode.getFolder()); @@ -173,10 +194,11 @@ public void actionPerformed(ActionEvent e) { androidMenu.addSeparator(); - fragmentItem = new JCheckBoxMenuItem("App"); - wallpaperItem = new JCheckBoxMenuItem("Wallpaper"); - watchfaceItem = new JCheckBoxMenuItem("Watch Face"); - vrItem = new JCheckBoxMenuItem("VR"); + fragmentItem = new JCheckBoxMenuItem(AndroidMode.getTextString("menu.android.app")); + wallpaperItem = new JCheckBoxMenuItem(AndroidMode.getTextString("menu.android.wallpaper")); + watchfaceItem = new JCheckBoxMenuItem(AndroidMode.getTextString("menu.android.watch_face")); + vrItem = new JCheckBoxMenuItem(AndroidMode.getTextString("menu.android.vr")); + arItem = new JCheckBoxMenuItem(AndroidMode.getTextString("menu.android.ar")); fragmentItem.addActionListener(new ActionListener() { @Override @@ -185,6 +207,7 @@ public void actionPerformed(ActionEvent e) { wallpaperItem.setState(false); watchfaceItem.setSelected(false); vrItem.setSelected(false); + arItem.setSelected(false); setAppComponent(AndroidBuild.APP); } }); @@ -195,6 +218,7 @@ public void actionPerformed(ActionEvent e) { wallpaperItem.setState(true); watchfaceItem.setSelected(false); vrItem.setSelected(false); + arItem.setSelected(false); setAppComponent(AndroidBuild.WALLPAPER); } }); @@ -205,35 +229,50 @@ public void actionPerformed(ActionEvent e) { wallpaperItem.setState(false); watchfaceItem.setSelected(true); vrItem.setSelected(false); + arItem.setSelected(false); setAppComponent(AndroidBuild.WATCHFACE); } }); vrItem.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent e) { fragmentItem.setState(false); wallpaperItem.setState(false); watchfaceItem.setSelected(false); vrItem.setSelected(true); + arItem.setSelected(false); setAppComponent(AndroidBuild.VR); } + }); + arItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fragmentItem.setState(false); + wallpaperItem.setState(false); + watchfaceItem.setSelected(false); + vrItem.setSelected(false); + arItem.setSelected(true); + setAppComponent(AndroidBuild.AR); + } }); fragmentItem.setState(false); wallpaperItem.setState(false); watchfaceItem.setSelected(false); vrItem.setSelected(false); + arItem.setSelected(false); androidMenu.add(fragmentItem); androidMenu.add(wallpaperItem); androidMenu.add(watchfaceItem); androidMenu.add(vrItem); + androidMenu.add(arItem); androidMenu.addSeparator(); - final JMenu devicesMenu = new JMenu("Devices"); + final JMenu devicesMenu = new JMenu(AndroidMode.getTextString("menu.android.devices")); - JMenuItem noDevicesItem = new JMenuItem("No connected devices"); + JMenuItem noDevicesItem = new JMenuItem(AndroidMode.getTextString("menu.android.devices.no_connected_devices")); noDevicesItem.setEnabled(false); devicesMenu.add(noDevicesItem); androidMenu.add(devicesMenu); @@ -262,10 +301,17 @@ public void menuCanceled(MenuEvent e) { }); androidMenu.addSeparator(); - + return androidMenu; } + private JMenu buildDebugMenu() { + initDebugger(); + debugMenu = new JMenu(Language.text("menu.debug")); + debugger.populateMenu(debugMenu); + return debugMenu; + } + private void setAppComponent(int comp) { if (appComponent != comp) { @@ -279,6 +325,8 @@ private void setAppComponent(int comp) { settings.set("component", "watchface"); } else if (appComponent == AndroidBuild.VR) { settings.set("component", "vr"); + } else if (appComponent == AndroidBuild.AR) { + settings.set("component", "ar"); } settings.save(); androidMode.resetManifest(sketch, appComponent); @@ -297,19 +345,19 @@ public JMenu buildHelpMenu() { menu.addSeparator(); - item = new JMenuItem("Processing for Android Site"); + item = new JMenuItem(AndroidMode.getTextString("menu.help.processing_for_android_site")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - Platform.openURL("http://android.processing.org/"); + Platform.openURL("https://android.processing.org/"); } }); menu.add(item); - item = new JMenuItem("Android Developer Site"); + item = new JMenuItem(AndroidMode.getTextString("menu.help.android_developer_site")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - Platform.openURL("http://developer.android.com/"); + Platform.openURL("https://developer.android.com/"); } }); menu.add(item); @@ -383,11 +431,74 @@ public void run() { public void handleStop() { - toolbar.deactivateRun(); - stopIndeterminate(); + /* + if (debugger.isStarted()) { + debugger.stopDebug(); + + } else { + toolbar.activateStop(); + androidMode.handleStop(this); + toolbar.deactivateStop(); + toolbar.deactivateRun(); + + // focus the PDE again after quitting presentation mode [toxi 030903] + toFront(); + } + */ + toolbar.activateStop(); androidMode.handleStop(this); + toolbar.deactivateStop(); + toolbar.deactivateRun(); + + // focus the PDE again after quitting presentation mode [toxi 030903] + toFront(); + } + + @Override + public AndroidDebugger getDebugger() { + return debugger; + } + + +// @Override protected void deactivateDebug() { +// super.deactivateDebug(); +// } + + @Override public void activateContinue() { + ((AndroidToolbar) toolbar).activateContinue(); + } + + @Override public void deactivateContinue() { + ((AndroidToolbar) toolbar).deactivateContinue(); + } + + @Override public void activateStep() { + ((AndroidToolbar) toolbar).activateStep(); + } + + @Override public void deactivateStep() { + ((AndroidToolbar) toolbar).deactivateStep(); + } + + + + @Override + public void toggleDebug() { + super.toggleDebug(); + // make the unused inspector invisible + super.debugger.dispose(); + debugger.toggleDebug(); + } + + @Override + public void toggleBreakpoint(int lineIndex) { + debugger.toggleBreakpoint(lineIndex); } + @Override + public LineID getCurrentLineID() { + return super.getCurrentLineID(); + } /** * Create a release build of the sketch and have its apk files ready. @@ -399,13 +510,15 @@ public void handleExportProject() { public void run() { ((AndroidToolbar) toolbar).activateExport(); startIndeterminate(); - statusNotice("Exporting a debug version of the sketch..."); + statusNotice(AndroidMode.getTextString("android_editor.status.exporting_project")); AndroidBuild build = new AndroidBuild(sketch, androidMode, appComponent); try { File exportFolder = build.exportProject(); if (exportFolder != null) { Platform.openFolder(exportFolder); - statusNotice("Done with export."); + statusNotice(AndroidMode.getTextString("android_editor.status.project_export_completed")); + } else { + statusError(AndroidMode.getTextString("android_editor.status.project_export_failed")); } } catch (IOException e) { statusError(e); @@ -419,32 +532,29 @@ public void run() { } } - /** - * Create a release build of the sketch and install its apk files on the - * attached device. + * Create a release package of the sketch */ public void handleExportPackage() { if (androidMode.checkPackageName(sketch, appComponent) && androidMode.checkAppIcons(sketch, appComponent) && handleExportCheckModified()) { - new KeyStoreManager(this); + new KeyStoreManager(this, KeyStoreManager.PACKAGE); } } - public void startExportPackage(final String keyStorePassword) { new Thread() { public void run() { startIndeterminate(); - statusNotice("Exporting signed package..."); + statusNotice(AndroidMode.getTextString("android_editor.status.exporting_package")); AndroidBuild build = new AndroidBuild(sketch, androidMode, appComponent); try { File projectFolder = build.exportPackage(keyStorePassword); if (projectFolder != null) { - statusNotice("Done with export."); + statusNotice(AndroidMode.getTextString("android_editor.status.package_export_completed")); Platform.openFolder(projectFolder); } else { - statusError("Error with export"); + statusError(AndroidMode.getTextString("android_editor.status.package_export_failed")); } } catch (IOException e) { statusError(e); @@ -459,7 +569,46 @@ public void run() { } }.start(); } - + + + /** + * Create a release bundle of the sketch + */ + public void handleExportBundle() { + if (androidMode.checkPackageName(sketch, appComponent) && + androidMode.checkAppIcons(sketch, appComponent) && handleExportCheckModified()) { + new KeyStoreManager(this, KeyStoreManager.BUNDLE); + } + } + + public void startExportBundle(final String keyStorePassword) { + new Thread() { + public void run() { + startIndeterminate(); + statusNotice(AndroidMode.getTextString("android_editor.status.exporting_bundle")); + AndroidBuild build = new AndroidBuild(sketch, androidMode, appComponent); + try { + File projectFolder = build.exportBundle(keyStorePassword); + if (projectFolder != null) { + statusNotice(AndroidMode.getTextString("android_editor.status.bundle_export_completed")); + Platform.openFolder(projectFolder); + } else { + statusError(AndroidMode.getTextString("android_editor.status.bundle_export_failed")); + } + } catch (IOException e) { + statusError(e); + } catch (SketchException e) { + statusError(e); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + stopIndeterminate(); + } + }.start(); + } + public int getAppComponent() { return appComponent; @@ -498,27 +647,48 @@ private void loadModeSettings() { } else if (component.equals("vr")) { appComponent = AndroidBuild.VR; vrItem.setState(true); - } - - if (save) androidMode.initManifest(sketch, appComponent); + } else if (component.equals("ar")) { + appComponent = AndroidBuild.AR; + arItem.setState(true); + } + + androidMode.initManifest(sketch, appComponent); } catch (IOException e) { - System.err.println("While creating " + sketchProps + ": " + e.getMessage()); + System.err.println(AndroidMode.getTextString("android_editor.error.cannot_create_sketch_properties", sketchProps, e.getMessage())); } } private List loadAndroidTools() { // This gets called before assigning mode to androidMode... ArrayList outgoing = new ArrayList(); - File toolPath = new File(androidMode.getFolder(), "tools/SDKUpdater"); - AndroidTool tool = null; - try { - tool = new AndroidTool(toolPath, androidMode.getSDK()); - tool.init(base); - outgoing.add(tool); - } catch (Throwable e) { - e.printStackTrace(); - } - Collections.sort(outgoing); + + File folder = new File(androidMode.getFolder(), "tools"); + String[] list = folder.list(); + if (list == null) { + return outgoing; + } + + Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); + for (String name : list) { + if (name.charAt(0) == '.') { + continue; + } + + File toolPath = new File(folder, name); + if (toolPath.isDirectory()) { + File jarPath = new File(toolPath, "tool" + File.separator + name + ".jar"); + if (jarPath.exists()) { + try { + AndroidTool tool = new AndroidTool(toolPath, androidMode); + tool.init(base); + outgoing.add(tool); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + return outgoing; } @@ -526,7 +696,7 @@ private void addToolsToMenu() { JMenuItem item; for (final Tool tool : androidTools) { - item = new JMenuItem(tool.getMenuTitle()); + item = new JMenuItem(AndroidMode.getTextString(tool.getMenuTitle())); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tool.run(); @@ -544,7 +714,7 @@ public void actionPerformed(ActionEvent e) { // }); // menu.add(item); - item = new JMenuItem("Reset ADB"); + item = new JMenuItem(AndroidMode.getTextString("menu.android.reset_adb")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // editor.statusNotice("Resetting the Android Debug Bridge server."); @@ -556,6 +726,15 @@ public void actionPerformed(ActionEvent e) { androidMenu.add(item); } + private void initDebugger() { + debugger = new AndroidDebugger(this, androidMode); + // Set saved breakpoints when sketch is opened for the first time +// for (LineID lineID : stripBreakpointComments()) { +// debugger.setBreakpoint(lineID); +// } + super.debugger = debugger; + + } class UpdateDeviceListTask extends TimerTask { @@ -586,7 +765,7 @@ public void run() { if (deviceList.size() == 0) { if (0 < deviceMenu.getItemCount()) { deviceMenu.removeAll(); - JMenuItem noDevicesItem = new JMenuItem("No connected devices"); + JMenuItem noDevicesItem = new JMenuItem(AndroidMode.getTextString("menu.android.devices.no_connected_devices")); noDevicesItem.setEnabled(false); deviceMenu.add(noDevicesItem); } @@ -646,4 +825,4 @@ public void actionPerformed(ActionEvent e) { } } } -} \ No newline at end of file +} diff --git a/mode/src/processing/mode/android/AndroidKeyStore.java b/processing/mode/src/processing/mode/android/AndroidKeyStore.java similarity index 81% rename from mode/src/processing/mode/android/AndroidKeyStore.java rename to processing/mode/src/processing/mode/android/AndroidKeyStore.java index a16c8364b..711678f56 100644 --- a/mode/src/processing/mode/android/AndroidKeyStore.java +++ b/processing/mode/src/processing/mode/android/AndroidKeyStore.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2014-16 The Processing Foundation + Copyright (c) 2014-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -32,8 +32,9 @@ * their apps. */ public class AndroidKeyStore { - public static final String ALIAS_STRING = "processing-keystore"; - public static final String KEYSTORE_FILE_NAME = "android-release-key.keystore"; + public static final String ALIAS_STRING = "p5android-key"; + public static final int KEY_VALIDITY_YEARS = 25; + public static final String KEYSTORE_FILE_NAME = "processing-upload-keystore.jks"; public static File getKeyStore() { return getKeyStore(KEYSTORE_FILE_NAME); @@ -53,9 +54,8 @@ public static File getKeyStoreLocation(String name) { boolean result = keyStoreFolder.mkdirs(); if (!result) { - Messages.showWarning("Folders, folders, folders", - "Could not create the necessary folders to build.\n" + - "Perhaps you have some file permissions to sort out?"); + Messages.showWarning(AndroidMode.getTextString("android_keystore.warn.cannot_create_folders.title"), + AndroidMode.getTextString("android_keystore.warn.cannot_create_folders.body")); return null; } } @@ -79,7 +79,7 @@ public static void generateKeyStore(String password, "-alias", ALIAS_STRING, "-keyalg", "RSA", "-keysize", "2048", - "-validity", "10000", + "-validity", Integer.toString(KEY_VALIDITY_YEARS * 365), "-keypass", password, "-storepass", password, "-dname", dname @@ -89,13 +89,12 @@ public static void generateKeyStore(String password, ProcessResult result = ph.execute(); if (result.succeeded()) { if (getKeyStore() == null) { - Messages.showWarning("Well, this is unexpected...", - "The keystore was succesfully cretated but cannot be found.\n" + - "Perhaps was it deleted accidentally?"); + Messages.showWarning(AndroidMode.getTextString("android_keystore.warn.cannot_find_keystore.title"), + AndroidMode.getTextString("android_keystore.warn.cannot_find_keystore.body")); } } else { String[] lines = PApplet.split(result.getStderr(), '\n'); - System.err.println("The keystore could not be created, due to the following error:"); + System.err.println(AndroidMode.getTextString("android_keystore.error.cannot_create_keystore")); for (String line: lines) { System.err.println(line); } diff --git a/processing/mode/src/processing/mode/android/AndroidLineBreakpoint.java b/processing/mode/src/processing/mode/android/AndroidLineBreakpoint.java new file mode 100644 index 000000000..3716826b1 --- /dev/null +++ b/processing/mode/src/processing/mode/android/AndroidLineBreakpoint.java @@ -0,0 +1,59 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2018-21 The Processing Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package processing.mode.android; + +import com.sun.jdi.ReferenceType; +import processing.mode.java.debug.Debugger; +import processing.mode.java.debug.LineBreakpoint; +import processing.mode.java.debug.LineID; + +public class AndroidLineBreakpoint extends LineBreakpoint { + private boolean alreadyAdded; + + public AndroidLineBreakpoint(LineID line, Debugger dbg) { + super(line, dbg); + } + + public AndroidLineBreakpoint(int lineIdx, Debugger dbg) { + super(lineIdx, dbg); + } + + @Override public void classLoaded(ReferenceType theClass) { + if (!isAttached()) { + addPackageName(); + // try to attach + attach(theClass); + } + } + + + /** + * Add package name to the class name. Needed to match + * the logical class name to the VM (Physical) class name + */ + private void addPackageName() { + if (!alreadyAdded) { + className = ((AndroidDebugger) dbg).getPackageName() + "." + className; + alreadyAdded = !alreadyAdded; + } + } +} diff --git a/mode/src/processing/mode/android/AndroidMode.java b/processing/mode/src/processing/mode/android/AndroidMode.java similarity index 58% rename from mode/src/processing/mode/android/AndroidMode.java rename to processing/mode/src/processing/mode/android/AndroidMode.java index 76cd97d4f..1d341c164 100644 --- a/mode/src/processing/mode/android/AndroidMode.java +++ b/processing/mode/src/processing/mode/android/AndroidMode.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2011-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -32,6 +32,7 @@ import processing.app.ui.Editor; import processing.app.ui.EditorException; import processing.app.ui.EditorState; +import processing.core.PApplet; import processing.mode.android.AndroidSDK.CancelException; import processing.mode.java.JavaMode; @@ -39,6 +40,8 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Future; /** @@ -55,67 +58,22 @@ public class AndroidMode extends JavaMode { private boolean checkingSDK = false; private boolean userCancelledSDKSearch = false; - - private static final String BLUETOOTH_DEBUG_URL = - "https://developer.android.com/training/wearables/apps/debugging.html"; - - private static final String WATCHFACE_DEBUG_TITLE = - "Is the watch connected to the computer?"; - - private static final String WATCHFACE_DEBUG_MESSAGE = - "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."; - - private static final String WALLPAPER_INSTALL_TITLE = - "Wallpaper installed!"; - private static final String WALLPAPER_INSTALL_MESSAGE = - "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."; - - private static final String WATCHFACE_INSTALL_TITLE = - "Watch face installed!"; + // Using this temporarily until support for mode translations is finalized in the Processing app + private static Map textStrings = null; + + private static final String VERSIONS_FILE = "version.properties"; - private static final String WATCHFACE_INSTALL_MESSAGE = - "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."; + private static final String BLUETOOTH_DEBUG_URL = + "https://developer.android.com/training/wearables/get-started/debugging"; private static final String DISTRIBUTING_APPS_TUT_URL = - "http://android.processing.org/tutorials/distributing/index.html"; - - private static final String EXPORT_DEFAULT_PACKAGE_TITLE = - "Cannot export package..."; - - private static final String EXPORT_DEFAULT_PACKAGE_MESSAGE = - "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."; - - private static final String EXPORT_DEFAULT_ICONS_TITLE = - "Cannot export package..."; - - private static final String EXPORT_DEFAULT_ICONS_MESSAGE = - "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."; - + "https://android.processing.org/tutorials/distributing/index.html"; public AndroidMode(Base base, File folder) { super(base, folder); + AndroidBuild.initVersions(getContentFile(VERSIONS_FILE)); + loadTextStrings(); } @@ -134,7 +92,8 @@ public String getTitle() { public File[] getKeywordFiles() { return new File[] { - Platform.getContentFile("modes/java/keywords.txt") + Platform.getContentFile("modes/java/keywords.txt"), + getContentFile("keywords.txt") }; } @@ -216,9 +175,8 @@ public void checkSDK(Editor editor) { } } if (sdk == null) { - Messages.showWarning("Bad news...", - "The Android SDK could not be loaded.\n" + - "The Android Mode will be disabled.", tr); + Messages.showWarning(AndroidMode.getTextString("android_mode.warn.cannot_load_sdk_title"), + AndroidMode.getTextString("android_mode.warn.cannot_load_sdk_body"), tr); } else { Devices devices = Devices.getInstance(); devices.setSDK(sdk); @@ -230,6 +188,17 @@ public void checkSDK(Editor editor) { public AndroidSDK getSDK() { return sdk; } + + + public File getResourcesFolder() { + return new File(getFolder(), "resources"); + } + + + public String getModeJar() { + String modePath = new File(getFolder(), "mode").getAbsolutePath(); + return modePath + File.separator + "AndroidMode.jar"; + } @Override @@ -239,7 +208,7 @@ public String getSearchPath() { } if (sdk == null) { - Messages.log("Android SDK path couldn't be loaded."); + Messages.log(AndroidMode.getTextString("android_mode.info.cannot_open_sdk_path")); return ""; } @@ -270,16 +239,29 @@ static public String getDateStamp(long stamp) { public void handleRunEmulator(Sketch sketch, AndroidEditor editor, RunnerListener listener) throws SketchException, IOException { listener.startIndeterminate(); - listener.statusNotice("Starting build..."); + listener.statusNotice(AndroidMode.getTextString("android_mode.status.starting_project_build")); AndroidBuild build = new AndroidBuild(sketch, this, editor.getAppComponent()); - listener.statusNotice("Building Android project..."); - build.build("debug"); + listener.statusNotice(AndroidMode.getTextString("android_mode.status.building_project")); + build.build("debug", ""); + + if (sdk.getEmulatorTool() == null) { + // System.out.println("Try to download the emulator using the SDK Manager..."); + listener.statusNotice(AndroidMode.getTextString("android_mode.status.downloading_emulator")); + boolean emulatorInstallationSucceded = sdk.downloadEmuOnDemand(); + if (!emulatorInstallationSucceded) { + SketchException emulatorInstallationErrorException = new SketchException(AndroidMode.getTextString("android_mode.error.emulator_installation_failed")); + emulatorInstallationErrorException.hideStackTrace(); + throw emulatorInstallationErrorException; + } else { + System.out.println(AndroidMode.getTextString("android_mode.status.downloading_emulator_successful")); + } + } boolean avd = AVD.ensureProperAVD(editor, this, sdk, build.isWear()); if (!avd) { SketchException se = - new SketchException("Could not create a virtual device for the emulator."); + new SketchException(AndroidMode.getTextString("android_mode.error.cannot_create_avd")); se.hideStackTrace(); throw se; } @@ -298,30 +280,27 @@ public void handleRunDevice(Sketch sketch, AndroidEditor editor, final Devices devices = Devices.getInstance(); java.util.List deviceList = devices.findMultiple(false); if (deviceList.size() == 0) { - Messages.showWarning("No devices found!", - "Processing did not find any device where to run\n" + - "your sketch on. Make sure that your handheld or\n" + - "wearable is properly connected to the computer\n" + - "and that USB or Bluetooth debugging is enabled."); - listener.statusError("No devices found."); + Messages.showWarning(AndroidMode.getTextString("android_mode.dialog.no_devices_found_title"), + AndroidMode.getTextString("android_mode.dialog.no_devices_found_body")); + listener.statusError(AndroidMode.getTextString("android_mode.status.no_devices_found")); return; } listener.startIndeterminate(); - listener.statusNotice("Starting build..."); + listener.statusNotice(AndroidMode.getTextString("android_mode.status.starting_project_build")); AndroidBuild build = new AndroidBuild(sketch, this, editor.getAppComponent()); - listener.statusNotice("Building Android project..."); - File projectFolder = build.build("debug"); + listener.statusNotice(AndroidMode.getTextString("android_mode.status.building_project")); + File projectFolder = build.build("debug", ""); if (projectFolder == null) { - listener.statusError("Build failed."); + listener.statusError(AndroidMode.getTextString("android_mode.status.project_build_failed")); return; } int comp = build.getAppComponent(); - Future dev = Devices.getInstance().getHardware(); + Future dev = Devices.getInstance().getHardware(); runner = new AndroidRunner(build, listener); - if (runner.launch(dev, comp, false)) { + if (runner.launch(dev, comp, false)) { showPostBuildMessage(comp); } } @@ -329,7 +308,8 @@ public void handleRunDevice(Sketch sketch, AndroidEditor editor, public void showSelectComponentMessage(int appComp) { if (showWatchFaceDebugMessage && appComp == AndroidBuild.WATCHFACE) { - AndroidUtil.showMessage(WATCHFACE_DEBUG_TITLE, WATCHFACE_DEBUG_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.watchface_debug_title"), + AndroidMode.getTextString("android_mode.dialog.watchface_debug_body", BLUETOOTH_DEBUG_URL)); showWatchFaceDebugMessage = false; } } @@ -337,11 +317,13 @@ public void showSelectComponentMessage(int appComp) { public void showPostBuildMessage(int appComp) { if (showWallpaperSelectMessage && appComp == AndroidBuild.WALLPAPER) { - AndroidUtil.showMessage(WALLPAPER_INSTALL_TITLE, WALLPAPER_INSTALL_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.wallpaper_installed_title"), + AndroidMode.getTextString("android_mode.dialog.wallpaper_installed_body")); showWallpaperSelectMessage = false; } if (showWatchFaceSelectMessage && appComp == AndroidBuild.WATCHFACE) { - AndroidUtil.showMessage(WATCHFACE_INSTALL_TITLE, WATCHFACE_INSTALL_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.watchface_installed_title"), + AndroidMode.getTextString("android_mode.dialog.watchface_installed_body")); showWatchFaceSelectMessage = false; } } @@ -368,7 +350,8 @@ public boolean checkPackageName(Sketch sketch, int comp) { String name = manifest.getPackageName(); if (name.toLowerCase().equals(defName.toLowerCase())) { // The user did not set the package name, show error and stop - AndroidUtil.showMessage(EXPORT_DEFAULT_PACKAGE_TITLE, EXPORT_DEFAULT_PACKAGE_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_export_package_title"), + AndroidMode.getTextString("android_mode.dialog.cannot_export_package_body", DISTRIBUTING_APPS_TUT_URL)); return false; } return true; @@ -377,31 +360,22 @@ public boolean checkPackageName(Sketch sketch, int comp) { public boolean checkAppIcons(Sketch sketch, int comp) { File sketchFolder = sketch.getFolder(); - - boolean allExist = false; - - File localIcon36 = new File(sketchFolder, AndroidBuild.ICON_36); - File localIcon48 = new File(sketchFolder, AndroidBuild.ICON_48); - File localIcon72 = new File(sketchFolder, AndroidBuild.ICON_72); - File localIcon96 = new File(sketchFolder, AndroidBuild.ICON_96); - File localIcon144 = new File(sketchFolder, AndroidBuild.ICON_144); - File localIcon192 = new File(sketchFolder, AndroidBuild.ICON_192); - allExist = localIcon36.exists() && localIcon48.exists() && - localIcon72.exists() && localIcon96.exists() && - localIcon144.exists() && localIcon192.exists(); + + File[] launcherIcons = AndroidUtil.getFileList(sketchFolder, AndroidBuild.SKETCH_LAUNCHER_ICONS, + AndroidBuild.SKETCH_OLD_LAUNCHER_ICONS); + boolean allFilesExist = AndroidUtil.allFilesExists(launcherIcons); if (comp == AndroidBuild.WATCHFACE) { // Additional preview icons are needed for watch faces - File localIconSquare = new File(sketchFolder, AndroidBuild.WATCHFACE_ICON_RECTANGULAR); - File localIconCircle = new File(sketchFolder, AndroidBuild.WATCHFACE_ICON_CIRCULAR); - allExist &= localIconSquare.exists() && localIconCircle.exists(); + File[] watchFaceIcons = AndroidUtil.getFileList(sketchFolder, AndroidBuild.SKETCH_WATCHFACE_ICONS); + allFilesExist &= AndroidUtil.allFilesExists(watchFaceIcons); } - if (!allExist) { + if (!allFilesExist) { // The user did not set custom icons, show error and stop - AndroidUtil.showMessage(EXPORT_DEFAULT_ICONS_TITLE, - EXPORT_DEFAULT_ICONS_MESSAGE); - return false; + AndroidUtil.showMessage(AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_title"), + AndroidMode.getTextString("android_mode.dialog.cannot_use_default_icons_body", DISTRIBUTING_APPS_TUT_URL)); + return false; } return true; } @@ -415,4 +389,53 @@ public void initManifest(Sketch sketch, int comp) { public void resetManifest(Sketch sketch, int comp) { new Manifest(sketch, comp, getFolder(), true); } -} \ No newline at end of file + + private void loadTextStrings() { + String baseFilename = "languages/mode.properties"; + File modeBaseFile = new File(getFolder(), baseFilename); + if (textStrings == null) { + textStrings = new HashMap(); + String[] lines = PApplet.loadStrings(modeBaseFile); + if (lines == null) { + throw new NullPointerException("File not found:\n" + modeBaseFile.getAbsolutePath()); + } + //for (String line : lines) { + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if ((line.length() == 0) || + (line.charAt(0) == '#')) continue; + + // this won't properly handle = signs inside in the text + int equals = line.indexOf('='); + if (equals != -1) { + String key = line.substring(0, equals).trim(); + String value = line.substring(equals + 1).trim(); + + value = value.replaceAll("\\\\n", "\n"); + value = value.replaceAll("\\\\'", "'"); + + textStrings.put(key, value); + } + } + } + } + + static public String getTextString(String key) { + if (textStrings.containsKey(key)) { + return textStrings.get(key); + } else { + return key; + } + +// return Language.text(key); + } + + static public String getTextString(String key, Object... arguments) { + String value = textStrings.get(key); + if (value == null) { + return key; + } + return String.format(value, arguments); + } +} + diff --git a/mode/src/processing/mode/android/AndroidRunner.java b/processing/mode/src/processing/mode/android/AndroidRunner.java similarity index 54% rename from mode/src/processing/mode/android/AndroidRunner.java rename to processing/mode/src/processing/mode/android/AndroidRunner.java index 4cc469b90..ea22aa93d 100644 --- a/mode/src/processing/mode/android/AndroidRunner.java +++ b/processing/mode/src/processing/mode/android/AndroidRunner.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2011-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -22,13 +22,20 @@ package processing.mode.android; +import java.io.IOException; import java.io.PrintStream; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.concurrent.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; +import com.sun.jdi.connect.AttachingConnector; +import com.sun.jdi.connect.Connector; +import com.sun.jdi.connect.IllegalConnectorArgumentsException; import processing.app.ui.Editor; import processing.app.Messages; import processing.app.RunnerListener; @@ -39,16 +46,26 @@ * Launches an app on the device or in the emulator. */ public class AndroidRunner implements DeviceListener { + private static final String DEFAULT_PACKAGE_NAME = "processing.android"; + AndroidBuild build; RunnerListener listener; protected PrintStream sketchErr; protected PrintStream sketchOut; + private VirtualMachine vm; + + private boolean isDebugEnabled; + public AndroidRunner(AndroidBuild build, RunnerListener listener) { this.build = build; this.listener = listener; + if (listener instanceof AndroidEditor) { + isDebugEnabled = ((AndroidEditor) listener).isDebuggerEnabled(); + } + if (listener instanceof Editor) { Editor editor = (Editor) listener; sketchErr = editor.getConsole().getErr(); @@ -62,11 +79,11 @@ public AndroidRunner(AndroidBuild build, RunnerListener listener) { public boolean launch(Future deviceFuture, int comp, boolean emu) { String devStr = emu ? "emulator" : "device"; - listener.statusNotice("Waiting for " + devStr + " to become available..."); + listener.statusNotice(AndroidMode.getTextString("android_runner.status.waiting_for_device", devStr)); final Device device = waitForDevice(deviceFuture, listener); if (device == null || !device.isAlive()) { - listener.statusError("Lost connection with " + devStr + " while launching. Try again."); + listener.statusError(AndroidMode.getTextString("android_runner.status.lost_connection_with_device", devStr)); // Reset the server, in case that's the problem. Sometimes when // launching the emulator times out, the device list refuses to update. final Devices devices = Devices.getInstance(); @@ -75,28 +92,25 @@ public boolean launch(Future deviceFuture, int comp, boolean emu) { } if (comp == AndroidBuild.WATCHFACE && !device.hasFeature("watch")) { - listener.statusError("Could not install the sketch."); - Messages.showWarning("Selected device is not a watch...", - "You are trying to install a watch face on a non-watch device.\n" + - "Select the correct device, or use the emulator."); + listener.statusError(AndroidMode.getTextString("android_runner.status.cannot_install_sketch")); + Messages.showWarning(AndroidMode.getTextString("android_runner.warn.non_watch_device_title"), + AndroidMode.getTextString("android_runner.warn.non_watch_device_body")); return false; } if (comp != AndroidBuild.WATCHFACE && device.hasFeature("watch")) { - listener.statusError("Could not install the sketch."); - Messages.showWarning("Selected device is a watch...", - "You are trying to install a non-watch app on a watch.\n" + - "Select the correct device, or use the emulator."); + listener.statusError(AndroidMode.getTextString("android_runner.status.cannot_install_sketch")); + Messages.showWarning(AndroidMode.getTextString("android_runner.warn.watch_device_title"), + AndroidMode.getTextString("android_runner.warn.watch_device_body")); return false; } device.addListener(this); device.setPackageName(build.getPackageName()); - - listener.statusNotice("Installing sketch on " + device.getId()); + listener.statusNotice(AndroidMode.getTextString("android_runner.status.installing_sketch", device.getId())); // this stopped working with Android SDK tools revision 17 if (!device.installApp(build, listener)) { - listener.statusError("Lost connection with " + devStr + " while installing. Try again."); + listener.statusError(AndroidMode.getTextString("android_runner.status.lost_connection", devStr)); final Devices devices = Devices.getInstance(); devices.killAdbServer(); // see above return false; @@ -105,28 +119,73 @@ public boolean launch(Future deviceFuture, int comp, boolean emu) { boolean status = false; if (comp == AndroidBuild.WATCHFACE || comp == AndroidBuild.WALLPAPER) { if (startSketch(build, device)) { - listener.statusNotice("Sketch installed " - + (device.isEmulator() ? "in the emulator" : "on the device") + "."); + listener.statusNotice(AndroidMode.getTextString("android_runner.status.sketch_installed") + + (device.isEmulator() ? " " + AndroidMode.getTextString("android_runner.status.in_emulator") : " " + + AndroidMode.getTextString("android_runner.status.on_device")) + "."); status = true; } else { - listener.statusError("Could not install the sketch."); + listener.statusError(AndroidMode.getTextString("android_runner.status.cannot_install_sketch")); } } else { - listener.statusNotice("Starting sketch on " + device.getId()); + listener.statusNotice(AndroidMode.getTextString("android_runner.status.launching_sketch", device.getId())); if (startSketch(build, device)) { - listener.statusNotice("Sketch launched " - + (device.isEmulator() ? "in the emulator" : "on the device") + "."); + listener.statusNotice(AndroidMode.getTextString("android_runner.status.sketch_launched") + + (device.isEmulator() ? " " + AndroidMode.getTextString("android_runner.status.in_emulator") : " " + + AndroidMode.getTextString("android_runner.status.on_device")) + "."); status = true; } else { - listener.statusError("Could not start the sketch."); + listener.statusError(AndroidMode.getTextString("android_runner.status.cannot_launch_sketch")); } } - + + // Start Debug if Debugger is enabled + if (isDebugEnabled) { + ((AndroidEditor) listener).getDebugger() + .startDebug(this, device); + } + listener.stopIndeterminate(); lastRunDevice = device; return status; } + public VirtualMachine connectVirtualMachine(int port) throws IOException { + String strPort = Integer.toString(port); + AttachingConnector connector = getConnector(); + try { + vm = connect(connector, strPort); + return vm; + } catch (IllegalConnectorArgumentsException e) { + throw new IllegalStateException(e); + } + } + + private AttachingConnector getConnector() { + VirtualMachineManager vmManager = org.eclipse.jdi.Bootstrap.virtualMachineManager(); + for (Connector connector : vmManager.attachingConnectors()) { + if ("com.sun.jdi.SocketAttach".equals(connector.name())) { + return (AttachingConnector) connector; + } + } + throw new IllegalStateException(); + } + + private VirtualMachine connect( + AttachingConnector connector, String port) throws IllegalConnectorArgumentsException, IOException { + Map args = connector + .defaultArguments(); + Connector.Argument pidArgument = args.get("port"); + if (pidArgument == null) { + throw new IllegalStateException(); + } + pidArgument.setValue(port); + + return connector.attach(args); + } + + public VirtualMachine vm() { + return vm; + } private volatile Device lastRunDevice = null; @@ -137,7 +196,7 @@ public boolean launch(Future deviceFuture, int comp, boolean emu) { private boolean startSketch(AndroidBuild build, final Device device) { final String packageName = build.getPackageName(); try { - if (device.launchApp(packageName)) { + if (device.launchApp(packageName, isDebugEnabled)) { return true; } } catch (final Exception e) { @@ -164,8 +223,7 @@ private Device waitForDevice(Future deviceFuture, RunnerListener listene } catch (final TimeoutException expected) { } } - listener.statusError("No, on second thought, I'm giving up " + - "on waiting for that device to show up."); + listener.statusError(AndroidMode.getTextString("android_runner.status.cancel_waiting_for_device")); return null; } @@ -188,9 +246,9 @@ public void stackTrace(final List trace) { final Matcher m = EXCEPTION_PARSER.matcher(exceptionLine); if (!m.matches()) { - System.err.println("Can't parse this exception line:"); + System.err.println(AndroidMode.getTextString("android_runner.error.cannot_parse_stacktrace")); System.err.println(exceptionLine); - listener.statusError("Unknown exception"); + listener.statusError(AndroidMode.getTextString("android_runner.status.unknwon_exception")); return; } final String exceptionClass = m.group(1); @@ -198,7 +256,7 @@ public void stackTrace(final List trace) { while (frames.hasNext()) { final String line = frames.next(); - if (line.contains("processing.android")) { + if (line.contains(DEFAULT_PACKAGE_NAME)) { final Matcher lm = LOCATION.matcher(line); if (lm.find()) { final String filename = lm.group(1); @@ -218,6 +276,17 @@ public void close() { if (lastRunDevice != null) { lastRunDevice.bringLauncherToFront(); } + + if (vm != null) { + try { + vm.exit(0); + + } catch (com.sun.jdi.VMDisconnectedException vmde) { + // if the vm has disconnected on its own, ignore message + //System.out.println("harmless disconnect " + vmde.getMessage()); + // TODO shouldn't need to do this, need to do more cleanup + } + } } diff --git a/mode/src/processing/mode/android/AndroidSDK.java b/processing/mode/src/processing/mode/android/AndroidSDK.java similarity index 61% rename from mode/src/processing/mode/android/AndroidSDK.java rename to processing/mode/src/processing/mode/android/AndroidSDK.java index 022279ed9..36e7423a1 100644 --- a/mode/src/processing/mode/android/AndroidSDK.java +++ b/processing/mode/src/processing/mode/android/AndroidSDK.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-17 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -21,6 +21,7 @@ package processing.mode.android; +import processing.app.Language; import processing.app.Messages; import processing.app.Platform; import processing.app.Preferences; @@ -40,11 +41,18 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.nio.file.attribute.PosixFilePermission; + +import java.io.PrintWriter; +import java.util.Set; /** * Class holding all needed references (path, tools, etc) to the SDK used by @@ -57,142 +65,37 @@ class AndroidSDK { final static private int TEXT_MARGIN = Toolkit.zoom(8); final static private int TEXT_WIDTH = Toolkit.zoom(300); - private final File folder; - private final File tools; + private final File folder; private final File platforms; - private final File targetPlatform; + private final File highestPlatform; private final File androidJar; private final File platformTools; private final File buildTools; + private final File cmdlineTools; private final File avdManager; private final File sdkManager; -// private final File wearablePath; -// private final File supportLibPath; - - private static final String SDK_DOWNLOAD_URL = - "https://developer.android.com/studio/index.html#downloads"; - - private static final String USE_ENV_SDK_TITLE = "Found an Android SDK!"; - private static final String USE_ENV_SDK_MESSAGE = - "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?"; - - private static final String MISSING_SDK_TITLE = - "Cannot find an Android SDK..."; - private static final String MISSING_SDK_MESSAGE = - "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 " + AndroidBuild.TARGET_SDK + "."; - - private static final String INVALID_SDK_TITLE = - "Android SDK is not valid..."; - private static final String INVALID_SDK_MESSAGE = - "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 " + AndroidBuild.TARGET_SDK + ".

    " + - "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 " + AndroidBuild.TARGET_SDK + "."; - - private static final String COMMAND_LINE_TUT_URL = - "http://android.processing.org/tutorials/command_line/index.html"; - - private static final String ANDROID_SYS_IMAGE_PRIMARY = - "Download phone system image?"; - - private static final String ANDROID_SYS_IMAGE_SECONDARY = - "The system image needed by the emulator does not appear to be installed. " + - "Do you want Processing to download and install it now?

    " + - "Otherwise, you will need to do it through the sdkmanager
    " + - "command line tool, check this online tutorial for more info."; - - private static final String ANDROID_SYS_IMAGE_WEAR_PRIMARY = - "Download watch system image?"; - - private static final String ANDROID_SYS_IMAGE_WEAR_SECONDARY = - "The system image needed by the emulator does not appear to be installed. " + - "Do you want Processing to download and install it now?

    " + - "Otherwise, you will need to do it through the sdkmanager
    " + - "command line tool, check this online tutorial for more info."; - - private static final String SELECT_ANDROID_SDK_FOLDER = - "Choose the location of the Android SDK"; + private final File adb; - private static final String SDK_INSTALL_TITLE = "SDK installed!"; + private File emulator; private static final String PROCESSING_FOR_ANDROID_URL = - "http://android.processing.org/"; + "https://android.processing.org/"; private static final String WHATS_NEW_URL = - "http://android.processing.org/whatsnew.html"; - + "https://android.processing.org/whatsnew"; + + private static final String SDK_DOWNLOAD_URL = + "https://developer.android.com/studio/index.html#android-studio-downloads"; + private static final String DRIVER_INSTALL_URL = - "https://developer.android.com/studio/run/oem-usb.html#InstallingDriver"; - - private static final String SDK_INSTALL_MESSAGE = - "Processing just downloaded and installed the Android SDK succesfully. " + - "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."; - - private static final String SDK_EXISTS_TITLE = "SDK configured!"; - - private static final String SDK_EXISTS_MESSAGE = - "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."; - - private static final String DRIVER_INSTALL_MESSAGE = "

    " + - "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:
    "; + "https://developer.android.com/studio/run/oem-usb.html#InstallingDriver"; + + private static final String SYSTEM_32BIT_URL = + "https://askubuntu.com/questions/710426/android-sdk-on-ubuntu-32bit"; private static final String SDK_LICENSE_URL = "https://developer.android.com/studio/terms.html"; - - private static final String SDK_LICENSE_TITLE = "Accept SDK license?"; - private static final String SDK_LICENSE_MESSAGE = - "You need to accept the terms of the Android SDK license from Google in " + - "order to use the SDK. Read the license from here."; - - private static final String NO_SDK_LICENSE_TITLE = "SDK license not accepted"; - - private static final String NO_SDK_LICENSE_MESSAGE = - "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"; - - private static final String SYSTEM_32BIT_TITLE = "System is 32 bit..."; - - private static final String SYSTEM_32BIT_URL = - "https://askubuntu.com/questions/710426/android-sdk-on-ubuntu-32bit"; - - private static final String SYSTEM_32BIT_MESSAGE = - "The Android SDK no longer includes 32 bit platform tools (adb, etc.), and so they will not work.

    " + - "This thread provides some possible workarounds."; - private static final int NO_ERROR = 0; private static final int SKIP_ENV_SDK = 1; private static final int MISSING_SDK = 2; @@ -202,59 +105,75 @@ class AndroidSDK { public AndroidSDK(File folder) throws BadSDKException, IOException { this.folder = folder; if (!folder.exists()) { - throw new BadSDKException(folder + " does not exist"); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_sdk_folder", folder)); } - tools = new File(folder, "tools"); - if (!tools.exists()) { - throw new BadSDKException("There is no tools folder in " + folder); + cmdlineTools = new File(folder, "cmdline-tools/latest"); + // We need only command line tools, as sdk/tools got deprecated and can't be used with java 17 + if (!cmdlineTools.exists()) { + // Let's be more specific to show the error + File sdkTools = new File(folder, "tools"); + if (sdkTools.exists()) { + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_cmdtools_folder_found_sdktools", folder)); + } else { + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_cmdtools_folder", folder)); + } } + // If we reached here, that means command line tools exists + // ok to go with the command line tools platformTools = new File(folder, "platform-tools"); if (!platformTools.exists()) { - throw new BadSDKException("There is no platform-tools folder in " + folder); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_platform_tools_folder", folder)); } buildTools = new File(folder, "build-tools"); if (!buildTools.exists()) { - throw new BadSDKException("There is no build-tools folder in " + folder); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_build_tools_folder", folder)); } platforms = new File(folder, "platforms"); if (!platforms.exists()) { - throw new BadSDKException("There is no platforms folder in " + folder); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_platforms_folder", folder)); + } + + // Retrieve the highest platform from the available targets + ArrayList targets = getAvailableSdkTargets(); + int highestBuild = 1; + int highestTarget = 1; + String highestName = ""; + for (Target targ: targets) { + if (highestBuild < targ.build) { + highestBuild = targ.build; + highestTarget = targ.sdk; + highestName = targ.name; + } } - targetPlatform = new File(platforms, AndroidBuild.TARGET_PLATFORM); - if (!targetPlatform.exists()) { - throw new BadSDKException("There is no Android " + - AndroidBuild.TARGET_SDK + " in " + platforms.getAbsolutePath()); + if (highestTarget < PApplet.parseInt(AndroidBuild.TARGET_SDK)) { + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_target_platform", + AndroidBuild.TARGET_SDK, platforms.getAbsolutePath())); } - androidJar = new File(targetPlatform, "android.jar"); + highestPlatform = new File(platforms, highestName); + androidJar = new File(highestPlatform, "android.jar"); if (!androidJar.exists()) { - throw new BadSDKException("android.jar for plaform " + - AndroidBuild.TARGET_SDK + " is missing from " + targetPlatform.getAbsolutePath()); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_android_jar", + AndroidBuild.TARGET_SDK, highestPlatform.getAbsolutePath())); } - -// wearablePath = new File(folder, "extras/google/m2repository/com/google/android/support/wearable"); -// if (!wearablePath.exists()) { -// throw new BadSDKException("There is no wearable folder in " + folder); -// } -// -// supportLibPath = new File(folder, "extras/android/m2repository/com/android/support"); -// if (!supportLibPath.exists()) { -// throw new BadSDKException("There is no support library folder in " + folder); -// } - - avdManager = findCliTool(new File(tools, "bin"), "avdmanager"); - sdkManager = findCliTool(new File(tools, "bin"), "sdkmanager"); + // Collecting the tools needed by the mode + adb = findCliTool(platformTools, "adb"); + avdManager = findCliTool(new File(cmdlineTools, "bin"), "avdmanager"); + sdkManager = findCliTool(new File(cmdlineTools, "bin"), "sdkmanager"); + + initEmu(); + String path = Platform.getenv("PATH"); Platform.setenv("ANDROID_SDK", folder.getCanonicalPath()); path = platformTools.getCanonicalPath() + File.pathSeparator + - tools.getCanonicalPath() + File.pathSeparator + path; + cmdlineTools.getCanonicalPath() + File.pathSeparator + path; String javaHomeProp = System.getProperty("java.home"); File javaHome = new File(javaHomeProp).getCanonicalFile(); @@ -265,6 +184,63 @@ public AndroidSDK(File folder) throws BadSDKException, IOException { checkDebugCertificate(); } + + private void initEmu() throws BadSDKException, IOException { + File emuFolder = new File(folder, "emulator"); + if (emuFolder.exists()) { + // First try the new location of the emulator inside its own folder + emulator = findCliTool(emuFolder, "emulator"); + } else { + // If not found, use old location inside tools as fallback + emuFolder = new File(cmdlineTools, "emulator"); + if (emuFolder.exists()) { + emulator = findCliTool(cmdlineTools, "emulator"); + } else { + emulator = null; + if (SDKDownloader.DOWNLOAD_EMU_WITH_SDK) { + // Only throw an exception if the downloader was supposed to download the emulator + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.missing_emulator", + AndroidBuild.TARGET_SDK, highestPlatform.getAbsolutePath())); + } + } + } + } + public boolean downloadEmuOnDemand() { + final String[] cmd = new String[] { + sdkManager.getAbsolutePath(), + "emulator" + }; + + ProcessBuilder pb = new ProcessBuilder(cmd); + Process process = null; + try { + process = pb.start(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + new RedirectStreamHandler(new PrintWriter(System.out, true), process.getInputStream()); + new RedirectStreamHandler(new PrintWriter(System.out, true), process.getErrorStream()); + + int emulatorDownloadResultCode = process.waitFor(); + System.out.println("Output from emulator download " + emulatorDownloadResultCode); + if (emulatorDownloadResultCode == 0) { + initEmu(); + return true; + } + } catch (IOException e) { + e.printStackTrace(); + } catch (BadSDKException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + process.destroy(); + } + + return false; + } /** @@ -298,19 +274,17 @@ protected void checkDebugCertificate() { Date date = df.parse(timestamp); long expireMillis = date.getTime(); if (expireMillis < System.currentTimeMillis()) { - System.out.println("Removing expired debug.keystore file."); + System.out.println(AndroidMode.getTextString("android_debugger.info.removing_expired_keystore")); String hidingName = "debug.keystore." + AndroidMode.getDateStamp(expireMillis); File hidingFile = new File(keystoreFile.getParent(), hidingName); if (!keystoreFile.renameTo(hidingFile)) { - System.err.println("Could not remove the expired debug.keystore file."); - System.err.println("Please remove the file " + keystoreFile.getAbsolutePath()); + System.err.println(AndroidMode.getTextString("android_debugger.error.cannot_remove_expired_keystore")); + System.err.println(AndroidMode.getTextString("android_debugger.error.request_removing_keystore", keystoreFile.getAbsolutePath())); } -// } else { -// System.out.println("Nah, that won't expire until " + date); //timestamp); } } catch (ParseException pe) { - System.err.println("The date “" + timestamp + "� could not be parsed."); - System.err.println("Please report this as a bug so we can fix it."); + System.err.println(AndroidMode.getTextString("android_debugger.error.invalid_keystore_timestamp", timestamp)); + System.err.println(AndroidMode.getTextString("android_debugger.error.request_bug_report")); } } } @@ -322,28 +296,9 @@ protected void checkDebugCertificate() { } - public File getToolsFolder() { - return tools; - } - - - public String getAvdManagerPath() { - return avdManager.getAbsolutePath(); - } - - - public File getSdkFolder() { + public File getFolder() { return folder; } - - - public File getTargetPlatform() { - return targetPlatform; - } - - public File getAndroidJarPath() { - return androidJar; - } public File getBuildToolsFolder() { @@ -354,38 +309,51 @@ public File getBuildToolsFolder() { public File getPlatformToolsFolder() { return platformTools; } - -// public File getWearableFolder() { -// return wearablePath; -// } - -// public File getSupportLibrary() { -// return supportLibPath; -// } + public File getAndroidJarPath() { + return androidJar; + } - public File getZipAlignTool() { - File[] files = buildTools.listFiles(); - String name = Platform.isWindows() ? "zipalign.exe" : "zipalign"; - for (File f: files) { - File z = new File(f, name); - if (z.exists()) return z; - } - return null; + public File getCommandLineToolsFolder() { + return cmdlineTools; + } + + + public File getEmulatorTool() { + return emulator; } + + public File getAVDManagerTool() { + return avdManager; + } + + + public File getHighestPlatform() { + return highestPlatform; + } + + + public File getTargetPlatform(String target) { + return new File(platforms, "android-" + target); + } + // Write to the process input, so the licenses will be accepted. In - // principle, We only need 7 'y', one for the 'yes' to the first + // principle, we only need 7 'y', one for the 'yes' to the first // 'review licenses?' question, the rest for the 6 licenses, but adding // 10 just in case, having more does not cause any trouble. private static final String response = "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"; private void acceptLicenses() { - ProcessBuilder pb = new ProcessBuilder(sdkManager.getAbsolutePath(), - "--licenses"); + final String[] cmd = new String[] { + sdkManager.getAbsolutePath(), + "--licenses" + }; + + ProcessBuilder pb = new ProcessBuilder(cmd); pb.redirectErrorStream(true); try { Process process = pb.start(); @@ -404,7 +372,7 @@ public void run() { } } }, "AndroidSDK: reading licenses").start(); - Thread.sleep(1000); + Thread.sleep(3000); os.write(response.getBytes()); os.flush(); os.close(); @@ -432,18 +400,51 @@ static public File getGoogleDriverFolder() { /** * Checks a path to see if there's a tools/android file inside, a rough check - * for the SDK installation. Also figures out the name of android/android.bat + * for the SDK installation. Also figures out the name of android/android.bat/android.exe * so that it can be called explicitly. */ - private static File findCliTool(final File tools, String name) + private static File findCliTool(final File toolDir, String toolName) throws BadSDKException { - if (new File(tools, name + ".bat").exists()) { - return new File(tools, name + ".bat"); - } - if (new File(tools, name).exists()) { - return new File(tools, name); + File toolFile; + if (Platform.isWindows()) { + toolFile = new File(toolDir, toolName + ".exe"); + if (!toolFile.exists()) { + toolFile = new File(toolDir, toolName + ".bat"); + } + } else { + toolFile = new File(toolDir, toolName); + } + + if (!toolFile.exists()) { + throw new BadSDKException("Cannot find " + toolName + " in " + toolDir); + } + + if (!Platform.isWindows()) { + try { + // Get the POSIX file permissions + Path toolPath = Paths.get(toolFile.getAbsolutePath()); + Set permissions = Files.getPosixFilePermissions(toolPath); + + boolean addedPerm = false; + if (!permissions.contains(PosixFilePermission.OWNER_EXECUTE)) { + permissions.add(PosixFilePermission.OWNER_EXECUTE); + addedPerm = true; + } + if (!permissions.contains(PosixFilePermission.GROUP_EXECUTE)) { + permissions.add(PosixFilePermission.GROUP_EXECUTE); + addedPerm = true; + } + + if (addedPerm) { + // Set the missing POSIX execute (group and owner) permissions + Files.setPosixFilePermissions(toolPath, permissions); + } + } catch (Exception e) { + e.printStackTrace(); + } } - throw new BadSDKException("Cannot find " + name + " in " + tools); + + return toolFile; } @@ -503,7 +504,9 @@ public static AndroidSDK load(boolean checkEnvSDK, Frame editor) throws IOExcept // and the user wants to use the SDK found in the environment. This // means we just installed the mode for the first time, so we show a // welcome message with some useful info. - AndroidUtil.showMessage(SDK_EXISTS_TITLE, SDK_EXISTS_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_sdk.dialog.using_existing_sdk_title"), + AndroidMode.getTextString("android_sdk.dialog.using_existing_sdk_body", + PROCESSING_FOR_ANDROID_URL, WHATS_NEW_URL)); return androidSDK; } catch (final BadSDKException badEnv) { @@ -536,21 +539,21 @@ static public AndroidSDK locate(final Frame window, final AndroidMode androidMod return download(window, androidMode); } else if (result == JOptionPane.NO_OPTION) { // User will manually select folder containing SDK folder - File folder = selectFolder(SELECT_ANDROID_SDK_FOLDER, null, window); + File folder = selectFolder(AndroidMode.getTextString("android_sdk.dialog.select_sdk_folder"), null, window); if (folder == null) { - throw new CancelException("User canceled attempt to find SDK"); + throw new CancelException(AndroidMode.getTextString("android_sdk.error.cancel_sdk_selection")); } else { final AndroidSDK androidSDK = new AndroidSDK(folder); Preferences.set("android.sdk.path", folder.getAbsolutePath()); return androidSDK; } } else { - throw new CancelException("User canceled attempt to find SDK"); + throw new CancelException(AndroidMode.getTextString("android_sdk.error.sdk_selection_canceled")); } } - static public boolean locateSysImage(final Frame window, - final AndroidMode androidMode, final boolean wear, final boolean ask) + static public boolean requestSysImage(final Frame window, + final AndroidMode androidMode, final boolean wear, final boolean ask) throws BadSDKException, CancelException, IOException { final int result = showDownloadSysImageDialog(window, wear); if (result == JOptionPane.YES_OPTION) { @@ -568,29 +571,31 @@ static public AndroidSDK download(final Frame editor, final AndroidMode androidM downloader.run(); // This call blocks until the SDK download complete, or user cancels. if (downloader.cancelled()) { - throw new CancelException("User canceled SDK download"); + throw new CancelException(AndroidMode.getTextString("android_sdk.error.sdk_download_canceled")); } AndroidSDK sdk = downloader.getSDK(); if (sdk == null) { - throw new BadSDKException("SDK could not be downloaded"); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.sdk_download_failed")); } final int result = showSDKLicenseDialog(editor); if (result == JOptionPane.YES_OPTION) { - sdk.acceptLicenses(); - String msg = SDK_INSTALL_MESSAGE; + sdk.acceptLicenses(); + String msg = AndroidMode.getTextString("android_sdk.dialog.sdk_installed_body", PROCESSING_FOR_ANDROID_URL, WHATS_NEW_URL); File driver = AndroidSDK.getGoogleDriverFolder(); if (Platform.isWindows() && driver.exists()) { - msg += DRIVER_INSTALL_MESSAGE + driver.getAbsolutePath(); + msg += AndroidMode.getTextString("android_sdk.dialog.install_usb_driver", DRIVER_INSTALL_URL, driver.getAbsolutePath()); } - AndroidUtil.showMessage(SDK_INSTALL_TITLE, msg); + AndroidUtil.showMessage(AndroidMode.getTextString("android_sdk.dialog.sdk_installed_title"), msg); } else { - AndroidUtil.showMessage(NO_SDK_LICENSE_TITLE, NO_SDK_LICENSE_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_sdk.dialog.sdk_license_rejected_title"), + AndroidMode.getTextString("android_sdk.dialog.sdk_license_rejected_body")); } - if (Platform.isLinux() && Platform.getNativeBits() == 32) { - AndroidUtil.showMessage(SYSTEM_32BIT_TITLE, SYSTEM_32BIT_MESSAGE); - } + // if (Platform.isLinux() && Platform.getNativeBits() == 32) { + // AndroidUtil.showMessage(AndroidMode.getTextString("android_sdk.dialog.32bit_system_title"), + // AndroidMode.getTextString("android_sdk.dialog.32bit_system_body", SYSTEM_32BIT_URL)); + // } return sdk; } @@ -602,25 +607,25 @@ static public boolean downloadSysImage(final Frame editor, downloader.run(); // This call blocks until the SDK download complete, or user cancels. if (downloader.cancelled()) { - throw new CancelException("User canceled emulator download"); + throw new CancelException(AndroidMode.getTextString("android_sdk.error.emulator_download_canceled")); } boolean res = downloader.getResult(); if (!res) { - throw new BadSDKException("Emulator could not be downloaded"); + throw new BadSDKException(AndroidMode.getTextString("android_sdk.error.emulator_download_failed")); } return res; } static public int showEnvSDKDialog(Frame editor) { - String title = USE_ENV_SDK_TITLE; + String title = AndroidMode.getTextString("android_sdk.dialog.found_installed_sdk_title"); String htmlString = " " + " " + - "

    " + USE_ENV_SDK_MESSAGE + "

    "; + "

    " + AndroidMode.getTextString("android_sdk.dialog.found_installed_sdk_body") + "

    "; JEditorPane pane = new JEditorPane("text/html", htmlString); pane.addHyperlinkListener(new HyperlinkListener() { @Override @@ -634,7 +639,8 @@ public void hyperlinkUpdate(HyperlinkEvent e) { JLabel label = new JLabel(); pane.setBackground(label.getBackground()); - String[] options = new String[] { "Use existing SDK", "Download new SDK" }; + String[] options = new String[] { AndroidMode.getTextString("android_sdk.option.use_existing_sdk"), + AndroidMode.getTextString("android_sdk.option.download_new_sdk") }; int result = JOptionPane.showOptionDialog(null, pane, title, JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); @@ -659,11 +665,11 @@ static public int showLocateDialog(Frame editor) { " "; String title = ""; if (loadError == MISSING_SDK) { - htmlString += "

    " + MISSING_SDK_MESSAGE + "

    "; - title = MISSING_SDK_TITLE; + htmlString += "

    " + AndroidMode.getTextString("android_sdk.dialog.cannot_find_sdk_body", SDK_DOWNLOAD_URL, AndroidBuild.TARGET_SDK) + "

    "; + title = AndroidMode.getTextString("android_sdk.dialog.cannot_find_sdk_title"); } else if (loadError == INVALID_SDK) { - htmlString += "

    " + INVALID_SDK_MESSAGE + "

    "; - title = INVALID_SDK_TITLE; + htmlString += "

    " + AndroidMode.getTextString("android_sdk.dialog.invalid_sdk_body", AndroidBuild.TARGET_SDK, SDK_DOWNLOAD_URL, AndroidBuild.TARGET_SDK) + "

    "; + title = AndroidMode.getTextString("android_sdk.dialog.invalid_sdk_title"); } JEditorPane pane = new JEditorPane("text/html", htmlString); pane.addHyperlinkListener(new HyperlinkListener() { @@ -678,9 +684,8 @@ public void hyperlinkUpdate(HyperlinkEvent e) { JLabel label = new JLabel(); pane.setBackground(label.getBackground()); - String[] options = new String[] { - "Download SDK automatically", "Locate SDK path manually" - }; + String[] options = new String[] { AndroidMode.getTextString("android_sdk.option.download_sdk"), + AndroidMode.getTextString("android_sdk.option.locate_sdk") }; int result = JOptionPane.showOptionDialog(null, pane, title, JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); @@ -695,8 +700,10 @@ public void hyperlinkUpdate(HyperlinkEvent e) { static public int showDownloadSysImageDialog(Frame editor, boolean wear) { - String title = wear ? ANDROID_SYS_IMAGE_WEAR_PRIMARY : ANDROID_SYS_IMAGE_PRIMARY; - String msg = wear ? ANDROID_SYS_IMAGE_WEAR_SECONDARY : ANDROID_SYS_IMAGE_SECONDARY; + String title = wear ? AndroidMode.getTextString("android_sdk.dialog.download_watch_image_title") : + AndroidMode.getTextString("android_sdk.dialog.download_phone_image_title"); + String msg = wear ? AndroidMode.getTextString("android_sdk.dialog.download_watch_image_body") : + AndroidMode.getTextString("android_sdk.dialog.download_phone_image_body"); String htmlString = " " + " " + - "

    " + text + "

    "; - JEditorPane pane = new JEditorPane("text/html", htmlString); + "

    " + text + "

    "; + JEditorPane pane = new JEditorPane(); + pane.setContentType("text/html"); + pane.setText(htmlString); + pane.setEditable(false); + pane.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { - Platform.openURL(e.getURL().toString()); + if (e.getURL() != null) { + Platform.openURL(e.getURL().toString()); + } else { + String description = e.getDescription(); + System.err.println("Cannot open this URL: " + description); + } } } }); - pane.setEditable(false); + JLabel label = new JLabel(); pane.setBackground(label.getBackground()); JOptionPane.showMessageDialog(null, pane, title, @@ -92,8 +110,7 @@ static public void writeFile(final File file, String[] lines) { writer.flush(); writer.close(); } - - + static public File createPath(final File parent, final String name) throws SketchException { final File result = new File(parent, name); @@ -102,12 +119,10 @@ static public File createPath(final File parent, final String name) } return result; } - - + static public void createFileFromTemplate(final File tmplFile, final File destFile) { createFileFromTemplate(tmplFile, destFile, null); - } - + } static public void createFileFromTemplate(final File tmplFile, final File destFile, final HashMap replaceMap) { @@ -131,8 +146,7 @@ static public void createFileFromTemplate(final File tmplFile, final File destFi pw.flush(); pw.close(); } - - + static public File createSubFolder(File parent, String name) throws IOException { File newFolder = new File(parent, name); if (newFolder.exists()) { @@ -172,15 +186,9 @@ static public File createSubFolder(File parent, String name) throws IOException } return newFolder; } - - - static public void extractFolder(File file, File newPath, boolean setExec) + + static public void extractFolder(File file, File newPath) throws IOException { - extractFolder(file, newPath, setExec, false); - } - - static public void extractFolder(File file, File newPath, boolean setExec, - boolean remRoot) throws IOException { int BUFFER = 2048; ZipFile zip = new ZipFile(file); @@ -191,18 +199,7 @@ static public void extractFolder(File file, File newPath, boolean setExec, // grab a zip file entry ZipEntry entry = zipFileEntries.nextElement(); String currentEntry = entry.getName(); - - if (remRoot) { - // Remove root folder from path - int idx = currentEntry.indexOf("/"); - if (idx == -1) { - // Let's try the system file separator - // https://stackoverflow.com/a/16485210 - idx = currentEntry.indexOf(File.separator); - } - currentEntry = currentEntry.substring(idx + 1); - } - + File destFile = new File(newPath, currentEntry); //destFile = new File(newPath, destFile.getName()); File destinationParent = destFile.getParentFile(); @@ -210,13 +207,6 @@ static public void extractFolder(File file, File newPath, boolean setExec, // create the parent directory structure if needed destinationParent.mkdirs(); - String ext = PApplet.getExtension(currentEntry); - if (setExec && ext.equals("unknown")) { - // On some OS X machines the android binaries lose their executable - // attribute, rendering the mode unusable - destFile.setExecutable(true); - } - if (!entry.isDirectory()) { // should preserve permissions // https://bitbucket.org/atlassian/amps/pull-requests/21/amps-904-preserve-executable-file-status/diff @@ -245,13 +235,91 @@ static public void extractClassesJarFromAar(File wearFile, File explodeDir, File jarFile) throws IOException { extractClassesJarFromAar(wearFile, explodeDir, jarFile, true); } - - + static public void extractClassesJarFromAar(File wearFile, File explodeDir, File jarFile, boolean removeDir) throws IOException { - extractFolder(wearFile, explodeDir, false); + extractFolder(wearFile, explodeDir); File classFile = new File(explodeDir, "classes.jar"); Util.copyFile(classFile, jarFile); Util.removeDir(explodeDir); - } + } + + static public File[] getFileList(File folder, String[] names) { + return getFileList(folder, names, null); + } + + static public File[] getFileList(File folder, String[] names, String[] altNames) { + File[] icons = new File[names.length]; + for (int i = 0; i < names.length; i++) { + File f = new File(folder, names[i]); + if (!f.exists() && altNames != null) { + f = new File(folder, altNames[i]); + } + icons[i] = f; + } + return icons; + } + + static public File[] getFileList(Mode mode, String prefix, String[] names) { + File[] icons = new File[names.length]; + for (int i = 0; i < names.length; i++) { + icons[i] = mode.getContentFile(prefix + names[i]); + } + return icons; + } + + static public boolean allFilesExists(File[] files) { + for (File f: files) { + if (!f.exists()) return false; + } + return true; + } + + static public boolean noFileExists(File[] files) { + for (File f: files) { + if (f.exists()) return false; + } + return true; + } + + static public void moveDir(File from, File to) { + try { + Files.move(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + static public void copyDir(File from, File to) { + final Path source = Paths.get(from.toURI()); + final Path target = Paths.get(to.toURI()); + + SimpleFileVisitor copyVisitor = new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Path resolve = target.resolve(source.relativize(dir)); + if (Files.notExists(resolve)) Files.createDirectories(resolve); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path resolve = target.resolve(source.relativize(file)); + Files.copy(file, resolve, StandardCopyOption.REPLACE_EXISTING); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + System.err.format("Unable to copy: %s: %s%n", file, exc); + return FileVisitResult.CONTINUE; + } + }; + + try { + Files.walkFileTree(source, copyVisitor); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/mode/src/processing/mode/android/Commander.java b/processing/mode/src/processing/mode/android/Commander.java similarity index 98% rename from mode/src/processing/mode/android/Commander.java rename to processing/mode/src/processing/mode/android/Commander.java index 0f9aab4c0..437dab96b 100644 --- a/mode/src/processing/mode/android/Commander.java +++ b/processing/mode/src/processing/mode/android/Commander.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2008-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -56,6 +56,7 @@ public class Commander implements RunnerListener { static final String targetArg_WALLPAPER = "wallpaper"; static final String targetArg_WATCHFACE = "watchface"; static final String targetArg_VR = "vr"; + static final String targetArg_AR = "ar"; static final String sketchArg = "--sketch="; static final String forceArg = "--force"; static final String outputArg = "--output="; @@ -135,6 +136,8 @@ private void parseArgs(String[] args) { appComponent = AndroidBuild.WATCHFACE; } else if (compStr.equals(targetArg_VR)) { appComponent = AndroidBuild.VR; + } else if (compStr.equals(targetArg_AR)) { + appComponent = AndroidBuild.AR; } } else if (arg.equals(buildArg)) { task = BUILD; @@ -234,7 +237,7 @@ private void execute() { sketch = new Sketch(pdePath, androidMode); if (task == BUILD || task == RUN) { AndroidBuild build = new AndroidBuild(sketch, androidMode, appComponent); - build.build(target); + build.build(target, ""); if (task == RUN) { AndroidRunner runner = new AndroidRunner(build, this); diff --git a/mode/src/processing/mode/android/Device.java b/processing/mode/src/processing/mode/android/Device.java similarity index 80% rename from mode/src/processing/mode/android/Device.java rename to processing/mode/src/processing/mode/android/Device.java index 042f0853c..017250da0 100644 --- a/mode/src/processing/mode/android/Device.java +++ b/processing/mode/src/processing/mode/android/Device.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-16 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -22,7 +22,6 @@ package processing.mode.android; import processing.app.Base; -import processing.app.Platform; import processing.app.RunnerListener; import processing.app.exec.LineProcessor; import processing.app.exec.ProcessRegistry; @@ -31,7 +30,6 @@ import processing.core.PApplet; import processing.mode.android.LogEntry.Severity; -import java.io.File; import java.io.IOException; import java.util.*; import java.util.regex.Matcher; @@ -41,30 +39,30 @@ class Device { private final Devices env; private final String id; - private final String features; + private final String features; private final Set activeProcesses = new HashSet(); - private final Set listeners = + private final Set listeners = Collections.synchronizedSet(new HashSet()); - + // public static final String APP_STARTED = "android.device.app.started"; // public static final String APP_ENDED = "android.device.app.ended"; private String packageName = ""; - + // mutable state private Process logcat; public Device(final Devices env, final String id) { this.env = env; this.id = id; - + // http://android.stackexchange.com/questions/82169/howto-get-devices-features-with-adb - String concat = ""; + String concat = ""; try { final ProcessResult res = adb("shell", "getprop", "ro.build.characteristics"); for (String line : res) { - concat += "," + line.toLowerCase(); - } + concat += "," + line.toLowerCase(); + } } catch (final Exception e) { } this.features = concat; @@ -72,9 +70,9 @@ public Device(final Devices env, final String id) { public void bringLauncherToFront() { try { - adb("shell", "am", "start", - "-a", "android.intent.action.MAIN", - "-c", "android.intent.category.HOME"); + adb("shell", "am", "start", + "-a", "android.intent.action.MAIN", + "-c", "android.intent.category.HOME"); } catch (final Exception e) { e.printStackTrace(System.err); } @@ -83,17 +81,17 @@ public void bringLauncherToFront() { public boolean hasFeature(String feature) { return -1 < features.indexOf(feature); } - + public String getName() { String name = ""; try { - ProcessResult result = env.getSDK().runADB("-s", id, "shell", "getprop", "ro.product.brand"); + ProcessResult result = adb("shell", "getprop", "ro.product.brand"); if (result.succeeded()) { name += result.getStdout() + " "; } - result = env.getSDK().runADB("-s", id, "shell", "getprop", "ro.product.model"); + result = adb("shell", "getprop", "ro.product.model"); if (result.succeeded()) { name += result.getStdout(); } @@ -102,11 +100,11 @@ public String getName() { } catch (IOException e) { e.printStackTrace(); } - + name += " [" + id + "]"; - + // if (hasFeature("watch")) { -// name += " (watch)"; +// name += " (watch)"; // } return name; @@ -130,14 +128,14 @@ public boolean installApp(final AndroidBuild build, final RunnerListener status) return false; } bringLauncherToFront(); - + String apkPath = build.getPathForAPK(); if (apkPath == null) { status.statusError("Could not install the sketch."); - System.err.println("The APK file is missing"); + System.err.println("The APK file is missing"); return false; } - + try { final ProcessResult installResult = adb("install", "-r", apkPath); if (!installResult.succeeded()) { @@ -180,23 +178,35 @@ public boolean removeApp(String packageName) throws IOException, InterruptedExce return true; } - + // different version that actually runs through JDI: // http://asantoso.wordpress.com/2009/09/26/using-jdb-with-adb-to-debugging-of-android-app-on-a-real-device/ - public boolean launchApp(final String packageName) + public boolean launchApp(final String packageName, boolean isDebuggerEnabled) throws IOException, InterruptedException { if (!isAlive()) { return false; } - String[] cmd = { - "shell", "am", "start", - "-e", "debug", "true", - "-a", "android.intent.action.MAIN", - "-c", "android.intent.category.LAUNCHER", - "-n", packageName + "/.MainActivity" - }; -// PApplet.println(cmd); - ProcessResult pr = adb(cmd); + ProcessResult pr; + if (isDebuggerEnabled) { + String[] cmd = { + "shell", "am", "start", + "-e", "debug", "true", + "-a", "android.intent.action.MAIN", + "-c", "android.intent.category.LAUNCHER", "-D", + "-n", packageName + "/.MainActivity" + }; + pr = adb(cmd); + }else { + String[] cmd = { + "shell", "am", "start", + "-e", "debug", "true", + "-a", "android.intent.action.MAIN", + "-c", "android.intent.category.LAUNCHER", + "-n", packageName + "/.MainActivity" + }; + pr = adb(cmd); + } + if (Base.DEBUG) { System.out.println(pr.toString()); } @@ -210,6 +220,35 @@ public boolean launchApp(final String packageName) return pr.succeeded(); } + public void forwardPort(int tcpPort) throws IOException, InterruptedException { + // Start ADB Server + adb("start-server"); + + Process deviceId = adbProc("jdwp"); + + // Get Process ID from ADB command `adb jdwp` + JDWPProcessor pIDProcessor = new JDWPProcessor(); + new StreamPump(deviceId.getInputStream(), "jdwp: ").addTarget( + pIDProcessor).start(); + new StreamPump(deviceId.getErrorStream(), "jdwperr: ").addTarget( + System.err).start(); + + Thread.sleep(1000); + + // Forward to tcp port + adb("forward", "tcp:" + tcpPort, "jdwp:" + pIDProcessor.getId()); + } + + private class JDWPProcessor implements LineProcessor { + private int pId; + public void processLine(final String line) { + pId = Integer.parseInt(line); + } + public int getId() { + return pId; + } + } + public boolean isEmulator() { return id.startsWith("emulator"); } @@ -217,7 +256,7 @@ public boolean isEmulator() { public void setPackageName(String pkgName) { packageName = pkgName; } - + // I/Process ( 9213): Sending signal. PID: 9213 SIG: 9 private static final Pattern SIG = Pattern .compile("PID:\\s+(\\d+)\\s+SIG:\\s+(\\d+)"); @@ -227,13 +266,13 @@ public void setPackageName(String pkgName) { private class LogLineProcessor implements LineProcessor { public void processLine(final String line) { final LogEntry entry = new LogEntry(line); -// System.err.println("***************************************************"); +// System.out.println("***************************************************"); // System.out.println(line); -// System.err.println(activeProcesses); -// System.err.println(entry.message); - +// System.out.println(activeProcesses); +// System.out.println(entry.message); + if (entry.message.startsWith("PROCESSING")) { - // Old start/stop process detection, does not seem to work anymore. + // Old start/stop process detection, does not seem to work anymore. // Should be ok to remove at some point. if (entry.message.contains("onStart")) { startProc(entry.source, entry.pid); @@ -241,12 +280,12 @@ public void processLine(final String line) { endProc(entry.pid); } } else if (packageName != null && !packageName.equals("") && - entry.message.contains("Start proc") && + entry.message.contains("Start proc") && entry.message.contains(packageName)) { // Sample message string from logcat when starting process: - // "Start proc 29318:processing.test.sketch001/u0a403 for activity processing.test.sketch001/.MainActivity" + // "Start proc 29318:processing.test.sketch001/u0a403 for activity processing.test.sketch001/.MainActivity" boolean pidFound = false; - + try { int idx0 = entry.message.indexOf("Start proc") + 11; int idx1 = entry.message.indexOf(packageName) - 1; @@ -255,29 +294,29 @@ public void processLine(final String line) { startProc(entry.source, pid); pidFound = true; } catch (Exception ex) { } - + if (!pidFound) { // In some cases (old adb maybe?): // https://github.com/processing/processing-android/issues/331 // the process start line is slightly different: - // I/ActivityManager( 648): Start proc processing.test.sketch_170818a for activity processing.test.sketch_170818a/.MainActivity: pid=4256 uid=10175 gids={50175} + // I/ActivityManager( 648): Start proc processing.test.sketch_170818a for activity processing.test.sketch_170818a/.MainActivity: pid=4256 uid=10175 gids={50175} try { int idx0 = entry.message.indexOf("pid=") + 4; int idx1 = entry.message.indexOf("uid") - 1; String pidStr = entry.message.substring(idx0, idx1); int pid = Integer.parseInt(pidStr); startProc(entry.source, pid); - pidFound = true; + pidFound = true; } catch (Exception ex) { } - + if (!pidFound) { System.err.println("AndroidDevice: cannot find process id, console output will be disabled."); } } } else if (packageName != null && !packageName.equals("") && - entry.message.contains("Killing") && - entry.message.contains(packageName)) { - // Sample message string from logcat when stopping process: + entry.message.contains("Killing") && + entry.message.contains(packageName)) { + // Sample message string from logcat when stopping process: // "Killing 31360:processing.test.test1/u0a403 (adj 900): remove task" try { int idx0 = entry.message.indexOf("Killing") + 8; @@ -287,7 +326,7 @@ public void processLine(final String line) { endProc(pid); } catch (Exception ex) { System.err.println("AndroidDevice: cannot find process id, console output will continue. " + packageName); - } + } } else if (entry.source.equals("Process")) { handleCrash(entry); } else if (activeProcesses.contains(entry.pid)) { @@ -349,9 +388,11 @@ private void reportStackTrace(final LogEntry entry) { void initialize() throws IOException, InterruptedException { adb("logcat", "-c"); - final String[] cmd = generateAdbCommand("logcat", "-v", "brief"); + + final String[] cmd = genAdbCommand("logcat", "-v", "brief"); final String title = PApplet.join(cmd, ' '); - logcat = Runtime.getRuntime().exec(cmd); + logcat = env.getSDK().getAdbProcess(cmd); + ProcessRegistry.watch(logcat); new StreamPump(logcat.getInputStream(), "log: " + title).addTarget( new LogLineProcessor()).start(); @@ -361,9 +402,6 @@ void initialize() throws IOException, InterruptedException { public void run() { try { logcat.waitFor(); - // final int result = logcat.waitFor(); - // System.err.println("AndroidDevice: " + getId() + " logcat exited " - // + (result == 0 ? "normally" : "with status " + result)); } catch (final InterruptedException e) { System.err .println("AndroidDevice: logcat process monitor interrupted"); @@ -428,15 +466,17 @@ public void removeListener(final DeviceListener listener) { } private ProcessResult adb(final String... cmd) throws InterruptedException, IOException { - final String[] adbCmd = generateAdbCommand(cmd); - return env.getSDK().runADB(adbCmd); + final String[] adbCmd = genAdbCommand(cmd); + return env.getSDK().runAdb(adbCmd); + } + + private Process adbProc(final String... cmd) throws IOException { + final String[] adbCmd = genAdbCommand(cmd); + return env.getSDK().getAdbProcess(adbCmd); } - private String[] generateAdbCommand(final String... cmd) throws IOException { - File toolsPath = env.getSDK().getPlatformToolsFolder(); - File abdPath = Platform.isWindows() ? new File(toolsPath, "adb.exe") : - new File(toolsPath, "adb"); - return PApplet.concat(new String[] { abdPath.getCanonicalPath(), "-s", getId() }, cmd); + private String[] genAdbCommand(final String... cmd) { + return PApplet.concat(new String[] { "-s", getId() }, cmd); } @Override diff --git a/mode/src/processing/mode/android/DeviceListener.java b/processing/mode/src/processing/mode/android/DeviceListener.java similarity index 94% rename from mode/src/processing/mode/android/DeviceListener.java rename to processing/mode/src/processing/mode/android/DeviceListener.java index 83b979dcf..f6be233be 100644 --- a/mode/src/processing/mode/android/DeviceListener.java +++ b/processing/mode/src/processing/mode/android/DeviceListener.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-16 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 diff --git a/mode/src/processing/mode/android/Devices.java b/processing/mode/src/processing/mode/android/Devices.java similarity index 87% rename from mode/src/processing/mode/android/Devices.java rename to processing/mode/src/processing/mode/android/Devices.java index e2df661c9..92f1e2208 100644 --- a/mode/src/processing/mode/android/Devices.java +++ b/processing/mode/src/processing/mode/android/Devices.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-16 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -41,23 +41,9 @@ * */ class Devices { - private static final String ADB_DEVICES_ERROR = - "Received unfamiliar output from “adb devices”.\n" + - "The device list may have errors."; - private static final String DEVICE_PERMISSIONS_URL = "https://developer.android.com/studio/run/device.html"; - private static final String DEVICE_PERMISSIONS_TITLE = - "Found devices with no permissions!"; - - private static final String DEVICE_PERMISSIONS_MESSAGE = - "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 runnings apps on hardware device " + - "for more details."; - private static final Devices INSTANCE = new Devices(); private static final String BT_DEBUG_PORT = "4444"; @@ -97,7 +83,7 @@ public void killAdbServer() { System.out.print("Shutting down any existing adb server..."); System.out.flush(); try { - sdk.runADB("kill-server"); + sdk.runAdb("kill-server"); System.out.println(" Done."); } catch (final Exception e) { System.err.println("/nDevices.killAdbServer() failed."); @@ -109,7 +95,7 @@ public void startAdbServer() { System.out.print("Starting a new adb server..."); System.out.flush(); try { - sdk.runADB("start-server"); + sdk.runAdb("start-server"); System.out.println(" Done."); } catch (final Exception e) { System.err.println("/nDevices.startAdbServer() failed."); @@ -132,9 +118,9 @@ public void enableBluetoothDebugging() { try { // Try Enable debugging over bluetooth // http://developer.android.com/training/wearables/apps/bt-debugging.html - sdk.runADB("-s", device.getId(), "forward", "tcp:" + BT_DEBUG_PORT, + sdk.runAdb("-s", device.getId(), "forward", "tcp:" + BT_DEBUG_PORT, "localabstract:/adb-hub"); - sdk.runADB("connect", "127.0.0.1:" + BT_DEBUG_PORT); + sdk.runAdb("connect", "127.0.0.1:" + BT_DEBUG_PORT); } catch (final Exception e) { e.printStackTrace(); } @@ -363,7 +349,7 @@ public List list() { ProcessResult result; try { // System.out.println("listing devices 00"); - result = sdk.runADB("devices"); + result = sdk.runAdb("devices"); // System.out.println("listing devices 05"); } catch (InterruptedException e) { return Collections.emptyList(); @@ -389,8 +375,8 @@ public List list() { // might read "List of devices attached" final String stdout = result.getStdout(); if (!(stdout.contains("List of devices") || stdout.trim().length() == 0)) { - System.err.println(ADB_DEVICES_ERROR); - System.err.println("Output was “" + stdout + "”"); + System.err.println(AndroidMode.getTextString("android_devices.error.cannot_get_device_list")); + System.err.println(stdout); return Collections.emptyList(); } @@ -402,7 +388,8 @@ public List list() { if (fields[1].equals("device")) { devices.add(fields[0]); } else if (fields[1].contains("no permissions") && showPermissionsErrorMessage) { - AndroidUtil.showMessage(DEVICE_PERMISSIONS_TITLE, DEVICE_PERMISSIONS_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("android_devices.error.no_permissions_title"), + AndroidMode.getTextString("android_devices.error.no_permissions_body", DEVICE_PERMISSIONS_URL)); showPermissionsErrorMessage = false; } } diff --git a/mode/src/processing/mode/android/EmulatorController.java b/processing/mode/src/processing/mode/android/EmulatorController.java similarity index 95% rename from mode/src/processing/mode/android/EmulatorController.java rename to processing/mode/src/processing/mode/android/EmulatorController.java index 555bfaaa9..e3d9b7a13 100644 --- a/mode/src/processing/mode/android/EmulatorController.java +++ b/processing/mode/src/processing/mode/android/EmulatorController.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-16 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -26,7 +26,6 @@ import java.util.concurrent.CountDownLatch; import processing.app.Base; -import processing.app.Platform; import processing.app.exec.*; import processing.core.PApplet; @@ -75,10 +74,14 @@ synchronized public void launch(final AndroidSDK sdk, final boolean wear) // https://developer.android.com/studio/run/emulator-acceleration.html#accel-graphics String gpuFlag = "auto"; - File emulatorPath = Platform.isWindows() ? new File(sdk.getToolsFolder(), "emulator.exe") : - new File(sdk.getToolsFolder(), "emulator"); + final File emulator = sdk.getEmulatorTool(); + if (emulator == null || !emulator.exists()) { + System.err.println("EmulatorController: Emulator is not available."); + return; + } + final String[] cmd = new String[] { - emulatorPath.getCanonicalPath(), + emulator.getCanonicalPath(), "-avd", avdName, "-port", portString, "-gpu", gpuFlag @@ -143,7 +146,7 @@ public void run() { } Thread.sleep(2000); //System.out.println("done sleeping"); - ProcessResult result = sdk.runADB("-s", "emulator-" + portString, + ProcessResult result = sdk.runAdb("-s", "emulator-" + portString, "shell", "getprop", "dev.bootcomplete"); if (result.getStdout().trim().equals("1")) { setState(State.RUNNING); diff --git a/mode/src/processing/mode/android/KeyStoreManager.java b/processing/mode/src/processing/mode/android/KeyStoreManager.java similarity index 73% rename from mode/src/processing/mode/android/KeyStoreManager.java rename to processing/mode/src/processing/mode/android/KeyStoreManager.java index bd667f290..d968687ca 100644 --- a/mode/src/processing/mode/android/KeyStoreManager.java +++ b/processing/mode/src/processing/mode/android/KeyStoreManager.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2014-17 The Processing Foundation + Copyright (c) 2014-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -21,6 +21,7 @@ package processing.mode.android; +import processing.app.Language; import processing.app.Messages; import processing.app.Platform; import processing.app.ui.Toolkit; @@ -41,6 +42,9 @@ @SuppressWarnings("serial") public class KeyStoreManager extends JFrame { + final static protected int PACKAGE = 0; + final static protected int BUNDLE = 1; + final static private int BOX_BORDER = Toolkit.zoom(13); final static private int PASS_BORDER = Toolkit.zoom(15); final static private int LABEL_WIDTH = Toolkit.zoom(400); @@ -49,7 +53,7 @@ public class KeyStoreManager extends JFrame { static final String GUIDE_URL = "https://developer.android.com/studio/publish/app-signing.html"; - + File keyStore; AndroidEditor editor; @@ -63,14 +67,14 @@ public class KeyStoreManager extends JFrame { JTextField country; JTextField stateName; - public KeyStoreManager(final AndroidEditor editor) { + public KeyStoreManager(final AndroidEditor editor, final int kind) { super("Android keystore manager"); this.editor = editor; - createLayout(); + createLayout(kind); } - private void createLayout() { + private void createLayout(int kind) { Container outer = getContentPane(); outer.removeAll(); @@ -90,7 +94,7 @@ private void createLayout() { // buttons JPanel buttons = new JPanel(); buttons.setAlignmentX(LEFT_ALIGNMENT); - JButton okButton = new JButton("OK"); + JButton okButton = new JButton(Language.text("prompt.ok")); Dimension dim = new Dimension(Toolkit.getButtonWidth(), okButton.getPreferredSize().height); okButton.setPreferredSize(dim); @@ -104,20 +108,28 @@ public void actionPerformed(ActionEvent e) { localityName.getText(), stateName.getText(), country.getText()); setVisible(false); - editor.startExportPackage(new String(passwordField.getPassword())); + if (kind == KeyStoreManager.BUNDLE) { + editor.startExportBundle(new String(passwordField.getPassword())); + } else { + editor.startExportPackage(new String(passwordField.getPassword())); + } } catch (Exception e1) { e1.printStackTrace(); } } else { setVisible(false); - editor.startExportPackage(new String(passwordField.getPassword())); + if (kind == KeyStoreManager.BUNDLE) { + editor.startExportBundle(new String(passwordField.getPassword())); + } else { + editor.startExportPackage(new String(passwordField.getPassword())); + } } } } }); okButton.setEnabled(true); - JButton cancelButton = new JButton("Cancel"); + JButton cancelButton = new JButton(Language.text("prompt.cancel")); cancelButton.setPreferredSize(dim); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -126,29 +138,27 @@ public void actionPerformed(ActionEvent e) { }); cancelButton.setEnabled(true); - JButton resetKeystoreButton = new JButton("Reset password"); + JButton resetKeystoreButton = new JButton(AndroidMode.getTextString("keystore_manager.reset_password")); dim = new Dimension(Toolkit.getButtonWidth()*2, resetKeystoreButton.getPreferredSize().height); resetKeystoreButton.setPreferredSize(dim); resetKeystoreButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(false); - int result = Messages.showYesNoQuestion(editor, "Android keystore", - "Are you sure you want to reset the password?", - "We will have to reset the keystore to do this, which means \n" + - "you won't be able to upload an update for your app signed with\n" + - "the new keystore to Google Play.\n\n" + - "We will make a backup for the old keystore."); + int result = Messages.showYesNoQuestion(editor, AndroidMode.getTextString("keystore_manager.dialog.reset_keyboard_title"), + AndroidMode.getTextString("keystore_manager.dialog.reset_keyboard_body_part1"), + AndroidMode.getTextString("keystore_manager.dialog.reset_keyboard_body_part2")); if (result == JOptionPane.NO_OPTION) { setVisible(true); } else { if (!AndroidKeyStore.resetKeyStore()) { - Messages.showWarning("Android keystore", "Failed to remove keystore"); + Messages.showWarning(AndroidMode.getTextString("keystore_manager.warn.cannot_remove_keystore_title"), + AndroidMode.getTextString("keystore_manager.warn.cannot_remove_keystore_body")); setVisible(true); } else { keyStore = null; - createLayout(); + createLayout(kind); } } } @@ -195,7 +205,7 @@ public void actionPerformed(ActionEvent actionEvent) { private void showKeystorePasswordLayout(Box pain) { passwordField = new JPasswordField(15); - JLabel passwordLabel = new JLabel("Keystore password: "); + JLabel passwordLabel = new JLabel("" + AndroidMode.getTextString("keystore_manager.password_label") + " "); passwordLabel.setLabelFor(passwordField); JPanel textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -212,23 +222,19 @@ private boolean checkRequiredFields() { if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword())) { return true; } else { - Messages.showWarning("Passwords", "Keystore passwords do not match"); + Messages.showWarning(AndroidMode.getTextString("keystore_manager.warn.password_missmatch_title"), + AndroidMode.getTextString("keystore_manager.warn.password_missmatch_body")); return false; } } else { - Messages.showWarning("Passwords", "Keystore password should be at least 6 characters long"); + Messages.showWarning(AndroidMode.getTextString("keystore_manager.warn.short_password_title"), + AndroidMode.getTextString("keystore_manager.warn.short_password_body")); return false; } } private void showKeystoreCredentialsLayout(Box box) { - String labelText = - "" + - "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."; + String labelText = AndroidMode.getTextString("keystore_manager.top_label"); JLabel textarea = new JLabel(labelText); textarea.setPreferredSize(new Dimension(LABEL_WIDTH, LABEL_HEIGHT)); textarea.addMouseListener(new MouseAdapter() { @@ -241,7 +247,7 @@ public void mouseClicked(MouseEvent e) { // password field passwordField = new JPasswordField(15); - JLabel passwordLabel = new JLabel("Keystore password: "); + JLabel passwordLabel = new JLabel("" + AndroidMode.getTextString("keystore_manager.password_label") + " "); passwordLabel.setLabelFor(passwordField); JPanel textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -252,7 +258,7 @@ public void mouseClicked(MouseEvent e) { // repeat password field repeatPasswordField = new JPasswordField(15); - JLabel repeatPasswordLabel = new JLabel("Repeat keystore password: "); + JLabel repeatPasswordLabel = new JLabel("" + AndroidMode.getTextString("keystore_manager.repeat_password_label") + " "); repeatPasswordLabel.setLabelFor(passwordField); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -263,14 +269,14 @@ public void mouseClicked(MouseEvent e) { box.add(textPane); MatteBorder mb = new MatteBorder(1, 0, 0, 0, Color.LIGHT_GRAY); - TitledBorder tb = new TitledBorder(mb, "Keystore issuer credentials", TitledBorder.LEFT, TitledBorder.DEFAULT_POSITION); + TitledBorder tb = new TitledBorder(mb, AndroidMode.getTextString("keystore_manager.issuer_credentials_header"), TitledBorder.LEFT, TitledBorder.DEFAULT_POSITION); JPanel separatorPanel = new JPanel(); separatorPanel.setBorder(tb); box.add(separatorPanel); // common name (CN) commonName = new JTextField(15); - JLabel commonNameLabel = new JLabel("First and last name: "); + JLabel commonNameLabel = new JLabel(AndroidMode.getTextString("keystore_manager.common_name_label")); commonNameLabel.setLabelFor(commonName); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -281,7 +287,7 @@ public void mouseClicked(MouseEvent e) { // organizational unit (OU) organizationalUnit = new JTextField(15); - JLabel organizationalUnitLabel = new JLabel("Organizational unit: "); + JLabel organizationalUnitLabel = new JLabel(AndroidMode.getTextString("keystore_manager.organizational_unitl_label")); organizationalUnitLabel.setLabelFor(organizationalUnit); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -292,7 +298,7 @@ public void mouseClicked(MouseEvent e) { // organization name (O) organizationName = new JTextField(15); - JLabel organizationNameLabel = new JLabel("Organization name: "); + JLabel organizationNameLabel = new JLabel(AndroidMode.getTextString("keystore_manager.organization_name_label")); organizationNameLabel.setLabelFor(organizationName); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -303,7 +309,7 @@ public void mouseClicked(MouseEvent e) { // locality name (L) localityName = new JTextField(15); - JLabel localityNameLabel = new JLabel("City or locality: "); + JLabel localityNameLabel = new JLabel(AndroidMode.getTextString("keystore_manager.city_name_label")); localityNameLabel.setLabelFor(localityName); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -314,7 +320,7 @@ public void mouseClicked(MouseEvent e) { // state name (S) stateName = new JTextField(15); - JLabel stateNameLabel = new JLabel("State name: "); + JLabel stateNameLabel = new JLabel(AndroidMode.getTextString("keystore_manager.state_name_label")); stateNameLabel.setLabelFor(stateName); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); @@ -325,7 +331,7 @@ public void mouseClicked(MouseEvent e) { // country (C) country = new JTextField(15); - JLabel countryLabel = new JLabel("Country code (XX): "); + JLabel countryLabel = new JLabel(AndroidMode.getTextString("keystore_manager.country_code_label")); countryLabel.setLabelFor(country); textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING)); diff --git a/mode/src/processing/mode/android/Keys.java b/processing/mode/src/processing/mode/android/Keys.java similarity index 95% rename from mode/src/processing/mode/android/Keys.java rename to processing/mode/src/processing/mode/android/Keys.java index fd20cbd30..02b796227 100644 --- a/mode/src/processing/mode/android/Keys.java +++ b/processing/mode/src/processing/mode/android/Keys.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-16 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2010-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify diff --git a/mode/src/processing/mode/android/LogEntry.java b/processing/mode/src/processing/mode/android/LogEntry.java similarity index 97% rename from mode/src/processing/mode/android/LogEntry.java rename to processing/mode/src/processing/mode/android/LogEntry.java index 86048343d..e4b0fd51c 100644 --- a/mode/src/processing/mode/android/LogEntry.java +++ b/processing/mode/src/processing/mode/android/LogEntry.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-16 The Processing Foundation + Copyright (c) 2013-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 diff --git a/mode/src/processing/mode/android/Manifest.java b/processing/mode/src/processing/mode/android/Manifest.java similarity index 81% rename from mode/src/processing/mode/android/Manifest.java rename to processing/mode/src/processing/mode/android/Manifest.java index 29bf7a863..1cc755f86 100644 --- a/mode/src/processing/mode/android/Manifest.java +++ b/processing/mode/src/processing/mode/android/Manifest.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2012-17 The Processing Foundation + Copyright (c) 2012-21 The Processing Foundation Copyright (c) 2010-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -44,7 +44,8 @@ public class Manifest { static final String MANIFEST_XML = "AndroidManifest.xml"; - static final String MANIFEST_ERROR = + static final String MANIFEST_ERROR_TITLE = "Error handling " + MANIFEST_XML; + static final String MANIFEST_ERROR_MESSAGE = "Errors occurred while reading or writing " + MANIFEST_XML + ",\n" + "which means lots of things are likely to stop working properly.\n" + "To prevent losing any data, it's recommended that you use “Save As”\n" + @@ -55,6 +56,7 @@ public class Manifest { "WallpaperManifest.xml.tmpl", "WatchFaceManifest.xml.tmpl", "VRManifest.xml.tmpl", + "ARManifest.xml.tmpl" }; // Default base package name, user need to change when exporting package. @@ -118,15 +120,6 @@ public void setPackageName(String packageName) { save(); } - - public void setSdkTarget(String version) { - XML usesSdk = xml.getChild("uses-sdk"); - if (usesSdk != null) { - usesSdk.setString("android:targetSdkVersion", version); - save(); - } - } - public String[] getPermissions() { XML[] elements = xml.getChildren("uses-permission"); @@ -135,7 +128,7 @@ public String[] getPermissions() { for (int i = 0; i < count; i++) { String tmp = elements[i].getString("android:name"); if (tmp.indexOf("android.permission") == 0) { - // Standard permission, remove perfix + // Standard permission, remove prefix int idx = tmp.lastIndexOf("."); names[i] = tmp.substring(idx + 1); } else { @@ -152,6 +145,7 @@ public void setPermissions(String[] names) { boolean hasWakeLock = false; boolean hasVibrate = false; boolean hasReadExtStorage = false; + boolean hasCameraAccess = false; // Remove all the old permissions... for (XML kid : xml.getChildren("uses-permission")) { @@ -170,6 +164,10 @@ public void setPermissions(String[] names) { hasReadExtStorage = true; continue; } + if (appComp == AndroidBuild.AR && name.equals(PERMISSION_PREFIX + "CAMERA")) { + hasCameraAccess = true; + continue; + } // Don't remove non-standard permissions, such as // com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA @@ -185,6 +183,7 @@ public void setPermissions(String[] names) { if (appComp == AndroidBuild.WATCHFACE && name.equals("WAKE_LOCK")) continue; if (appComp == AndroidBuild.VR && name.equals("VIBRATE")) continue; if (appComp == AndroidBuild.VR && name.equals("READ_EXTERNAL_STORAGE")) continue; + if (appComp == AndroidBuild.AR && name.equals(PERMISSION_PREFIX + "CAMERA")) continue; XML newbie = xml.addChild("uses-permission"); if (-1 < name.indexOf(".")) { @@ -208,6 +207,10 @@ public void setPermissions(String[] names) { xml.addChild("uses-permission"). setString("android:name", PERMISSION_PREFIX + "READ_EXTERNAL_STORAGE"); } + if (appComp == AndroidBuild.AR && !hasCameraAccess) { + xml.addChild("uses-permission"). + setString("android:name", PERMISSION_PREFIX + "CAMERA"); + } save(); } @@ -216,7 +219,8 @@ public void setPermissions(String[] names) { private void fixPermissions(XML mf) { boolean hasWakeLock = false; boolean hasVibrate = false; - boolean hasReadExtStorage = false; + boolean hasReadExtStorage = false; + boolean hasCameraAccess = false; for (XML kid : mf.getChildren("uses-permission")) { String name = kid.getString("android:name"); if (appComp == AndroidBuild.WATCHFACE && name.equals(PERMISSION_PREFIX + "WAKE_LOCK")) { @@ -231,6 +235,15 @@ private void fixPermissions(XML mf) { hasReadExtStorage = true; continue; } + if (appComp == AndroidBuild.AR && name.equals(PERMISSION_PREFIX + "CAMERA")) { + hasCameraAccess = true; + continue; + } + + if (appComp == AndroidBuild.AR && !hasCameraAccess) { + mf.addChild("uses-permission"). + setString("android:name", PERMISSION_PREFIX + "CAMERA"); + } } if (appComp == AndroidBuild.WATCHFACE && !hasWakeLock) { mf.addChild("uses-permission"). @@ -248,20 +261,9 @@ private void fixPermissions(XML mf) { private void writeBlankManifest(final File xmlFile, final int appComp) { - File xmlTemplate = new File(modeFolder, "templates/" + MANIFEST_TEMPLATE[appComp]); - + File xmlTemplate = new File(modeFolder, "templates/" + MANIFEST_TEMPLATE[appComp]); HashMap replaceMap = new HashMap(); - if (appComp == AndroidBuild.APP) { - replaceMap.put("@@min_sdk@@", AndroidBuild.MIN_SDK_APP); - } else if (appComp == AndroidBuild.WALLPAPER) { - replaceMap.put("@@min_sdk@@", AndroidBuild.MIN_SDK_WALLPAPER); - } else if (appComp == AndroidBuild.WATCHFACE) { - replaceMap.put("@@min_sdk@@", AndroidBuild.MIN_SDK_WATCHFACE); - } else if (appComp == AndroidBuild.VR) { - replaceMap.put("@@min_sdk@@", AndroidBuild.MIN_SDK_VR); - } - - AndroidUtil.createFileFromTemplate(xmlTemplate, xmlFile, replaceMap); + AndroidUtil.createFileFromTemplate(xmlTemplate, xmlFile, replaceMap); } @@ -300,9 +302,9 @@ protected void writeCopy(File file, String className) throws IOException { } } - // Make sure that the required permissions for watch faces and VR apps are + // Make sure that the required permissions for watch faces, AR and VR apps are // included. - if (appComp == AndroidBuild.WATCHFACE || appComp == AndroidBuild.VR) { + if (appComp == AndroidBuild.WATCHFACE || appComp == AndroidBuild.VR|| appComp == AndroidBuild.AR) { fixPermissions(mf); } @@ -321,6 +323,37 @@ protected void load(boolean forceNew) { if (manifestFile.exists()) { try { xml = new XML(manifestFile); + + boolean saveOld = false; + + XML app = xml.getChild("application"); + String icon = app.getString("android:icon"); + if (icon.equals("@drawable/icon")) { + // Manifest file generated with older version of the mode, replace icon and save + app.setString("android:icon", "@mipmap/ic_launcher"); + saveOld = true; + } + + XML activity = app.getChild("activity"); + XML service = app.getChild("service"); + if (activity != null && activity.getString("android:name").equals(".MainActivity")) { + addExportedAttrib(activity); + saveOld = true; + } + if (service != null && service.getString("android:name").equals(".MainService")) { + addExportedAttrib(service); + saveOld = true; + } + + XML usesSDK = xml.getChild("uses-sdk"); + if (usesSDK != null) { + // Manifest file generated with older version of the mode, uses-sdk is no longer needed in manifest + xml.removeChild(usesSDK); + saveOld = true; + } + + if (saveOld && !forceNew) save(); + } catch (Exception e) { e.printStackTrace(); System.err.println("Problem reading AndroidManifest.xml, creating a new version"); @@ -378,7 +411,15 @@ protected void load(boolean forceNew) { } } if (xml == null) { - Messages.showWarning("Error handling " + MANIFEST_XML, MANIFEST_ERROR); + Messages.showWarning(AndroidMode.getTextString("manifest.warn.cannot_handle_file_title", MANIFEST_XML), + AndroidMode.getTextString("manifest.warn.cannot_handle_file_body", MANIFEST_XML)); + } + } + + protected void addExportedAttrib(XML child) { + if (!child.hasAttribute("android:exported")) { + // Manifest file generated with older version of the mode, missing android:exported attributed + child.setString("android:exported", "true"); } } diff --git a/processing/mode/src/processing/mode/android/Pair.java b/processing/mode/src/processing/mode/android/Pair.java new file mode 100644 index 000000000..04ef7ea81 --- /dev/null +++ b/processing/mode/src/processing/mode/android/Pair.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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 + * + * http://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. + */ + + package processing.mode.android; + +/** + * Pair of two elements. + */ +public final class Pair { + private final A mFirst; + private final B mSecond; + private Pair(A first, B second) { + mFirst = first; + mSecond = second; + } + public static Pair create(A first, B second) { + return new Pair(first, second); + } + public A getFirst() { + return mFirst; + } + public B getSecond() { + return mSecond; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); + result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + @SuppressWarnings("rawtypes") + Pair other = (Pair) obj; + if (mFirst == null) { + if (other.mFirst != null) { + return false; + } + } else if (!mFirst.equals(other.mFirst)) { + return false; + } + if (mSecond == null) { + if (other.mSecond != null) { + return false; + } + } else if (!mSecond.equals(other.mSecond)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/processing/mode/src/processing/mode/android/Permissions.java b/processing/mode/src/processing/mode/android/Permissions.java new file mode 100644 index 000000000..10b80f98f --- /dev/null +++ b/processing/mode/src/processing/mode/android/Permissions.java @@ -0,0 +1,683 @@ +/* -*- 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) 2010-12 Ben Fry and Casey Reas + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.mode.android; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.*; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; + +import processing.app.Language; +import processing.app.Platform; +import processing.app.Sketch; +import processing.app.ui.Toolkit; + + +@SuppressWarnings("serial") +public class Permissions extends JFrame { + static final String GUIDE_URL = + "https://developer.android.com/training/articles/security-tips.html#Permissions"; + + static final int BORDER_HORIZ = Toolkit.zoom(5); + static final int BORDER_VERT = Toolkit.zoom(3); + static final int CELL_HEIGHT = Toolkit.zoom(20); + static final int BORDER = Toolkit.zoom(13); + static final int TEXT_WIDTH = Toolkit.zoom(400); + static final int TEXT_HEIGHT = Toolkit.zoom(80); + static final int URL_WIDTH = Toolkit.zoom(400); + static final int URL_HEIGHT = Toolkit.zoom(30); + static final int DESC_WIDTH = Toolkit.zoom(400); + static final int DESC_HEIGHT = Toolkit.zoom(50); + static final int GAP = Toolkit.zoom(8); + + JScrollPane permissionScroller; + JList permissionList; + JLabel descriptionLabel; + Sketch sketch; + + int appComp; + + File modeFolder; + + + public Permissions(Sketch sketch, int appComp, File modeFolder) { + super("Android Permissions Selector"); + this.appComp = appComp; + this.sketch = sketch; + this.modeFolder = modeFolder; + + permissionList = new CheckBoxList(); + permissionList.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting() == false) { + int index = permissionList.getSelectedIndex(); + if (index == -1) { + descriptionLabel.setText(""); + } else { + descriptionLabel.setText("" + description[index] + ""); + } + } + } + }); + permissionList.setFixedCellHeight(CELL_HEIGHT); + permissionList.setBorder(new EmptyBorder(BORDER_VERT, BORDER_HORIZ, + BORDER_VERT, BORDER_HORIZ)); + + DefaultListModel model = new DefaultListModel(); + permissionList.setModel(model); + for (String item : title) { + model.addElement(new JCheckBox(item)); + } + + permissionScroller = + new JScrollPane(permissionList, + ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + permissionList.setVisibleRowCount(12); + permissionList.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + if (e.getKeyChar() == ' ') { + int index = permissionList.getSelectedIndex(); + JCheckBox checkbox = + permissionList.getModel().getElementAt(index); + checkbox.setSelected(!checkbox.isSelected()); + permissionList.repaint(); + } + } + }); + + Container outer = getContentPane(); + Box vbox = Box.createVerticalBox(); + vbox.setBorder(new EmptyBorder(BORDER, BORDER, BORDER, BORDER)); + outer.add(vbox); + + String labelText = AndroidMode.getTextString("permissions.dialog.label"); + String urlText = AndroidMode.getTextString("permissions.dialog.url", GUIDE_URL); + JLabel textarea = new JLabel(labelText); + JLabel urlarea = new JLabel(urlText); + textarea.setPreferredSize(new Dimension(TEXT_WIDTH, TEXT_HEIGHT)); + urlarea.setPreferredSize(new Dimension(URL_WIDTH, URL_HEIGHT)); + urlarea.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + Platform.openURL(GUIDE_URL); + } + }); + urlarea.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); + textarea.setAlignmentX(LEFT_ALIGNMENT); + urlarea.setAlignmentX(LEFT_ALIGNMENT); + vbox.add(textarea); + vbox.add(urlarea); + + permissionScroller.setAlignmentX(LEFT_ALIGNMENT); + vbox.add(permissionScroller); + vbox.add(Box.createVerticalStrut(GAP)); + + descriptionLabel = new JLabel(); + descriptionLabel.setPreferredSize(new Dimension(DESC_WIDTH, DESC_HEIGHT)); + descriptionLabel.setVerticalAlignment(SwingConstants.TOP); + descriptionLabel.setAlignmentX(LEFT_ALIGNMENT); + vbox.add(descriptionLabel); + vbox.add(Box.createVerticalStrut(GAP)); + + JPanel buttons = new JPanel(); + buttons.setAlignmentX(LEFT_ALIGNMENT); + JButton okButton = new JButton(Language.text("prompt.ok")); + Dimension dim = new Dimension(Toolkit.getButtonWidth(), + okButton.getPreferredSize().height); + okButton.setPreferredSize(dim); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + saveSelections(); + setVisible(false); + } + }); + okButton.setEnabled(true); + + JButton cancelButton = new JButton(Language.text("prompt.cancel")); + cancelButton.setPreferredSize(dim); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setVisible(false); + } + }); + cancelButton.setEnabled(true); + + // think different, biznatchios! + if (Platform.isMacOS()) { + buttons.add(cancelButton); + buttons.add(okButton); + } else { + buttons.add(okButton); + buttons.add(cancelButton); + } + vbox.add(buttons); + + JRootPane root = getRootPane(); + root.setDefaultButton(okButton); + ActionListener disposer = new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + setVisible(false); + } + }; + Toolkit.registerWindowCloseKeys(root, disposer); + Toolkit.setIcon(this); + + pack(); + + Dimension screen = Toolkit.getScreenSize(); + Dimension windowSize = getSize(); + + setLocation((screen.width - windowSize.width) / 2, + (screen.height - windowSize.height) / 2); + + Manifest mf = new Manifest(sketch, appComp, modeFolder, false); + setSelections(mf.getPermissions()); + + // show the window and get to work + setVisible(true); + } + + + @SuppressWarnings("rawtypes") + protected void setSelections(String[] sel) { +// processing.core.PApplet.println("permissions are:"); +// processing.core.PApplet.println(sel); + HashMap map = new HashMap(); + for (String s : sel) { + map.put(s, new Object()); + } + DefaultListModel model = (DefaultListModel) permissionList.getModel(); + for (int i = 0; i < count; i++) { + JCheckBox box = (JCheckBox) model.get(i); +// System.out.println(map.containsKey(box.getText()) + " " + box.getText()); + box.setSelected(map.containsKey(box.getText())); + } + } + + + @SuppressWarnings("rawtypes") + protected String[] getSelections() { + ArrayList sel = new ArrayList(); + DefaultListModel model = (DefaultListModel) permissionList.getModel(); + for (int i = 0; i < count; i++) { + if (((JCheckBox) model.get(i)).isSelected()) { + sel.add(title[i]); + } + } + return sel.toArray(new String[0]); + } + + + protected void saveSelections() { + String[] sel = getSelections(); + Manifest mf = new Manifest(sketch, appComp, modeFolder, false); + mf.setPermissions(sel); + } + + + public String getMenuTitle() { + return "Android Permissions"; + } + + + // List of constants for each permission and a brief description: + // https://developer.android.com/reference/android/Manifest.permission + static final String[] listing = { + "ACCEPT_HANDOVER", "Allows a calling app to continue a call which was started in another app.", + "ACCESS_BACKGROUND_LOCATION", "Allows an app to access location in the background.", + "ACCESS_BLOBS_ACROSS_USERS", "Allows an application to access data blobs across users.", + "ACCESS_CHECKIN_PROPERTIES", "Allows read/write access to the \"properties\" table in the checkin database, to change values that get uploaded.", + "ACCESS_COARSE_LOCATION", "Allows an app to access approximate location.", + "ACCESS_FINE_LOCATION", "Allows an app to access precise location.", + "ACCESS_HIDDEN_PROFILES", "Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property", + "ACCESS_LOCATION_EXTRA_COMMANDS", "Allows an application to access extra location provider commands.", + "ACCESS_MEDIA_LOCATION", "Allows an application to access any geographic locations persisted in the user's shared collection.", + "ACCESS_NETWORK_STATE", "Allows applications to access information about networks.", + "ACCESS_NOTIFICATION_POLICY", "Marker permission for applications that wish to access notification policy.", + "ACCESS_WIFI_STATE", "Allows applications to access information about Wi-Fi networks.", + "ACCOUNT_MANAGER", "Allows applications to call into AccountAuthenticators.", + "ACTIVITY_RECOGNITION", "Allows an application to recognize physical activity.", + "ADD_VOICEMAIL", "Allows an application to add voicemails into the system.", + "ANSWER_PHONE_CALLS", "Allows the app to answer an incoming phone call.", + "BATTERY_STATS", "Allows an application to collect battery statistics", + "BIND_ACCESSIBILITY_SERVICE", "Must be required by an AccessibilityService , to ensure that only the system can bind to it.", + "BIND_APPWIDGET", "Allows an application to tell the AppWidget service which application can access AppWidget's data.", + "BIND_AUTOFILL_SERVICE", "Must be required by a AutofillService , to ensure that only the system can bind to it.", + "BIND_CALL_REDIRECTION_SERVICE", "Must be required by a CallRedirectionService , to ensure that only the system can bind to it.", + "BIND_CARRIER_MESSAGING_CLIENT_SERVICE", "A subclass of CarrierMessagingClientService must be protected with this permission.", + "BIND_CARRIER_SERVICES", "The system process that is allowed to bind to services in carrier apps will have this permission.", + "BIND_COMPANION_DEVICE_SERVICE", "Must be required by any CompanionDeviceService s to ensure that only the system can bind to it.", + "BIND_CONDITION_PROVIDER_SERVICE", "Must be required by a ConditionProviderService , to ensure that only the system can bind to it.", + "BIND_CONTROLS", "Allows SystemUI to request third party controls.", + "BIND_CREDENTIAL_PROVIDER_SERVICE", "Must be required by a CredentialProviderService to ensure that only the system can bind to it.", + "BIND_DEVICE_ADMIN", "Must be required by device administration receiver, to ensure that only the system can interact with it.", + "BIND_DREAM_SERVICE", "Must be required by an DreamService , to ensure that only the system can bind to it.", + "BIND_INCALL_SERVICE", "Must be required by a InCallService , to ensure that only the system can bind to it.", + "BIND_INPUT_METHOD", "Must be required by an InputMethodService , to ensure that only the system can bind to it.", + "BIND_MIDI_DEVICE_SERVICE", "Must be required by an MidiDeviceService , to ensure that only the system can bind to it.", + "BIND_NFC_SERVICE", "Must be required by a HostApduService or OffHostApduService to ensure that only the system can bind to it.", + "BIND_NOTIFICATION_LISTENER_SERVICE", "Must be required by an NotificationListenerService , to ensure that only the system can bind to it.", + "BIND_PRINT_SERVICE", "Must be required by a PrintService , to ensure that only the system can bind to it.", + "BIND_QUICK_ACCESS_WALLET_SERVICE", "Must be required by a QuickAccessWalletService to ensure that only the system can bind to it.", + "BIND_QUICK_SETTINGS_TILE", "Allows an application to bind to third party quick settings tiles.", + "BIND_REMOTEVIEWS", "Must be required by a RemoteViewsService , to ensure that only the system can bind to it.", + "BIND_SCREENING_SERVICE", "Must be required by a CallScreeningService , to ensure that only the system can bind to it.", + "BIND_TELECOM_CONNECTION_SERVICE", "Must be required by a ConnectionService , to ensure that only the system can bind to it.", + "BIND_TEXT_SERVICE", "Must be required by a TextService (e.g. SpellCheckerService) to ensure that only the system can bind to it.", + "BIND_TV_INPUT", "Must be required by a TvInputService to ensure that only the system can bind to it.", + "BIND_TV_INTERACTIVE_APP", "Must be required by a TvInteractiveAppService to ensure that only the system can bind to it.", + "BIND_VISUAL_VOICEMAIL_SERVICE", "Must be required by a link VisualVoicemailService to ensure that only the system can bind to it.", + "BIND_VOICE_INTERACTION", "Must be required by a VoiceInteractionService , to ensure that only the system can bind to it.", + "BIND_VPN_SERVICE", "Must be required by a VpnService , to ensure that only the system can bind to it.", + "BIND_VR_LISTENER_SERVICE", "Must be required by an VrListenerService , to ensure that only the system can bind to it.", + "BIND_WALLPAPER", "Must be required by a WallpaperService , to ensure that only the system can bind to it.", + "BLUETOOTH", "Allows applications to connect to paired bluetooth devices.", + "BLUETOOTH_ADMIN", "Allows applications to discover and pair bluetooth devices.", + "BLUETOOTH_ADVERTISE", "Required to be able to advertise to nearby Bluetooth devices.", + "BLUETOOTH_CONNECT", "Required to be able to connect to paired Bluetooth devices.", + "BLUETOOTH_PRIVILEGED", "Allows applications to pair bluetooth devices without user interaction, and to allow or disallow phonebook access or message access.", + "BLUETOOTH_SCAN", "Required to be able to discover and pair nearby Bluetooth devices.", + "BODY_SENSORS", "Allows an application to access data from sensors that the user uses to measure what is happening inside their body, such as heart rate.", + "BODY_SENSORS_BACKGROUND", "Allows an application to access data from sensors that the user uses to measure what is happening inside their body, such as heart rate.", + "BROADCAST_PACKAGE_REMOVED", "Allows an application to broadcast a notification that an application package has been removed.", + "BROADCAST_SMS", "Allows an application to broadcast an SMS receipt notification.", + "BROADCAST_STICKY", "Allows an application to broadcast sticky intents.", + "BROADCAST_WAP_PUSH", "Allows an application to broadcast a WAP PUSH receipt notification.", + "CALL_COMPANION_APP", "Allows an app which implements the InCallService API to be eligible to be enabled as a calling companion app.", + "CALL_PHONE", "Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call.", + "CALL_PRIVILEGED", "Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.", + "CAMERA", "Required to be able to access the camera device.", + "CAPTURE_AUDIO_OUTPUT", "Allows an application to capture audio output.", + "CHANGE_COMPONENT_ENABLED_STATE", "Allows an application to change whether an application component (other than its own) is enabled or not.", + "CHANGE_CONFIGURATION", "Allows an application to modify the current configuration, such as locale.", + "CHANGE_NETWORK_STATE", "Allows applications to change network connectivity state.", + "CHANGE_WIFI_MULTICAST_STATE", "Allows applications to enter Wi-Fi Multicast mode.", + "CHANGE_WIFI_STATE", "Allows applications to change Wi-Fi connectivity state.", + "CLEAR_APP_CACHE", "Allows an application to clear the caches of all installed applications on the device.", + "CONFIGURE_WIFI_DISPLAY", "Allows an application to configure and connect to Wifi displays", + "CONTROL_LOCATION_UPDATES", "Allows enabling/disabling location update notifications from the radio.", + "CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS", "Allows a browser to invoke the set of query apis to get metadata about credential candidates prepared during the CredentialManager.prepareGetCredential API.", + "CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS", "Allows specifying candidate credential providers to be queried in Credential Manager get flows, or to be preferred as a default in the Credential Manager create flows.", + "CREDENTIAL_MANAGER_SET_ORIGIN", "Allows a browser to invoke credential manager APIs on behalf of another RP.", + "DELETE_CACHE_FILES", "Old permission for deleting an app's cache files, no longer used, but signals for us to quietly ignore calls instead of throwing an exception.", + "DELETE_PACKAGES", "Allows an application to delete packages.", + "DELIVER_COMPANION_MESSAGES", "Allows an application to deliver companion messages to system", + "DETECT_SCREEN_CAPTURE", "Allows an application to get notified when a screen capture of its windows is attempted.", + "DETECT_SCREEN_RECORDING", "Allows an application to get notified when it is being recorded.", + "DIAGNOSTIC", "Allows applications to RW to diagnostic resources.", + "DISABLE_KEYGUARD", "Allows applications to disable the keyguard if it is not secure.", + "DUMP", "Allows an application to retrieve state dump information from system services.", + "ENFORCE_UPDATE_OWNERSHIP", "Allows an application to indicate via PackageInstaller.SessionParams.setRequestUpdateOwnership(boolean) that it has the intention of becoming the update owner.", + "EXECUTE_APP_ACTION", "Allows an assistive application to perform actions on behalf of users inside of applications.", + "EXPAND_STATUS_BAR", "Allows an application to expand or collapse the status bar.", + "FACTORY_TEST", "Run as a manufacturer test application, running as the root user.", + "FOREGROUND_SERVICE", "Allows a regular application to use Service.startForeground .", + "FOREGROUND_SERVICE_CAMERA", "Allows a regular application to use Service.startForeground with the type \"camera\".", + "FOREGROUND_SERVICE_CONNECTED_DEVICE", "Allows a regular application to use Service.startForeground with the type \"connectedDevice\".", + "FOREGROUND_SERVICE_DATA_SYNC", "Allows a regular application to use Service.startForeground with the type \"dataSync\".", + "FOREGROUND_SERVICE_HEALTH", "Allows a regular application to use Service.startForeground with the type \"health\".", + "FOREGROUND_SERVICE_LOCATION", "Allows a regular application to use Service.startForeground with the type \"location\".", + "FOREGROUND_SERVICE_MEDIA_PLAYBACK", "Allows a regular application to use Service.startForeground with the type \"mediaPlayback\".", + "FOREGROUND_SERVICE_MEDIA_PROCESSING", "Allows a regular application to use Service.startForeground with the type \"mediaProcessing\".", + "FOREGROUND_SERVICE_MEDIA_PROJECTION", "Allows a regular application to use Service.startForeground with the type \"mediaProjection\".", + "FOREGROUND_SERVICE_MICROPHONE", "Allows a regular application to use Service.startForeground with the type \"microphone\".", + "FOREGROUND_SERVICE_PHONE_CALL", "Allows a regular application to use Service.startForeground with the type \"phoneCall\".", + "FOREGROUND_SERVICE_REMOTE_MESSAGING", "Allows a regular application to use Service.startForeground with the type \"remoteMessaging\".", + "FOREGROUND_SERVICE_SPECIAL_USE", "Allows a regular application to use Service.startForeground with the type \"specialUse\".", + "FOREGROUND_SERVICE_SYSTEM_EXEMPTED", "Allows a regular application to use Service.startForeground with the type \"systemExempted\".", + "GET_ACCOUNTS", "Allows access to the list of accounts in the Accounts Service.", + "GET_ACCOUNTS_PRIVILEGED", "Allows access to the list of accounts in the Accounts Service.", + "GET_PACKAGE_SIZE", "Allows an application to find out the space used by any package.", + "GLOBAL_SEARCH", "This permission can be used on content providers to allow the global search system to access their data.", + "HIDE_OVERLAY_WINDOWS", "Allows an app to prevent non-system-overlay windows from being drawn on top of it", + "HIGH_SAMPLING_RATE_SENSORS", "Allows an app to access sensor data with a sampling rate greater than 200 Hz.", + "INSTALL_LOCATION_PROVIDER", "Allows an application to install a location provider into the Location Manager.", + "INSTALL_PACKAGES", "Allows an application to install packages.", + "INSTALL_SHORTCUT", "Allows an application to install a shortcut in Launcher.", + "INSTANT_APP_FOREGROUND_SERVICE", "Allows an instant app to create foreground services.", + "INTERACT_ACROSS_PROFILES", "Allows interaction across profiles in the same profile group.", + "INTERNET", "Allows applications to open network sockets.", + "KILL_BACKGROUND_PROCESSES", "Allows an application to call ActivityManager.killBackgroundProcesses(String) .", + "LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE", "Allows an application to capture screen content to perform a screenshot using the intent action Intent.ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE .", + "LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK", "An application needs this permission for Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY to show its Activity embedded in Settings app.", + "LOADER_USAGE_STATS", "Allows a data loader to read a package's access logs.", + "LOCATION_HARDWARE", "Allows an application to use location features in hardware, such as the geofencing api.", + "MANAGE_DEVICE_LOCK_STATE", "Allows financed device kiosk apps to perform actions on the Device Lock service", + "MANAGE_DEVICE_POLICY_ACCESSIBILITY", "Allows an application to manage policy related to accessibility.", + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT", "Allows an application to set policy related to account management.", + "MANAGE_DEVICE_POLICY_ACROSS_USERS", "Allows an application to set device policies outside the current user that are required for securing device ownership without accessing user data.", + "MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL", "Allows an application to set device policies outside the current user.", + "MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL", "Allows an application to set device policies outside the current user that are critical for securing data within the current user.", + "MANAGE_DEVICE_POLICY_AIRPLANE_MODE", "Allows an application to set policy related to airplane mode.", + "MANAGE_DEVICE_POLICY_APPS_CONTROL", "Allows an application to manage policy regarding modifying applications.", + "MANAGE_DEVICE_POLICY_APP_RESTRICTIONS", "Allows an application to manage application restrictions.", + "MANAGE_DEVICE_POLICY_APP_USER_DATA", "Allows an application to manage policy related to application user data.", + "MANAGE_DEVICE_POLICY_ASSIST_CONTENT", "Allows an application to set policy related to sending assist content to a privileged app such as the Assistant app.", + "MANAGE_DEVICE_POLICY_AUDIO_OUTPUT", "Allows an application to set policy related to audio output.", + "MANAGE_DEVICE_POLICY_AUTOFILL", "Allows an application to set policy related to autofill.", + "MANAGE_DEVICE_POLICY_BACKUP_SERVICE", "Allows an application to manage backup service policy.", + "MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL", "Allows an application to manage policy related to block package uninstallation.", + "MANAGE_DEVICE_POLICY_BLUETOOTH", "Allows an application to set policy related to bluetooth.", + "MANAGE_DEVICE_POLICY_BUGREPORT", "Allows an application to request bugreports with user consent.", + "MANAGE_DEVICE_POLICY_CALLS", "Allows an application to manage calling policy.", + "MANAGE_DEVICE_POLICY_CAMERA", "Allows an application to set policy related to restricting a user's ability to use or enable and disable the camera.", + "MANAGE_DEVICE_POLICY_CAMERA_TOGGLE", "Allows an application to manage policy related to camera toggle.", + "MANAGE_DEVICE_POLICY_CERTIFICATES", "Allows an application to set policy related to certificates.", + "MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE", "Allows an application to manage policy related to common criteria mode.", + "MANAGE_DEVICE_POLICY_CONTENT_PROTECTION", "Allows an application to manage policy related to content protection.", + "MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES", "Allows an application to manage debugging features policy.", + "MANAGE_DEVICE_POLICY_DEFAULT_SMS", "Allows an application to set policy related to the default sms application.", + "MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS", "Allows an application to manage policy related to device identifiers.", + "MANAGE_DEVICE_POLICY_DISPLAY", "Allows an application to set policy related to the display.", + "MANAGE_DEVICE_POLICY_FACTORY_RESET", "Allows an application to set policy related to factory reset.", + "MANAGE_DEVICE_POLICY_FUN", "Allows an application to set policy related to fun.", + "MANAGE_DEVICE_POLICY_INPUT_METHODS", "Allows an application to set policy related to input methods.", + "MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES", "Allows an application to manage installing from unknown sources policy.", + "MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES", "Allows an application to set policy related to keeping uninstalled packages.", + "MANAGE_DEVICE_POLICY_KEYGUARD", "Allows an application to manage policy related to keyguard.", + "MANAGE_DEVICE_POLICY_LOCALE", "Allows an application to set policy related to locale.", + "MANAGE_DEVICE_POLICY_LOCATION", "Allows an application to set policy related to location.", + "MANAGE_DEVICE_POLICY_LOCK", "Allows an application to lock a profile or the device with the appropriate cross-user permission.", + "MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS", "Allows an application to set policy related to lock credentials.", + "MANAGE_DEVICE_POLICY_LOCK_TASK", "Allows an application to manage lock task policy.", + "MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS", "Allows an application to set policy related to subscriptions downloaded by an admin.", + "MANAGE_DEVICE_POLICY_METERED_DATA", "Allows an application to manage policy related to metered data.", + "MANAGE_DEVICE_POLICY_MICROPHONE", "Allows an application to set policy related to restricting a user's ability to use or enable and disable the microphone.", + "MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE", "Allows an application to manage policy related to microphone toggle.", + "MANAGE_DEVICE_POLICY_MOBILE_NETWORK", "Allows an application to set policy related to mobile networks.", + "MANAGE_DEVICE_POLICY_MODIFY_USERS", "Allows an application to manage policy preventing users from modifying users.", + "MANAGE_DEVICE_POLICY_MTE", "Allows an application to manage policy related to the Memory Tagging Extension (MTE).", + "MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION", "Allows an application to set policy related to nearby communications (e.g. Beam and nearby streaming).", + "MANAGE_DEVICE_POLICY_NETWORK_LOGGING", "Allows an application to set policy related to network logging.", + "MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY", "Allows an application to manage the identity of the managing organization.", + "MANAGE_DEVICE_POLICY_OVERRIDE_APN", "Allows an application to set policy related to override APNs.", + "MANAGE_DEVICE_POLICY_PACKAGE_STATE", "Allows an application to set policy related to hiding and suspending packages.", + "MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA", "Allows an application to set policy related to physical media.", + "MANAGE_DEVICE_POLICY_PRINTING", "Allows an application to set policy related to printing.", + "MANAGE_DEVICE_POLICY_PRIVATE_DNS", "Allows an application to set policy related to private DNS.", + "MANAGE_DEVICE_POLICY_PROFILES", "Allows an application to set policy related to profiles.", + "MANAGE_DEVICE_POLICY_PROFILE_INTERACTION", "Allows an application to set policy related to interacting with profiles (e.g. Disallowing cross-profile copy and paste).", + "MANAGE_DEVICE_POLICY_PROXY", "Allows an application to set a network-independent global HTTP proxy.", + "MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES", "Allows an application query system updates.", + "MANAGE_DEVICE_POLICY_RESET_PASSWORD", "Allows an application to force set a new device unlock password or a managed profile challenge on current user.", + "MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS", "Allows an application to set policy related to restricting the user from configuring private DNS.", + "MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS", "Allows an application to set the grant state of runtime permissions on packages.", + "MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND", "Allows an application to set policy related to users running in the background.", + "MANAGE_DEVICE_POLICY_SAFE_BOOT", "Allows an application to manage safe boot policy.", + "MANAGE_DEVICE_POLICY_SCREEN_CAPTURE", "Allows an application to set policy related to screen capture.", + "MANAGE_DEVICE_POLICY_SCREEN_CONTENT", "Allows an application to set policy related to the usage of the contents of the screen.", + "MANAGE_DEVICE_POLICY_SECURITY_LOGGING", "Allows an application to set policy related to security logging.", + "MANAGE_DEVICE_POLICY_SETTINGS", "Allows an application to set policy related to settings.", + "MANAGE_DEVICE_POLICY_SMS", "Allows an application to set policy related to sms.", + "MANAGE_DEVICE_POLICY_STATUS_BAR", "Allows an application to set policy related to the status bar.", + "MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE", "Allows an application to set support messages for when a user action is affected by an active policy.", + "MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS", "Allows an application to set policy related to suspending personal apps.", + "MANAGE_DEVICE_POLICY_SYSTEM_APPS", "Allows an application to manage policy related to system apps.", + "MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS", "Allows an application to set policy related to system dialogs.", + "MANAGE_DEVICE_POLICY_SYSTEM_UPDATES", "Allows an application to set policy related to system updates.", + "MANAGE_DEVICE_POLICY_TIME", "Allows an application to manage device policy relating to time.", + "MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING", "Allows an application to set policy related to usb data signalling.", + "MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER", "Allows an application to set policy related to usb file transfers.", + "MANAGE_DEVICE_POLICY_USERS", "Allows an application to set policy related to users.", + "MANAGE_DEVICE_POLICY_VPN", "Allows an application to set policy related to VPNs.", + "MANAGE_DEVICE_POLICY_WALLPAPER", "Allows an application to set policy related to the wallpaper.", + "MANAGE_DEVICE_POLICY_WIFI", "Allows an application to set policy related to Wifi.", + "MANAGE_DEVICE_POLICY_WINDOWS", "Allows an application to set policy related to windows.", + "MANAGE_DEVICE_POLICY_WIPE_DATA", "Allows an application to manage policy related to wiping data.", + "MANAGE_DOCUMENTS", "Allows an application to manage access to documents, usually as part of a document picker.", + "MANAGE_EXTERNAL_STORAGE", "Allows an application a broad access to external storage in scoped storage.", + "MANAGE_MEDIA", "Allows an application to modify and delete media files on this device or any connected storage device without user confirmation.", + "MANAGE_ONGOING_CALLS", "Allows to query ongoing call details and manage ongoing calls", + "MANAGE_OWN_CALLS", "Allows a calling application which manages its own calls through the self-managed ConnectionService APIs.", + "MANAGE_WIFI_INTERFACES", "Allows applications to get notified when a Wi-Fi interface request cannot be satisfied without tearing down one or more other interfaces, and provide a decision whether to approve the request or reject it.", + "MANAGE_WIFI_NETWORK_SELECTION", "This permission is used to let OEMs grant their trusted app access to a subset of privileged wifi APIs to improve wifi performance.", + "MASTER_CLEAR", "Not for use by third-party applications.", + "MEDIA_CONTENT_CONTROL", "Allows an application to know what content is playing and control its playback.", + "MEDIA_ROUTING_CONTROL", "Allows an application to control the routing of media apps.", + "MODIFY_AUDIO_SETTINGS", "Allows an application to modify global audio settings.", + "MODIFY_PHONE_STATE", "Allows modification of the telephony state - power on, mmi, etc.", + "MOUNT_FORMAT_FILESYSTEMS", "Allows formatting file systems for removable storage.", + "MOUNT_UNMOUNT_FILESYSTEMS", "Allows mounting and unmounting file systems for removable storage.", + "NEARBY_WIFI_DEVICES", "Required to be able to advertise and connect to nearby devices via Wi-Fi.", + "NFC", "Allows applications to perform I/O operations over NFC.", + "NFC_PREFERRED_PAYMENT_INFO", "Allows applications to receive NFC preferred payment service information.", + "NFC_TRANSACTION_EVENT", "Allows applications to receive NFC transaction events.", + "OVERRIDE_WIFI_CONFIG", "Allows an application to modify any wifi configuration, even if created by another application.", + "PACKAGE_USAGE_STATS", "Allows an application to collect component usage statistics", + "POST_NOTIFICATIONS", "Allows an app to post notifications", + "PROVIDE_OWN_AUTOFILL_SUGGESTIONS", "Allows an application to display its suggestions using the autofill framework.", + "PROVIDE_REMOTE_CREDENTIALS", "Allows an application to be able to store and retrieve credentials from a remote device.", + "QUERY_ALL_PACKAGES", "Allows query of any normal app on the device, regardless of manifest declarations.", + "READ_ASSISTANT_APP_SEARCH_DATA", "Allows an application to query over global data in AppSearch that's visible to the ASSISTANT role.", + "READ_BASIC_PHONE_STATE", "Allows read only access to phone state with a non dangerous permission, including the information like cellular network type, software version.", + "READ_CALENDAR", "Allows an application to read the user's calendar data.", + "READ_CALL_LOG", "Allows an application to read the user's call log.", + "READ_CONTACTS", "Allows an application to read the user's contacts data.", + "READ_DROPBOX_DATA", "Allows an application to access the data in Dropbox.", + "READ_EXTERNAL_STORAGE", "Allows an application to read from external storage.", + "READ_HOME_APP_SEARCH_DATA", "Allows an application to query over global data in AppSearch that's visible to the HOME role.", + "READ_LOGS", "Allows an application to read the low-level system log files.", + "READ_MEDIA_AUDIO", "Allows an application to read audio files from external storage.", + "READ_MEDIA_IMAGES", "Allows an application to read image files from external storage.", + "READ_MEDIA_VIDEO", "Allows an application to read video files from external storage.", + "READ_MEDIA_VISUAL_USER_SELECTED", "Allows an application to read image or video files from external storage that a user has selected via the permission prompt photo picker.", + "READ_NEARBY_STREAMING_POLICY", "Allows an application to read nearby streaming policy.", + "READ_PHONE_NUMBERS", "Allows read access to the device's phone number(s).", + "READ_PHONE_STATE", "Allows read only access to phone state, including the current cellular network information, the status of any ongoing calls, and a list of any PhoneAccount s registered on the device.", + "READ_PRECISE_PHONE_STATE", "Allows read only access to precise phone state.", + "READ_SMS", "Allows an application to read SMS messages.", + "READ_SYNC_SETTINGS", "Allows applications to read the sync settings.", + "READ_SYNC_STATS", "Allows applications to read the sync stats.", + "READ_VOICEMAIL", "Allows an application to read voicemails in the system.", + "REBOOT", "Required to be able to reboot the device.", + "RECEIVE_BOOT_COMPLETED", "Allows an application to receive the Intent.ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.", + "RECEIVE_MMS", "Allows an application to monitor incoming MMS messages.", + "RECEIVE_SMS", "Allows an application to receive SMS messages.", + "RECEIVE_WAP_PUSH", "Allows an application to receive WAP push messages.", + "RECORD_AUDIO", "Allows an application to record audio.", + "REORDER_TASKS", "Allows an application to change the Z-order of tasks.", + "REQUEST_COMPANION_PROFILE_APP_STREAMING", "Allows application to request to be associated with a virtual display capable of streaming Android applications ( AssociationRequest.DEVICE_PROFILE_APP_STREAMING ) by CompanionDeviceManager .", + "REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION", "Allows application to request to be associated with a vehicle head unit capable of automotive projection ( AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ) by CompanionDeviceManager .", + "REQUEST_COMPANION_PROFILE_COMPUTER", "Allows application to request to be associated with a computer to share functionality and/or data with other devices, such as notifications, photos and media ( AssociationRequest.DEVICE_PROFILE_COMPUTER ) by CompanionDeviceManager .", + "REQUEST_COMPANION_PROFILE_GLASSES", "Allows app to request to be associated with a device via CompanionDeviceManager as \"glasses\"", + "REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING", "Allows application to request to stream content from an Android host to a nearby device ( AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING ) by CompanionDeviceManager .", + "REQUEST_COMPANION_PROFILE_WATCH", "Allows app to request to be associated with a device via CompanionDeviceManager as a \"watch\"", + "REQUEST_COMPANION_RUN_IN_BACKGROUND", "Allows a companion app to run in the background.", + "REQUEST_COMPANION_SELF_MANAGED", "Allows an application to create a \"self-managed\" association.", + "REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND", "Allows a companion app to start a foreground service from the background.", + "REQUEST_COMPANION_USE_DATA_IN_BACKGROUND", "Allows a companion app to use data in the background.", + "REQUEST_DELETE_PACKAGES", "Allows an application to request deleting packages.", + "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", "Permission an application must hold in order to use Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS .", + "REQUEST_INSTALL_PACKAGES", "Allows an application to request installing packages.", + "REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE", "Allows an application to subscribe to notifications about the presence status change of their associated companion device", + "REQUEST_PASSWORD_COMPLEXITY", "Allows an application to request the screen lock complexity and prompt users to update the screen lock to a certain complexity level.", + "RUN_USER_INITIATED_JOBS", "Allows applications to use the user-initiated jobs API.", + "SCHEDULE_EXACT_ALARM", "Allows applications to use exact alarm APIs.", + "SEND_RESPOND_VIA_MESSAGE", "Allows an application (Phone) to send a request to other applications to handle the respond-via-message action during incoming calls.", + "SEND_SMS", "Allows an application to send SMS messages.", + "SET_ALARM", "Allows an application to broadcast an Intent to set an alarm for the user.", + "SET_ALWAYS_FINISH", "Allows an application to control whether activities are immediately finished when put in the background.", + "SET_ANIMATION_SCALE", "Modify the global animation scaling factor.", + "SET_BIOMETRIC_DIALOG_ADVANCED", "Allows an application to set the advanced features on BiometricDialog (SystemUI), including logo, logo description, and content view with more options button.", + "SET_DEBUG_APP", "Configure an application for debugging.", + "SET_PROCESS_LIMIT", "Allows an application to set the maximum number of (not needed) application processes that can be running.", + "SET_TIME", "Allows applications to set the system time directly.", + "SET_TIME_ZONE", "Allows applications to set the system time zone directly.", + "SET_WALLPAPER", "Allows applications to set the wallpaper.", + "SET_WALLPAPER_HINTS", "Allows applications to set the wallpaper hints.", + "SIGNAL_PERSISTENT_PROCESSES", "Allow an application to request that a signal be sent to all persistent processes.", + "START_FOREGROUND_SERVICES_FROM_BACKGROUND", "Allows an application to start foreground services from the background at any time.", + "START_VIEW_APP_FEATURES", "Allows the holder to start the screen with a list of app features.", + "START_VIEW_PERMISSION_USAGE", "Allows the holder to start the permission usage screen for an app.", + "STATUS_BAR", "Allows an application to open, close, or disable the status bar and its icons.", + "SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE", "Allows an application to subscribe to keyguard locked (i.e., showing) state.", + "SYSTEM_ALERT_WINDOW", "Allows an app to create windows using the type WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY , shown on top of all other apps.", + "TRANSMIT_IR", "Allows using the device's IR transmitter, if available.", + "TURN_SCREEN_ON", "Allows an app to turn on the screen on, e.g. with PowerManager.ACQUIRE_CAUSES_WAKEUP .", + "UPDATE_DEVICE_STATS", "Allows an application to update device statistics.", + "UPDATE_PACKAGES_WITHOUT_USER_ACTION", "Allows an application to indicate via PackageInstaller.SessionParams.setRequireUserAction(int) that user action should not be required for an app update.", + "USE_BIOMETRIC", "Allows an app to use device supported biometric modalities.", + "USE_EXACT_ALARM", "Allows apps to use exact alarms just like with SCHEDULE_EXACT_ALARM but without needing to request this permission from the user.", + "USE_FULL_SCREEN_INTENT", "Required for apps targeting Build.VERSION_CODES.Q that want to use notification full screen intents .", + "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER", "Allows to read device identifiers and use ICC based authentication like EAP-AKA.", + "USE_SIP", "Allows an application to use SIP service.", + "UWB_RANGING", "Required to be able to range to devices using ultra-wideband.", + "VIBRATE", "Allows access to the vibrator.", + "WAKE_LOCK", "Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming.", + "WRITE_APN_SETTINGS", "Allows applications to write the apn settings and read sensitive fields of an existing apn settings like user and password.", + "WRITE_CALENDAR", "Allows an application to write the user's calendar data.", + "WRITE_CALL_LOG", "Allows an application to write and read the user's call log data.", + "WRITE_CONTACTS", "Allows an application to write the user's contacts data.", + "WRITE_EXTERNAL_STORAGE", "Allows an application to write to external storage.", + "WRITE_GSERVICES", "Allows an application to modify the Google service map.", + "WRITE_SECURE_SETTINGS", "Allows an application to read or write the secure system settings.", + "WRITE_SETTINGS", "Allows an application to read or write the system settings.", + "WRITE_SYNC_SETTINGS", "Allows applications to write the sync settings.", + "WRITE_VOICEMAIL", "Allows an application to modify and remove existing voicemails in the system." + }; + + // Dangerous permissions that need runtime approval: + // https://developer.android.com/guide/topics/permissions/overview#runtime + public static final String[] dangerous = { + "ACCEPT_HANDOVER", + "ACCESS_BACKGROUND_LOCATION", + "ACCESS_COARSE_LOCATION", + "ACCESS_FINE_LOCATION", + "ACCESS_MEDIA_LOCATION", + "ACTIVITY_RECOGNITION", + "ADD_VOICEMAIL", + "ANSWER_PHONE_CALLS", + "BLUETOOTH_ADVERTISE", + "BLUETOOTH_CONNECT", + "BLUETOOTH_SCAN", + "BODY_SENSORS", + "BODY_SENSORS_BACKGROUND", + "CALL_PHONE", + "CAMERA", + "GET_ACCOUNTS", + "NEARBY_WIFI_DEVICES", + "POST_NOTIFICATIONS", + "READ_CALENDAR", + "READ_CALL_LOG", + "READ_CONTACTS", + "READ_EXTERNAL_STORAGE", + "READ_MEDIA_AUDIO", + "READ_MEDIA_IMAGES", + "READ_MEDIA_VIDEO", + "READ_MEDIA_VISUAL_USER_SELECTED", + "READ_PHONE_NUMBERS", + "READ_PHONE_STATE", + "READ_SMS", + "RECEIVE_MMS", + "RECEIVE_SMS", + "RECEIVE_WAP_PUSH", + "RECORD_AUDIO", + "SEND_SMS", + "USE_SIP", + "UWB_RANGING", + "WRITE_CALENDAR", + "WRITE_CALL_LOG", + "WRITE_CONTACTS", + "WRITE_EXTERNAL_STORAGE" + }; + + static String[] title; + static String[] description; + static int count; + static { + count = listing.length / 2; + title = new String[count]; + description = new String[count]; + for (int i = 0; i < count; i++) { + title[i] = listing[i*2]; + description[i] = listing[i*2+1]; + } + } +} + + +// Code for this CheckBoxList class found on the net, though I've lost the +// link. If you run across the original version, please let me know so that +// the original author can be credited properly. It was from a snippet +// collection, but it seems to have been picked up so many places with others +// placing their copyright on it that I haven't been able to determine the +// original author. [fry 20100216] +@SuppressWarnings("serial") +class CheckBoxList extends JList { + protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); + int checkboxWidth; + + public CheckBoxList() { + setCellRenderer(new CellRenderer()); + + // get the width of a checkbox so we can figure out if the mouse is inside + checkboxWidth = new JCheckBox().getPreferredSize().width; + // add the amount for the inset + checkboxWidth += Permissions.BORDER_HORIZ; + + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (isEnabled()) { +// System.out.println("cbw = " + checkboxWidth); + int index = locationToIndex(e.getPoint()); +// descriptionLabel.setText(description[index]); + if (index != -1) { + JCheckBox checkbox = getModel().getElementAt(index); + //System.out.println("mouse event in list: " + e); +// System.out.println(checkbox.getSize() + " ... " + checkbox); +// if (e.getX() < checkbox.getSize().height) { + if (e.getX() < checkboxWidth) { + checkbox.setSelected(!checkbox.isSelected()); + repaint(); + } + } + } + } + }); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + + + protected class CellRenderer implements ListCellRenderer { + public Component getListCellRendererComponent(JList list, + JCheckBox checkbox, + int index, boolean isSelected, + boolean cellHasFocus) { + checkbox.setBackground(isSelected ? getSelectionBackground() : getBackground()); + checkbox.setForeground(isSelected ? getSelectionForeground() : getForeground()); + checkbox.setEnabled(list.isEnabled()); + checkbox.setFont(getFont()); + checkbox.setFocusPainted(false); + checkbox.setBorderPainted(true); + checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); + return checkbox; + } + } +} diff --git a/processing/mode/src/processing/mode/android/RedirectStreamHandler.java b/processing/mode/src/processing/mode/android/RedirectStreamHandler.java new file mode 100644 index 000000000..933669106 --- /dev/null +++ b/processing/mode/src/processing/mode/android/RedirectStreamHandler.java @@ -0,0 +1,34 @@ +package processing.mode.android; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; + + +public class RedirectStreamHandler extends Thread { + // Streams Redirection- from and to + private final InputStream input; + private final PrintWriter output; + + RedirectStreamHandler(PrintWriter output, InputStream input) { + this.input = input; + this.output = output; + start(); + } + + @Override + public void run() { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(input)); + String line; + while ((line = reader.readLine()) != null) { + // print to output line by line + output.println(line); + } + } catch (IOException ioException) { + System.out.println("I/O Redirection failure: "+ ioException.toString()); + } + } + } diff --git a/mode/src/processing/mode/android/SDKDownloader.java b/processing/mode/src/processing/mode/android/SDKDownloader.java similarity index 75% rename from mode/src/processing/mode/android/SDKDownloader.java rename to processing/mode/src/processing/mode/android/SDKDownloader.java index 98bd153f7..2f9312b81 100644 --- a/mode/src/processing/mode/android/SDKDownloader.java +++ b/processing/mode/src/processing/mode/android/SDKDownloader.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2014-17 The Processing Foundation + Copyright (c) 2014-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -63,11 +63,10 @@ public class SDKDownloader extends JDialog implements PropertyChangeListener { private static final String REPOSITORY_URL = "https://dl.google.com/android/repository/"; private static final String HAXM_URL = "https://dl.google.com/android/repository/extras/intel/"; - private static final String REPOSITORY_LIST = "repository2-1.xml"; - private static final String ADDON_LIST = "addon2-1.xml"; + private static final String REPOSITORY_LIST = "repository2-3.xml"; + private static final String ADDON_LIST = "addon2-3.xml"; - private static final String PROPERTY_CHANGE_EVENT_TOTAL = "total"; - private static final String PROPERTY_CHANGE_EVENT_DOWNLOADED = "downloaded"; + public static final boolean DOWNLOAD_EMU_WITH_SDK = false; private JProgressBar progressBar; private JLabel downloadedTextArea; @@ -82,10 +81,8 @@ public class SDKDownloader extends JDialog implements PropertyChangeListener { class SDKUrlHolder { public String platformVersion, buildToolsVersion; - public String platformToolsUrl, buildToolsUrl, platformUrl, toolsUrl, emulatorUrl; - public String platformToolsFilename, buildToolsFilename, platformFilename, toolsFilename, emulatorFilename; -// public String supportRepoUrl, googleRepoUrl; -// public String supportRepoFilename, googleRepoFilename; + public String platformToolsUrl, buildToolsUrl, platformUrl, cmdlineToolsUrl, emulatorUrl; + public String platformToolsFilename, buildToolsFilename, platformFilename, cmdlineToolsFilename, emulatorFilename; public String usbDriverUrl; public String usbDriverFilename; public String haxmFilename, haxmUrl; @@ -110,16 +107,17 @@ protected Object doInBackground() throws Exception { if (!platformsFolder.exists()) platformsFolder.mkdir(); File buildToolsFolder = new File(sdkFolder, "build-tools"); if (!buildToolsFolder.exists()) buildToolsFolder.mkdir(); - File emulatorFolder = new File(sdkFolder, "emulator"); - if (!emulatorFolder.exists()) emulatorFolder.mkdir(); File extrasFolder = new File(sdkFolder, "extras"); if (!extrasFolder.exists()) extrasFolder.mkdir(); File googleRepoFolder = new File(extrasFolder, "google"); - if (!googleRepoFolder.exists()) googleRepoFolder.mkdir(); -// File androidRepoFolder = new File(extrasFolder, "android"); -// if (!androidRepoFolder.exists()) androidRepoFolder.mkdir(); + if (!googleRepoFolder.exists()) googleRepoFolder.mkdir(); File haxmFolder = new File(extrasFolder, "intel/HAXM"); if (!haxmFolder.exists()) haxmFolder.mkdirs(); + + if (DOWNLOAD_EMU_WITH_SDK) { + File emulatorFolder = new File(sdkFolder, "emulator"); + if (!emulatorFolder.exists()) emulatorFolder.mkdir(); + } // creating temp folder for downloaded zip packages File tempFolder = new File(androidFolder, "temp"); @@ -130,51 +128,57 @@ protected Object doInBackground() throws Exception { String repositoryUrl = REPOSITORY_URL + REPOSITORY_LIST; String addonUrl = REPOSITORY_URL + ADDON_LIST; String haxmUrl = HAXM_URL + ADDON_LIST; - getMainDownloadUrls(downloadUrls, repositoryUrl, Platform.getName()); - getExtrasDownloadUrls(downloadUrls, addonUrl, Platform.getName()); - getHaxmDownloadUrl(downloadUrls, haxmUrl, Platform.getName()); - firePropertyChange(PROPERTY_CHANGE_EVENT_TOTAL, 0, downloadUrls.totalSize); - - // tools - File downloadedTools = new File(tempFolder, downloadUrls.toolsFilename); - downloadAndUnpack(downloadUrls.toolsUrl, downloadedTools, sdkFolder, true); - // platform-tools + String platformName = Platform.getName(); + System.out.println("PLATFORM NAME " + platformName); + if (platformName.equals("macos")) { + platformName = "macosx"; + } + getMainDownloadUrls(downloadUrls, repositoryUrl, platformName); + getExtrasDownloadUrls(downloadUrls, addonUrl, platformName); + getHaxmDownloadUrl(downloadUrls, haxmUrl, platformName); + firePropertyChange(AndroidMode.getTextString("download_property.change_event_total"), 0, downloadUrls.totalSize); + + // Command-line tools + File downloadedCmdLineTools = new File(tempFolder, downloadUrls.cmdlineToolsFilename); + downloadAndUnpack(downloadUrls.cmdlineToolsUrl, downloadedCmdLineTools, sdkFolder); + File tmpFrom = new File(sdkFolder, "cmdline-tools"); + File tmpTo = new File(sdkFolder, "cmdline-tmp"); + AndroidUtil.moveDir(tmpFrom, tmpTo); + File cmdlineToolsFolder = new File(sdkFolder, "cmdline-tools/latest"); + if (!cmdlineToolsFolder.exists()) cmdlineToolsFolder.mkdirs(); + AndroidUtil.moveDir(tmpTo, cmdlineToolsFolder); + + // Platform tools File downloadedPlatformTools = new File(tempFolder, downloadUrls.platformToolsFilename); - downloadAndUnpack(downloadUrls.platformToolsUrl, downloadedPlatformTools, sdkFolder, true); + downloadAndUnpack(downloadUrls.platformToolsUrl, downloadedPlatformTools, sdkFolder); - // build-tools + // Build tools File downloadedBuildTools = new File(tempFolder, downloadUrls.buildToolsFilename); - downloadAndUnpack(downloadUrls.buildToolsUrl, downloadedBuildTools, buildToolsFolder, true); + downloadAndUnpack(downloadUrls.buildToolsUrl, downloadedBuildTools, buildToolsFolder); - // platform + // Platform File downloadedPlatform = new File(tempFolder, downloadUrls.platformFilename); - downloadAndUnpack(downloadUrls.platformUrl, downloadedPlatform, platformsFolder, false); - - // emulator, unpacks directly to sdk folder - File downloadedEmulator = new File(tempFolder, downloadUrls.emulatorFilename); - downloadAndUnpack(downloadUrls.emulatorUrl, downloadedEmulator, sdkFolder, true); - - // google repository -// File downloadedGoogleRepo = new File(tempFolder, downloadUrls.googleRepoFilename); -// downloadAndUnpack(downloadUrls.googleRepoUrl, downloadedGoogleRepo, googleRepoFolder, false); - - // android repository -// File downloadedSupportRepo = new File(tempFolder, downloadUrls.supportRepoFilename); -// downloadAndUnpack(downloadUrls.supportRepoUrl, downloadedSupportRepo, androidRepoFolder, false); + downloadAndUnpack(downloadUrls.platformUrl, downloadedPlatform, platformsFolder); - // usb driver - if (Platform.isWindows()) { + // USB driver + if (Platform.isWindows() && downloadUrls.usbDriverFilename != null) { File downloadedFolder = new File(tempFolder, downloadUrls.usbDriverFilename); - downloadAndUnpack(downloadUrls.usbDriverUrl, downloadedFolder, googleRepoFolder, false); + downloadAndUnpack(downloadUrls.usbDriverUrl, downloadedFolder, googleRepoFolder); } // HAXM - if (!Platform.isLinux()) { + if (!Platform.isLinux() && downloadUrls.haxmFilename != null) { File downloadedFolder = new File(tempFolder, downloadUrls.haxmFilename); - downloadAndUnpack(downloadUrls.haxmUrl, downloadedFolder, haxmFolder, true); + downloadAndUnpack(downloadUrls.haxmUrl, downloadedFolder, haxmFolder); } + if (DOWNLOAD_EMU_WITH_SDK && downloadUrls.emulatorFilename != null) { + // Emulator, unpacks directly to sdk folder + File downloadedEmulator = new File(tempFolder, downloadUrls.emulatorFilename); + downloadAndUnpack(downloadUrls.emulatorUrl, downloadedEmulator, sdkFolder); + } + if (Platform.isLinux() || Platform.isMacOS()) { Runtime.getRuntime().exec("chmod -R 755 " + sdkFolder.getAbsolutePath()); } @@ -182,10 +186,11 @@ protected Object doInBackground() throws Exception { for (File f: tempFolder.listFiles()) f.delete(); tempFolder.delete(); - // Normalize built-tools and platform folders to android- - String actualName = platformsFolder.listFiles()[0].getName(); - renameFolder(platformsFolder, "android-" + AndroidBuild.TARGET_SDK, actualName); - actualName = buildToolsFolder.listFiles()[0].getName(); + +// String actualName = platformsFolder.listFiles()[0].getName(); +// renameFolder(platformsFolder, "android-" + AndroidBuild.TARGET_SDK, actualName); + // Rename build-tools folder to the expected name if it's not that already + String actualName = buildToolsFolder.listFiles()[0].getName(); renameFolder(buildToolsFolder, downloadUrls.buildToolsVersion, actualName); // Done, let's set the environment and load the new SDK! @@ -212,7 +217,7 @@ protected void done() { } private void downloadAndUnpack(String urlString, File saveTo, - File unpackTo, boolean setExec) throws IOException { + File unpackTo) throws IOException { URL url = null; try { url = new URL(urlString); @@ -237,18 +242,18 @@ private void downloadAndUnpack(String urlString, File saveTo, outputStream.write(b, 0, count); downloadedSize += count; - firePropertyChange(PROPERTY_CHANGE_EVENT_DOWNLOADED, 0, downloadedSize); + firePropertyChange(AndroidMode.getTextString("download_property.change_event_downloaded"), 0, downloadedSize); } outputStream.flush(); outputStream.close(); inputStream.close(); inputStream.close(); outputStream.close(); - AndroidUtil.extractFolder(saveTo, unpackTo, setExec); + AndroidUtil.extractFolder(saveTo, unpackTo); } private void getMainDownloadUrls(SDKUrlHolder urlHolder, - String repositoryUrl, String requiredHostOs) + String repositoryUrl, String requiredHostOs) throws ParserConfigurationException, IOException, SAXException, XPathException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); @@ -261,7 +266,7 @@ private void getMainDownloadUrls(SDKUrlHolder urlHolder, boolean found; // ----------------------------------------------------------------------- - // platform + // Platform expr = xpath.compile("//remotePackage[starts-with(@path, \"platforms;\")" + "and contains(@path, '" + AndroidBuild.TARGET_SDK + "')]"); // Skip latest platform; download only the targeted remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); @@ -283,42 +288,45 @@ private void getMainDownloadUrls(SDKUrlHolder urlHolder, urlHolder.platformUrl = REPOSITORY_URL + urlHolder.platformFilename; urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); } else { - throw new IOException("Cannot find the platform files"); + throw new IOException(AndroidMode.getTextString("sdk_downloader.error_cannot_find_platform_files")); } - // Difference between platform tools, build tools, and SDK tools: + // Difference between platform tools, build tools, and SDK (now command-line) tools: // http://stackoverflow.com/questions/19911762/what-is-android-sdk-build-tools-and-which-version-should-be-used // Always get the latest! // ----------------------------------------------------------------------- - // platform-tools + // Platform tools expr = xpath.compile("//remotePackage[@path=\"platform-tools\"]"); remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); if (remotePackages != null) { parseAndSet(urlHolder, remotePackages, requiredHostOs, PLATFORM_TOOLS); } else { - throw new IOException("Cannot find the platform-tools"); + throw new IOException(AndroidMode.getTextString("sdk_downloader.error_cannot_find_platform_tools")); } // ----------------------------------------------------------------------- - // build-tools + // Build tools expr = xpath.compile("//remotePackage[starts-with(@path, \"build-tools;\")]"); remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); found = false; if (remotePackages != null) { - for(int buildTool=0; buildTool < remotePackages.getLength(); buildTool++) { + for (int buildTool = 0; buildTool < remotePackages.getLength(); buildTool++) { NodeList childNodes = remotePackages.item(buildTool).getChildNodes(); NodeList channel = ((Element) childNodes).getElementsByTagName("channelRef"); - if(!channel.item(0).getAttributes().item(0).getNodeValue().equals("channel-0")) - continue; //Stable channel only, skip others + if (!channel.item(0).getAttributes().item(0).getNodeValue().equals("channel-0")) { + continue; // Stable channel only, skip others + } NodeList revision = ((Element) childNodes).getElementsByTagName("revision"); String major = (((Element) revision.item(0)).getElementsByTagName("major")).item(0).getTextContent(); String minor = (((Element) revision.item(0)).getElementsByTagName("minor")).item(0).getTextContent(); String micro = (((Element) revision.item(0)).getElementsByTagName("micro")).item(0).getTextContent(); - if(!major.equals(AndroidBuild.TARGET_SDK)) // Allows only the latest build tools for the target platform - continue; + if (!major.equals(AndroidBuild.TARGET_SDK)) { + continue; // Allows only the latest build tools for the target platform + } + urlHolder.buildToolsVersion = major + "." + minor + "." + micro; NodeList archives = ((Element) childNodes).getElementsByTagName("archive"); @@ -343,56 +351,26 @@ private void getMainDownloadUrls(SDKUrlHolder urlHolder, } } if (!found) { - throw new IOException("Cannot find the build-tools"); + throw new IOException(AndroidMode.getTextString("sdk_downloader.error_cannot_find_build_tools")); } // ----------------------------------------------------------------------- - // tools - expr = xpath.compile("//remotePackage[@path=\"tools\"]"); //Matches two items according to xml file - remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); - found = false; - if (remotePackages != null) { - NodeList childNodes = remotePackages.item(1).getChildNodes(); //Second item is the latest tools for now - NodeList archives = ((Element) childNodes).getElementsByTagName("archive"); - - for (int i = 0; i < archives.getLength(); ++i) { - NodeList archive = archives.item(i).getChildNodes(); - NodeList complete = ((Element) archive).getElementsByTagName("complete"); - - NodeList os = ((Element) archive).getElementsByTagName("host-os"); - NodeList url = ((Element) complete.item(0)).getElementsByTagName("url"); - NodeList size = ((Element) complete.item(0)).getElementsByTagName("size"); - - if (os.item(0).getTextContent().equals(requiredHostOs)) { - urlHolder.toolsFilename = url.item(0).getTextContent(); - urlHolder.toolsUrl = REPOSITORY_URL + urlHolder.toolsFilename; - urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); - found = true; - break; - } - } - } - if (!found) { - throw new IOException("Cannot find the tools"); - } - - // ----------------------------------------------------------------------- - // emulator - expr = xpath.compile("//remotePackage[@path=\"emulator\"]"); //Matches two items according to xml file + // Command-line tools + expr = xpath.compile("//remotePackage[starts-with(@path, \"cmdline-tools;\")]"); remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); found = false; if (remotePackages != null) { - for(int i = 0; i < remotePackages.getLength(); ++i) { - NodeList childNodes = remotePackages.item(i).getChildNodes(); + for (int tool = 0; tool < remotePackages.getLength(); tool++) { + NodeList childNodes = remotePackages.item(tool).getChildNodes(); NodeList channel = ((Element) childNodes).getElementsByTagName("channelRef"); - if(!channel.item(0).getAttributes().item(0).getNodeValue().equals("channel-0")) - continue; //Stable channel only, skip others - + if (!channel.item(0).getAttributes().item(0).getNodeValue().equals("channel-0")) { + continue; // Stable channel only, skip others + } + NodeList archives = ((Element) childNodes).getElementsByTagName("archive"); - - for (int j = 0; j < archives.getLength(); ++j) { - NodeList archive = archives.item(j).getChildNodes(); + for (int i = 0; i < archives.getLength(); ++i) { + NodeList archive = archives.item(i).getChildNodes(); NodeList complete = ((Element) archive).getElementsByTagName("complete"); NodeList os = ((Element) archive).getElementsByTagName("host-os"); @@ -400,24 +378,65 @@ private void getMainDownloadUrls(SDKUrlHolder urlHolder, NodeList size = ((Element) complete.item(0)).getElementsByTagName("size"); if (os.item(0).getTextContent().equals(requiredHostOs)) { - urlHolder.emulatorFilename = url.item(0).getTextContent(); - urlHolder.emulatorUrl = REPOSITORY_URL + urlHolder.emulatorFilename; + urlHolder.cmdlineToolsFilename = url.item(0).getTextContent(); + urlHolder.cmdlineToolsUrl = REPOSITORY_URL + urlHolder.cmdlineToolsFilename; urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); found = true; break; - } + } } - if (found) break; + if (found) break; } } if (!found) { - throw new IOException("Cannot find the emulator"); + throw new IOException(AndroidMode.getTextString("sdk_downloader.error_cannot_find_tools")); + } + + if (DOWNLOAD_EMU_WITH_SDK) { + // ----------------------------------------------------------------------- + // Emulator + expr = xpath.compile("//remotePackage[@path=\"emulator\"]"); + remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + found = false; + if (remotePackages != null) { + for (int i = 0; i < remotePackages.getLength(); ++i) { + NodeList childNodes = remotePackages.item(i).getChildNodes(); + + NodeList channel = ((Element) childNodes).getElementsByTagName("channelRef"); + if (!channel.item(0).getAttributes().item(0).getNodeValue().equals("channel-0")) { + continue; //Stable channel only, skip others + } + + NodeList archives = ((Element) childNodes).getElementsByTagName("archive"); + + for (int j = 0; j < archives.getLength(); ++j) { + NodeList archive = archives.item(j).getChildNodes(); + NodeList complete = ((Element) archive).getElementsByTagName("complete"); + + NodeList os = ((Element) archive).getElementsByTagName("host-os"); + NodeList url = ((Element) complete.item(0)).getElementsByTagName("url"); + NodeList size = ((Element) complete.item(0)).getElementsByTagName("size"); + + if (os.item(0).getTextContent().equals(requiredHostOs)) { + urlHolder.emulatorFilename = url.item(0).getTextContent(); + urlHolder.emulatorUrl = REPOSITORY_URL + urlHolder.emulatorFilename; + urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); + found = true; + break; + } + } + if (found) break; + } + } + } + if (!found) { + throw new IOException(AndroidMode.getTextString("sdk_downloader.error_cannot_find_emulator")); } } } private void getExtrasDownloadUrls(SDKUrlHolder urlHolder, - String repositoryUrl, String requiredHostOs) + String repositoryUrl, String requiredHostOs) throws ParserConfigurationException, IOException, SAXException, XPathException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); @@ -471,15 +490,16 @@ private void getHaxmDownloadUrl(SDKUrlHolder urlHolder, expr = xpath.compile("//remotePackage[@path=\"extras;intel;Hardware_Accelerated_Execution_Manager\"]"); remotePackages = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); if (remotePackages != null) { - for (int i=0; i < remotePackages.getLength(); ++i) { + for (int i = 0; i < remotePackages.getLength(); ++i) { NodeList childNodes = remotePackages.item(i).getChildNodes(); NodeList archives = ((Element) childNodes).getElementsByTagName("archive"); NodeList archive = archives.item(0).getChildNodes(); NodeList os = ((Element) archive).getElementsByTagName("host-os"); - if (!os.item(0).getTextContent().equals(requiredHostOs)) + if (!os.item(0).getTextContent().equals(requiredHostOs)) { continue; + } NodeList complete = ((Element) archive).getElementsByTagName("complete"); NodeList url = ((Element) complete.item(0)).getElementsByTagName("url"); @@ -507,22 +527,13 @@ private void parseAndSet(SDKUrlHolder urlHolder, NodeList remotePackages, String switch (packageN) { case PLATFORM_TOOLS: NodeList os = ((Element) archive).getElementsByTagName("host-os"); - if (!os.item(0).getTextContent().equals(requiredHostOs)) + if (!os.item(0).getTextContent().equals(requiredHostOs)) { continue; + } urlHolder.platformToolsFilename = url.item(0).getTextContent(); urlHolder.platformToolsUrl = REPOSITORY_URL + urlHolder.platformToolsFilename; urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); break; -// case ANDROID_REPO: -// urlHolder.supportRepoFilename = url.item(0).getTextContent(); -// urlHolder.supportRepoUrl = REPOSITORY_URL + urlHolder.supportRepoFilename; -// urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); -// break; -// case GOOGLE_REPO: -// urlHolder.googleRepoFilename = url.item(0).getTextContent(); -// urlHolder.googleRepoUrl = REPOSITORY_URL + urlHolder.googleRepoFilename; -// urlHolder.totalSize += Integer.parseInt(size.item(0).getTextContent()); -// break; case USB_DRIVER: urlHolder.usbDriverFilename = url.item(0).getTextContent(); urlHolder.usbDriverUrl = REPOSITORY_URL + urlHolder.usbDriverFilename; @@ -541,19 +552,18 @@ private void renameFolder(File baseFolder, String expected, String actual) if (actualPath.exists()) { actualPath.renameTo(expectedPath); } else { - throw new IOException("Error unpacking platform to " + - actualPath.getAbsolutePath()); + throw new IOException(AndroidMode.getTextString("sdk_downloader.error.cannot_unpack_platform", actualPath.getAbsolutePath())); } } } @Override public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(PROPERTY_CHANGE_EVENT_TOTAL)) { + if (evt.getPropertyName().equals(AndroidMode.getTextString("download_property.change_event_total"))) { progressBar.setIndeterminate(false); totalSize = (Integer) evt.getNewValue(); progressBar.setMaximum(totalSize); - } else if (evt.getPropertyName().equals(PROPERTY_CHANGE_EVENT_DOWNLOADED)) { + } else if (evt.getPropertyName().equals(AndroidMode.getTextString("download_property.change_event_downloaded"))) { downloadedTextArea.setText(humanReadableByteCount((Integer) evt.getNewValue(), true) + " / " + humanReadableByteCount(totalSize, true)); progressBar.setValue((Integer) evt.getNewValue()); @@ -570,7 +580,7 @@ public static String humanReadableByteCount(long bytes, boolean si) { } public SDKDownloader(Frame editor) { - super(editor, "SDK download", true); + super(editor, AndroidMode.getTextString("sdk_downloader.download_title"), true); this.editor = editor; this.sdk = null; createLayout(); @@ -601,7 +611,7 @@ private void createLayout() { vbox.setBorder(new EmptyBorder(BOX_BORDER, BOX_BORDER, BOX_BORDER, BOX_BORDER)); outer.add(vbox); - String labelText = "Downloading Android SDK..."; + String labelText = AndroidMode.getTextString("sdk_downloader.download_sdk_label"); JLabel textarea = new JLabel(labelText); textarea.setAlignmentX(LEFT_ALIGNMENT); vbox.add(textarea); @@ -642,7 +652,7 @@ private void createLayout() { // Box buttons = Box.createHorizontalBox(); buttons.setAlignmentX(LEFT_ALIGNMENT); - JButton cancelButton = new JButton("Cancel download"); + JButton cancelButton = new JButton(AndroidMode.getTextString("download_prompt.cancel")); Dimension dim = new Dimension(Toolkit.getButtonWidth()*2, Toolkit.zoom(cancelButton.getPreferredSize().height)); diff --git a/mode/src/processing/mode/android/SysImageDownloader.java b/processing/mode/src/processing/mode/android/SysImageDownloader.java similarity index 83% rename from mode/src/processing/mode/android/SysImageDownloader.java rename to processing/mode/src/processing/mode/android/SysImageDownloader.java index b21632c1b..d665991f4 100644 --- a/mode/src/processing/mode/android/SysImageDownloader.java +++ b/processing/mode/src/processing/mode/android/SysImageDownloader.java @@ -2,18 +2,18 @@ /* Part of the Processing project - http://processing.org - - Copyright (c) 2016 The Processing Foundation + + Copyright (c) 2016-21 The Processing Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. - + This program 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 General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -58,51 +58,19 @@ public class SysImageDownloader extends JDialog implements PropertyChangeListene final static private int TEXT_MARGIN = Toolkit.zoom(8); final static private int TEXT_WIDTH = Toolkit.zoom(300); - private static final String EMULATOR_GUIDE_URL = - "https://developer.android.com/studio/run/emulator-acceleration.html"; - - private static final String SYS_IMAGE_SELECTION_MESSAGE = - "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."; - - private static final String HAXM_INSTALL_TITLE = "Some words of caution..."; - - private static final String HAXM_INSTALL_MESSAGE = - "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."; - - private static final String KVM_LINUX_GUIDE_URL = - "https://developer.android.com/studio/run/emulator-acceleration.html#vm-linux"; - - private static final String KVM_INSTALL_MESSAGE = - "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."; - - private static final String IA32LIBS_TITLE = "Additional setup may be required..."; - private static final String IA32LIBS_MESSAGE = - "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"; - private static final String SYS_IMAGES_ARM_URL = "https://dl.google.com/android/repository/sys-img/android/"; private static final String SYS_IMAGES_PHONE_URL = "https://dl.google.com/android/repository/sys-img/google_apis/"; - private static final String SYS_IMAGES_PHONE_LIST = "sys-img2-1.xml"; + private static final String SYS_IMAGES_PHONE_LIST = "sys-img2-3.xml"; private static final String SYS_IMAGES_WEAR_URL = "https://dl.google.com/android/repository/sys-img/android-wear/"; - private static final String SYS_IMAGES_WEAR_LIST = "sys-img2-1.xml"; + private static final String SYS_IMAGES_WEAR_LIST = "sys-img2-3.xml"; + + private static final String EMULATOR_GUIDE_URL = + "https://developer.android.com/studio/run/emulator-acceleration.html"; - private static final String PROPERTY_CHANGE_EVENT_TOTAL = "total"; - private static final String PROPERTY_CHANGE_EVENT_DOWNLOADED = "downloaded"; + private static final String KVM_LINUX_GUIDE_URL = + "https://developer.android.com/studio/run/emulator-acceleration.html#vm-linux"; private JProgressBar progressBar; private JLabel downloadedTextArea; @@ -168,29 +136,28 @@ protected Object doInBackground() throws Exception { UrlHolder downloadUrls = new UrlHolder(); getDownloadUrls(downloadUrls, repo, Platform.getName()); - firePropertyChange(PROPERTY_CHANGE_EVENT_TOTAL, 0, downloadUrls.totalSize); + firePropertyChange(AndroidMode.getTextString("download_property.change_event_total"), 0, downloadUrls.totalSize); totalSize = downloadUrls.totalSize; + String level = AVD.getTargetSDK(wear, abi); + if (wear) { // wear system images File downloadedSysImgWear = new File(tempFolder, downloadUrls.sysImgWearFilename); - File tmp = new File(sysImgFolder, "android-" + AndroidBuild.TARGET_SDK); + File tmp = new File(sysImgFolder, "android-" + level); if (!tmp.exists()) tmp.mkdir(); File sysImgWearFinalFolder = new File(tmp, downloadUrls.sysImgWearTag); if (!sysImgWearFinalFolder.exists()) sysImgWearFinalFolder.mkdir(); - downloadAndUnpack(downloadUrls.sysImgWearUrl, downloadedSysImgWear, sysImgWearFinalFolder, false); + downloadAndUnpack(downloadUrls.sysImgWearUrl, downloadedSysImgWear, sysImgWearFinalFolder); fixSourceProperties(sysImgWearFinalFolder); } else { // mobile system images - File downloadedSysImg = new File(tempFolder, downloadUrls.sysImgFilename); - - String level = abi.equals("arm") ? AVD.TARGET_SDK_ARM : AndroidBuild.TARGET_SDK; - File tmp = new File(sysImgFolder, "android-" + level); - + File downloadedSysImg = new File(tempFolder, downloadUrls.sysImgFilename); + File tmp = new File(sysImgFolder, "android-" + level); if (!tmp.exists()) tmp.mkdir(); File sysImgFinalFolder = new File(tmp, downloadUrls.sysImgTag); if (!sysImgFinalFolder.exists()) sysImgFinalFolder.mkdir(); - downloadAndUnpack(downloadUrls.sysImgUrl, downloadedSysImg, sysImgFinalFolder, false); + downloadAndUnpack(downloadUrls.sysImgUrl, downloadedSysImg, sysImgFinalFolder); fixSourceProperties(sysImgFinalFolder); } @@ -202,7 +169,7 @@ protected Object doInBackground() throws Exception { tempFolder.delete(); if (Platform.isLinux() && Platform.getVariant().equals("64")) { - AndroidUtil.showMessage(IA32LIBS_TITLE, IA32LIBS_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("sys_image_downloader.dialog.ia32libs_title"), AndroidMode.getTextString("sys_image_downloader.dialog.ia32libs_body")); } result = true; @@ -226,7 +193,7 @@ protected void done() { } private void downloadAndUnpack(String urlString, File saveTo, - File unpackTo, boolean setExec) throws IOException { + File unpackTo) throws IOException { URL url = null; try { url = new URL(urlString); @@ -244,15 +211,14 @@ private void downloadAndUnpack(String urlString, File saveTo, while ((count = inputStream.read(b)) >= 0) { outputStream.write(b, 0, count); downloadedSize += count; - - firePropertyChange(PROPERTY_CHANGE_EVENT_DOWNLOADED, 0, downloadedSize); + firePropertyChange(AndroidMode.getTextString("download_property.change_event_downloaded"), 0, downloadedSize); } outputStream.flush(); outputStream.close(); inputStream.close(); inputStream.close(); outputStream.close(); - AndroidUtil.extractFolder(saveTo, unpackTo, setExec); + AndroidUtil.extractFolder(saveTo, unpackTo); } // For some reason the source.properties file includes Addon entries, @@ -286,18 +252,23 @@ private void getDownloadUrls(UrlHolder urlHolder, XPathExpression expr; NodeList remotePackages; - if (abi.equals("arm")) - expr = xpath.compile("//remotePackage[contains(@path, '" + AVD.TARGET_SDK_ARM + "')" + - "and contains(@path, \"armeabi-v7a\")]"); - else - expr = xpath.compile("//remotePackage[contains(@path, '" + AndroidBuild.TARGET_SDK + "')" + - "and contains(@path, \"x86\")]"); + String targetSDK = AVD.getTargetSDK(wear, abi); + if (abi.equals("arm")) { + expr = xpath.compile("//remotePackage[contains(@path, '" + targetSDK + "')" + + "and contains(@path, \"armeabi-v7a\")]"); + } if (abi.equals("arm64-v8a")) { + expr = xpath.compile("//remotePackage[contains(@path, '" + targetSDK + "')" + + "and contains(@path, \"arm64-v8a\")]"); + } else { + expr = xpath.compile("//remotePackage[contains(@path, '" + targetSDK + "')" + + "and contains(@path, \"x86\")]"); + } if (wear) { Document docSysImgWear = db.parse(new URL(repositoryUrl).openStream()); + remotePackages = (NodeList) expr.evaluate(docSysImgWear, XPathConstants.NODESET); NodeList childNodes = remotePackages.item(0).getChildNodes(); - NodeList typeDetails = ((Element) childNodes).getElementsByTagName("type-details"); NodeList tag = ((Element) typeDetails.item(0)).getElementsByTagName("tag"); NodeList id = ((Element) tag.item(0)).getElementsByTagName("id"); @@ -344,11 +315,11 @@ private void getDownloadUrls(UrlHolder urlHolder, @Override public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(PROPERTY_CHANGE_EVENT_TOTAL)) { + if (evt.getPropertyName().equals(AndroidMode.getTextString("download_property.change_event_total"))) { progressBar.setIndeterminate(false); totalSize = (Integer) evt.getNewValue(); progressBar.setMaximum(totalSize); - } else if (evt.getPropertyName().equals(PROPERTY_CHANGE_EVENT_DOWNLOADED)) { + } else if (evt.getPropertyName().equals(AndroidMode.getTextString("download_property.change_event_downloaded"))) { downloadedTextArea.setText(humanReadableByteCount((Integer) evt.getNewValue(), true) + " / " + humanReadableByteCount(totalSize, true)); progressBar.setValue((Integer) evt.getNewValue()); @@ -371,8 +342,8 @@ static public int showSysImageMessage() { "margin: " + TEXT_MARGIN + "px; " + "width: " + TEXT_WIDTH + "px }" + " "; - htmlString += "

    " + SYS_IMAGE_SELECTION_MESSAGE + "

    "; - String title = "Choose system image type to download..."; + htmlString += "

    " + AndroidMode.getTextString("sys_image_downloader.dialog.select_image_body", EMULATOR_GUIDE_URL) + "

    "; + String title = AndroidMode.getTextString("sys_image_downloader.dialog.select_image_title"); JEditorPane pane = new JEditorPane("text/html", htmlString); pane.addHyperlinkListener(new HyperlinkListener() { @Override @@ -387,7 +358,8 @@ public void hyperlinkUpdate(HyperlinkEvent e) { pane.setBackground(label.getBackground()); String[] options = new String[] { - "Use x86 image", "Use ARM image" + AndroidMode.getTextString("sys_image_downloader.option.x86_image"), + AndroidMode.getTextString("sys_image_downloader.option.arm_image") }; int result = JOptionPane.showOptionDialog(null, pane, title, JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, @@ -402,7 +374,7 @@ public void hyperlinkUpdate(HyperlinkEvent e) { } public SysImageDownloader(Frame editor, boolean wear, boolean ask) { - super(editor, "System image download", true); + super(editor, AndroidMode.getTextString("sys_image_downloader.download_title"), true); this.editor = editor; this.wear = wear; this.askABI = ask; @@ -418,7 +390,8 @@ public void run() { // Either there was no image architecture selected, or the default was set. // In this case, we give the user the option to choose between ARM and x86 - final int result; + int result = 0; + boolean arm64 = false; // PROCESSOR_IDENTIFIER is only defined on Windows. For cross-platform CPU // info, in the future we could use OSHI: https://github.com/oshi/oshi String procId = System.getenv("PROCESSOR_IDENTIFIER"); @@ -431,14 +404,22 @@ public void run() { result = JOptionPane.NO_OPTION; } } else if (Platform.isMacOS()) { - // Macs only have Intel CPUs, so we also go for the x86 abi - result = JOptionPane.YES_OPTION; + if (Platform.getNativeArch().equals("aarch64")) { + // Apple Silicon Mac, so we go for the arm64 abi + arm64 = true; + result = JOptionPane.NO_OPTION; + } else { + // Intel Mac, so we go for the x86 abi + result = JOptionPane.YES_OPTION; + } } else { result = showSysImageMessage(); } if (result == JOptionPane.YES_OPTION || result == JOptionPane.CLOSED_OPTION) { abi = "x86"; installHAXM(); + } else if (arm64) { + abi = "arm64-v8a"; } else { abi = "arm"; } @@ -463,9 +444,11 @@ public boolean getResult() { static public void installHAXM() { File haxmFolder = AndroidSDK.getHAXMInstallerFolder(); if (Platform.isLinux()) { - AndroidUtil.showMessage(HAXM_INSTALL_TITLE, KVM_INSTALL_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("sys_image_downloader.dialog.accel_images_title"), + AndroidMode.getTextString("sys_image_downloader.dialog.kvm_config_body", KVM_LINUX_GUIDE_URL)); } else if (haxmFolder.exists()) { - AndroidUtil.showMessage(HAXM_INSTALL_TITLE, HAXM_INSTALL_MESSAGE); + AndroidUtil.showMessage(AndroidMode.getTextString("sys_image_downloader.dialog.accel_images_title"), + AndroidMode.getTextString("sys_image_downloader.dialog.haxm_install_body")); ProcessBuilder pb; if (Platform.isWindows()) { @@ -498,6 +481,9 @@ public void processLine(String line) { process.waitFor(); } catch (final InterruptedException ie) { ie.printStackTrace(); + System.out.println("Processing was not able to install HAXM automatically, " + + "but the installation package was downloaded into android/sdk/extras/intel/HAXM. " + + "You can try install to install it manually from there."); } finally { process.destroy(); } @@ -513,8 +499,8 @@ private void createLayout() { pain.setBorder(new EmptyBorder(13, 13, 13, 13)); outer.add(pain); - String labelText = wear ? "Downloading watch system image..." : - "Downloading phone system image..."; + String labelText = wear ? AndroidMode.getTextString("sys_image_downloader.download_watch_label") : + AndroidMode.getTextString("sys_image_downloader.download_phone_label"); JLabel textarea = new JLabel(labelText); textarea.setAlignmentX(LEFT_ALIGNMENT); pain.add(textarea); @@ -547,7 +533,7 @@ private void createLayout() { // Box buttons = Box.createHorizontalBox(); buttons.setAlignmentX(LEFT_ALIGNMENT); - JButton cancelButton = new JButton("Cancel download"); + JButton cancelButton = new JButton(AndroidMode.getTextString("download_prompt.cancel")); Dimension dim = new Dimension(Toolkit.getButtonWidth()*2, cancelButton.getPreferredSize().height); @@ -582,4 +568,4 @@ public void actionPerformed(ActionEvent actionEvent) { setResizable(false); setLocationRelativeTo(editor); } -} \ No newline at end of file +} diff --git a/processing/mode/templates/ARActivity.java.tmpl b/processing/mode/templates/ARActivity.java.tmpl new file mode 100644 index 000000000..ca441712f --- /dev/null +++ b/processing/mode/templates/ARActivity.java.tmpl @@ -0,0 +1,107 @@ +package @@package_name@@; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.content.Intent; +import android.provider.Settings; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import processing.android.PFragment; +import processing.android.CompatUtils; +import processing.core.PApplet; + +public class MainActivity extends AppCompatActivity { + private static final int CAMERA_PERMISSION_CODE = 0; + private static boolean CAMERA_PERMISSION_REQUESTED = false; + private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; + private static final String CAMERA_PERMISSION_MESSAGE = "Camera permission is needed to use AR"; + + private PApplet sketch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FrameLayout frame = new FrameLayout(this); + frame.setId(CompatUtils.getUniqueViewId()); + setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + sketch = new @@sketch_class_name@@(); + @@external@@ + PFragment fragment = new PFragment(sketch); + fragment.setView(frame, this); + } + + @Override + protected void onResume() { + super.onResume(); + if (!hasCameraPermission()) requestCameraPermission(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + if (!hasCameraPermission()) { + Toast.makeText(this, CAMERA_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); + if (!shouldShowRequestPermissionRationale()) { + launchPermissionSettings(); + } + finish(); + } + if (sketch != null) { + sketch.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + CAMERA_PERMISSION_REQUESTED = false; + } + + @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(); + } + } + + private boolean hasCameraPermission() { + int res = ContextCompat.checkSelfPermission(this, CAMERA_PERMISSION); + return res == PackageManager.PERMISSION_GRANTED; + } + + private void requestCameraPermission() { + if (!CAMERA_PERMISSION_REQUESTED) { + CAMERA_PERMISSION_REQUESTED = true; + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_CODE); + } + } + + private boolean shouldShowRequestPermissionRationale() { + return ActivityCompat.shouldShowRequestPermissionRationale(this, CAMERA_PERMISSION); + } + + private void launchPermissionSettings() { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", this.getPackageName(), null)); + this.startActivity(intent); + } +} diff --git a/processing/mode/templates/ARBuild.gradle.tmpl b/processing/mode/templates/ARBuild.gradle.tmpl new file mode 100644 index 000000000..9bd31923a --- /dev/null +++ b/processing/mode/templates/ARBuild.gradle.tmpl @@ -0,0 +1,54 @@ +apply plugin: 'com.android.application' + +android { + sourceSets { + main.jni.srcDirs = [] + main.jniLibs.srcDirs = ['libs'] + } + compileSdkVersion @@target_sdk@@ + defaultConfig { + applicationId "@@package_name@@" + minSdkVersion @@min_sdk@@ + targetSdkVersion @@target_sdk@@ + versionCode @@version_code@@ + versionName "@@version_name@@" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + signingConfigs { + release { + if (project.hasProperty('PROCESSING_UPLOAD_KEYSTORE_FILE')) { + storeFile file(PROCESSING_UPLOAD_KEYSTORE_FILE) + storePassword PROCESSING_UPLOAD_STORE_PASSWORD + keyAlias PROCESSING_UPLOAD_KEY_ALIAS + keyPassword PROCESSING_UPLOAD_KEY_PASSWORD + } + } + } + buildTypes { + debug { + debuggable true + } + release { + minifyEnabled false + signingConfig signingConfigs.release + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.appcompat:appcompat:@@appcompat_version@@' + implementation 'com.google.ar:core:@@gar_version@@' + implementation files('libs/processing-core.jar') + implementation files('libs/ar.jar') + androidTestImplementation 'com.android.support.test:runner:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + testImplementation 'junit:junit:4.13' +} diff --git a/processing/mode/templates/ARBuildECJ.gradle.tmpl b/processing/mode/templates/ARBuildECJ.gradle.tmpl new file mode 100644 index 000000000..45e72f569 --- /dev/null +++ b/processing/mode/templates/ARBuildECJ.gradle.tmpl @@ -0,0 +1,100 @@ +apply plugin: 'com.android.application' + +android { + sourceSets { + main.jni.srcDirs = [] + main.jniLibs.srcDirs = ['libs'] + } + compileSdkVersion @@target_sdk@@ + defaultConfig { + applicationId "@@package_name@@" + minSdkVersion @@min_sdk@@ + targetSdkVersion @@target_sdk@@ + versionCode @@version_code@@ + versionName "@@version_name@@" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + signingConfigs { + release { + if (project.hasProperty('PROCESSING_UPLOAD_KEYSTORE_FILE')) { + storeFile file(PROCESSING_UPLOAD_KEYSTORE_FILE) + storePassword PROCESSING_UPLOAD_STORE_PASSWORD + keyAlias PROCESSING_UPLOAD_KEY_ALIAS + keyPassword PROCESSING_UPLOAD_KEY_PASSWORD + } + } + } + buildTypes { + debug { + debuggable true + } + release { + minifyEnabled false + signingConfig signingConfigs.release + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + + // We create a variant of the compile task, where we use the Eclipse Compiler for Java + // (ECJ) instead of the JDK (which would require the user to download and install + // the Oracle JDK). Inspired by the following: + // https://github.com/bytedeco/javacpp/wiki/Gradle + // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html + applicationVariants.all { variant -> + variant.javaCompileProvider.get().doFirst { + // The main class that runs the Eclipse batch compiler + String ecjMain = 'org.eclipse.jdt.internal.compiler.batch.Main' + + // We construct the list of arguments needed by the batch compiler + // https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_batch_compiler.htm + List ecjArgs = ['-nowarn', + '-source', variant.javaCompileProvider.get().sourceCompatibility, + '-target', variant.javaCompileProvider.get().targetCompatibility, + '-d', variant.javaCompileProvider.get().destinationDir] as String[] + + // Set the debug attributes level according to the build target + if (variant.name == 'debug') { + // All debug info + ecjArgs += '-g' + } else { + // No debug info + ecjArgs += '-g:none' + } + + // Adding all the source files to the list of arguments + ecjArgs += variant.javaCompileProvider.get().source + + // Add the Android jar to the classpath inherited from the task + FileCollection ecjClasspath = files('@@target_platform@@/android.jar', + variant.javaCompileProvider.get().classpath) + + // Running the JavaExec task, which requires the main class to run, + // the classpath, and the list of arguments + // https://docs.gradle.org/3.5/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main + javaexec { + main ecjMain + classpath ecjClasspath + args ecjArgs + } + + // We skip the rest of the compileXxxJavaWithJavac task, since we + // source is already compiled with ecj + throw new StopExecutionException("skip javac") + } + } +} + +dependencies { + compileOnly files('@@mode_folder@@/org.eclipse.jdt.core.jar') + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:@@appcompat_version@@' + implementation 'com.google.ar:core:@@gar_version@@' + implementation files('libs/processing-core.jar') + implementation files('libs/ar.jar') +} diff --git a/processing/mode/templates/ARManifest.xml.tmpl b/processing/mode/templates/ARManifest.xml.tmpl new file mode 100644 index 000000000..1b31e497b --- /dev/null +++ b/processing/mode/templates/ARManifest.xml.tmpl @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/mode/templates/AppActivity.java.tmpl b/processing/mode/templates/AppActivity.java.tmpl similarity index 96% rename from mode/templates/AppActivity.java.tmpl rename to processing/mode/templates/AppActivity.java.tmpl index c89390485..3e24984b4 100644 --- a/mode/templates/AppActivity.java.tmpl +++ b/processing/mode/templates/AppActivity.java.tmpl @@ -4,7 +4,7 @@ import android.os.Bundle; import android.content.Intent; import android.view.ViewGroup; import android.widget.FrameLayout; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import processing.android.PFragment; import processing.android.CompatUtils; diff --git a/mode/templates/AppBuild.gradle.tmpl b/processing/mode/templates/AppBuild.gradle.tmpl similarity index 57% rename from mode/templates/AppBuild.gradle.tmpl rename to processing/mode/templates/AppBuild.gradle.tmpl index a0e27c50a..7774d0368 100644 --- a/mode/templates/AppBuild.gradle.tmpl +++ b/processing/mode/templates/AppBuild.gradle.tmpl @@ -1,6 +1,10 @@ apply plugin: 'com.android.application' android { + sourceSets { + main.jni.srcDirs = [] + main.jniLibs.srcDirs = ['libs'] + } compileSdkVersion @@target_sdk@@ defaultConfig { applicationId "@@package_name@@" @@ -15,28 +19,41 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + signingConfigs { + release { + if (project.hasProperty('PROCESSING_UPLOAD_KEYSTORE_FILE')) { + storeFile file(PROCESSING_UPLOAD_KEYSTORE_FILE) + storePassword PROCESSING_UPLOAD_STORE_PASSWORD + keyAlias PROCESSING_UPLOAD_KEY_ALIAS + keyPassword PROCESSING_UPLOAD_KEY_PASSWORD + } + } + } buildTypes { debug { debuggable true } release { minifyEnabled false + signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false - } + } + aaptOptions { + noCompress "tflite" + noCompress "lite" + } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:@@support_version@@' - implementation 'com.android.support:design:@@support_version@@' - implementation 'com.google.android.support:wearable:@@wear_version@@' - compileOnly 'com.google.android.wearable:wearable:@@wear_version@@' + implementation 'androidx.appcompat:appcompat:@@appcompat_version@@' implementation files('libs/processing-core.jar') - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + androidTestImplementation 'com.android.support.test:runner:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + testImplementation 'junit:junit:4.13' } + diff --git a/mode/templates/AppBuildECJ.gradle.tmpl b/processing/mode/templates/AppBuildECJ.gradle.tmpl similarity index 73% rename from mode/templates/AppBuildECJ.gradle.tmpl rename to processing/mode/templates/AppBuildECJ.gradle.tmpl index b1710cdd4..6e2d7f055 100644 --- a/mode/templates/AppBuildECJ.gradle.tmpl +++ b/processing/mode/templates/AppBuildECJ.gradle.tmpl @@ -1,6 +1,10 @@ apply plugin: 'com.android.application' android { + sourceSets { + main.jni.srcDirs = [] + main.jniLibs.srcDirs = ['libs'] + } compileSdkVersion @@target_sdk@@ defaultConfig { applicationId "@@package_name@@" @@ -15,18 +19,33 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + signingConfigs { + release { + if (project.hasProperty('PROCESSING_UPLOAD_KEYSTORE_FILE')) { + storeFile file(PROCESSING_UPLOAD_KEYSTORE_FILE) + storePassword PROCESSING_UPLOAD_STORE_PASSWORD + keyAlias PROCESSING_UPLOAD_KEY_ALIAS + keyPassword PROCESSING_UPLOAD_KEY_PASSWORD + } + } + } buildTypes { debug { debuggable true } release { minifyEnabled false + signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false - } + } + aaptOptions { + noCompress "tflite" + noCompress "lite" + } // We create a variant of the compile task, where we use the Eclipse Compiler for Java // (ECJ) instead of the JDK (which would require the user to download and install @@ -34,16 +53,16 @@ android { // https://github.com/bytedeco/javacpp/wiki/Gradle // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html applicationVariants.all { variant -> - variant.javaCompile.doFirst { + variant.javaCompileProvider.get().doFirst { // The main class that runs the Eclipse batch compiler String ecjMain = 'org.eclipse.jdt.internal.compiler.batch.Main' // We construct the list of arguments needed by the batch compiler // https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_batch_compiler.htm List ecjArgs = ['-nowarn', - '-source', variant.javaCompile.sourceCompatibility, - '-target', variant.javaCompile.targetCompatibility, - '-d', variant.javaCompile.destinationDir] as String[] + '-source', variant.javaCompileProvider.get().sourceCompatibility, + '-target', variant.javaCompileProvider.get().targetCompatibility, + '-d', variant.javaCompileProvider.get().destinationDir] as String[] // Set the debug attributes level according to the build target if (variant.name == 'debug') { @@ -55,15 +74,15 @@ android { } // Adding all the source files to the list of arguments - ecjArgs += variant.javaCompile.source + ecjArgs += variant.javaCompileProvider.get().source // Add the Android jar to the classpath inherited from the task FileCollection ecjClasspath = files('@@target_platform@@/android.jar', - variant.javaCompile.classpath) + variant.javaCompileProvider.get().classpath) // Running the JavaExec task, which requires the main class to run, // the classpath, and the list of arguments - // https://docs.gradle.org/4.4/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main + // https://docs.gradle.org/4.4/dsl/org.gradle.api.tasks.JavaExec.html#org.gradle.api.tasks.JavaExec:main javaexec { main ecjMain classpath ecjClasspath @@ -78,11 +97,8 @@ android { } dependencies { - compileOnly files('@@tools_folder@@/../modes/java/mode/org.eclipse.jdt.core.jar') + compileOnly files('@@mode_folder@@/org.eclipse.jdt.core.jar') implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:@@support_version@@' - implementation 'com.android.support:design:@@support_version@@' - implementation 'com.google.android.support:wearable:@@wear_version@@' - compileOnly 'com.google.android.wearable:wearable:@@wear_version@@' + implementation 'androidx.appcompat:appcompat:@@appcompat_version@@' implementation files('libs/processing-core.jar') } diff --git a/mode/templates/AppManifest.xml.tmpl b/processing/mode/templates/AppManifest.xml.tmpl similarity index 75% rename from mode/templates/AppManifest.xml.tmpl rename to processing/mode/templates/AppManifest.xml.tmpl index ccd0239e5..4f1abe5ef 100644 --- a/mode/templates/AppManifest.xml.tmpl +++ b/processing/mode/templates/AppManifest.xml.tmpl @@ -2,12 +2,12 @@ - + android:versionName="1.0"> + android:icon="@mipmap/ic_launcher"> + android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen" + android:exported="true"> diff --git a/processing/mode/templates/ExpProperties.gradle.tmpl b/processing/mode/templates/ExpProperties.gradle.tmpl new file mode 100644 index 000000000..777b6aad8 --- /dev/null +++ b/processing/mode/templates/ExpProperties.gradle.tmpl @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536m +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/mode/templates/LayoutActivity.xml.tmpl b/processing/mode/templates/LayoutActivity.xml.tmpl similarity index 100% rename from mode/templates/LayoutActivity.xml.tmpl rename to processing/mode/templates/LayoutActivity.xml.tmpl diff --git a/processing/mode/templates/Properties.gradle.tmpl b/processing/mode/templates/Properties.gradle.tmpl new file mode 100644 index 000000000..6511be76b --- /dev/null +++ b/processing/mode/templates/Properties.gradle.tmpl @@ -0,0 +1,12 @@ +org.gradle.jvmargs=-Xmx1536m +android.enableJetifier=true +android.useAndroidX=true + +PROCESSING_UPLOAD_KEYSTORE_FILE=@@keystore_file@@ +PROCESSING_UPLOAD_KEY_ALIAS=@@key_alias@@ +PROCESSING_UPLOAD_STORE_PASSWORD=@@key_password@@ +PROCESSING_UPLOAD_KEY_PASSWORD=@@key_password@@ + + + + diff --git a/processing/mode/templates/Properties.local.tmpl b/processing/mode/templates/Properties.local.tmpl new file mode 100644 index 000000000..ee51efb73 --- /dev/null +++ b/processing/mode/templates/Properties.local.tmpl @@ -0,0 +1 @@ +sdk.dir=@@sdk_path@@ \ No newline at end of file diff --git a/mode/templates/Settings.gradle.tmpl b/processing/mode/templates/Settings.gradle.tmpl similarity index 100% rename from mode/templates/Settings.gradle.tmpl rename to processing/mode/templates/Settings.gradle.tmpl diff --git a/mode/templates/StringsWallpaper.xml.tmpl b/processing/mode/templates/StringsWallpaper.xml.tmpl similarity index 100% rename from mode/templates/StringsWallpaper.xml.tmpl rename to processing/mode/templates/StringsWallpaper.xml.tmpl diff --git a/processing/mode/templates/StylesAR.xml.tmpl b/processing/mode/templates/StylesAR.xml.tmpl new file mode 100644 index 000000000..38b270523 --- /dev/null +++ b/processing/mode/templates/StylesAR.xml.tmpl @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/mode/templates/StylesFragment.xml.tmpl b/processing/mode/templates/StylesFragment.xml.tmpl similarity index 100% rename from mode/templates/StylesFragment.xml.tmpl rename to processing/mode/templates/StylesFragment.xml.tmpl diff --git a/mode/templates/StylesVR.xml.tmpl b/processing/mode/templates/StylesVR.xml.tmpl similarity index 100% rename from mode/templates/StylesVR.xml.tmpl rename to processing/mode/templates/StylesVR.xml.tmpl diff --git a/mode/templates/TopBuild.gradle.tmpl b/processing/mode/templates/TopBuild.gradle.tmpl similarity index 60% rename from mode/templates/TopBuild.gradle.tmpl rename to processing/mode/templates/TopBuild.gradle.tmpl index fbdb9f106..ef01cc08e 100644 --- a/mode/templates/TopBuild.gradle.tmpl +++ b/processing/mode/templates/TopBuild.gradle.tmpl @@ -4,10 +4,10 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:@@gradle_plugin_version@@' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -16,8 +16,11 @@ buildscript { allprojects { repositories { + maven { url "https://maven.google.com" } + maven { url "https://jitpack.io" } + maven { url 'https://repo.gradle.org/gradle/libs-releases' } google() - jcenter() + mavenCentral() } } diff --git a/processing/mode/templates/VRActivity.java.tmpl b/processing/mode/templates/VRActivity.java.tmpl new file mode 100644 index 000000000..bd2b73cce --- /dev/null +++ b/processing/mode/templates/VRActivity.java.tmpl @@ -0,0 +1,24 @@ +package @@package_name@@; + +import android.os.Build; +import android.os.Bundle; +import android.view.WindowManager; + +import processing.vr.VRActivity; +import processing.core.PApplet; + +public class MainActivity extends VRActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // This is to ensure that the app shows in fullscreen mode with display cutout: + // https://stackoverflow.com/questions/49190381/fullscreen-app-with-displaycutout + getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + PApplet sketch = new @@sketch_class_name@@(); + @@external@@ + setSketch(sketch); + } +} \ No newline at end of file diff --git a/processing/mode/templates/VRBuild.gradle.tmpl b/processing/mode/templates/VRBuild.gradle.tmpl new file mode 100644 index 000000000..3837b763a --- /dev/null +++ b/processing/mode/templates/VRBuild.gradle.tmpl @@ -0,0 +1,59 @@ +apply plugin: 'com.android.application' + +android { + sourceSets { + main.jni.srcDirs = [] + main.jniLibs.srcDirs = ['libs'] + } + compileSdkVersion @@target_sdk@@ + defaultConfig { + applicationId "@@package_name@@" + minSdkVersion @@min_sdk@@ + targetSdkVersion @@target_sdk@@ + versionCode @@version_code@@ + versionName "@@version_name@@" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + signingConfigs { + release { + if (project.hasProperty('PROCESSING_UPLOAD_KEYSTORE_FILE')) { + storeFile file(PROCESSING_UPLOAD_KEYSTORE_FILE) + storePassword PROCESSING_UPLOAD_STORE_PASSWORD + keyAlias PROCESSING_UPLOAD_KEY_ALIAS + keyPassword PROCESSING_UPLOAD_KEY_PASSWORD + } + } + } + buildTypes { + debug { + debuggable true + } + release { + minifyEnabled false + signingConfig signingConfigs.release + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + aaptOptions { + noCompress "tflite" + noCompress "lite" + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.appcompat:appcompat:@@appcompat_version@@' + implementation project('libs:google-vr') + implementation files('libs/processing-core.jar') + implementation files('libs/vr.jar') + implementation 'com.google.protobuf.nano:protobuf-javanano:3.1.0' + androidTestImplementation 'com.android.support.test:runner:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + testImplementation 'junit:junit:4.13' +} diff --git a/mode/templates/VRBuildECJ.gradle.tmpl b/processing/mode/templates/VRBuildECJ.gradle.tmpl similarity index 72% rename from mode/templates/VRBuildECJ.gradle.tmpl rename to processing/mode/templates/VRBuildECJ.gradle.tmpl index cc0adc814..d8368f00f 100644 --- a/mode/templates/VRBuildECJ.gradle.tmpl +++ b/processing/mode/templates/VRBuildECJ.gradle.tmpl @@ -1,6 +1,10 @@ apply plugin: 'com.android.application' android { + sourceSets { + main.jni.srcDirs = [] + main.jniLibs.srcDirs = ['libs'] + } compileSdkVersion @@target_sdk@@ defaultConfig { applicationId "@@package_name@@" @@ -13,17 +17,32 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + signingConfigs { + release { + if (project.hasProperty('PROCESSING_UPLOAD_KEYSTORE_FILE')) { + storeFile file(PROCESSING_UPLOAD_KEYSTORE_FILE) + storePassword PROCESSING_UPLOAD_STORE_PASSWORD + keyAlias PROCESSING_UPLOAD_KEY_ALIAS + keyPassword PROCESSING_UPLOAD_KEY_PASSWORD + } + } + } buildTypes { debug { debuggable true - } + } release { minifyEnabled false + signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false + } + aaptOptions { + noCompress "tflite" + noCompress "lite" } // We create a variant of the compile task, where we use the Eclipse Compiler for Java @@ -32,16 +51,16 @@ android { // https://github.com/bytedeco/javacpp/wiki/Gradle // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html applicationVariants.all { variant -> - variant.javaCompile.doFirst { + variant.javaCompileProvider.get().doFirst { // The main class that runs the Eclipse batch compiler String ecjMain = 'org.eclipse.jdt.internal.compiler.batch.Main' // We construct the list of arguments needed by the batch compiler // https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_batch_compiler.htm List ecjArgs = ['-nowarn', - '-source', variant.javaCompile.sourceCompatibility, - '-target', variant.javaCompile.targetCompatibility, - '-d', variant.javaCompile.destinationDir] as String[] + '-source', variant.javaCompileProvider.get().sourceCompatibility, + '-target', variant.javaCompileProvider.get().targetCompatibility, + '-d', variant.javaCompileProvider.get().destinationDir] as String[] // Set the debug attributes level according to the build target if (variant.name == 'debug') { @@ -53,11 +72,11 @@ android { } // Adding all the source files to the list of arguments - ecjArgs += variant.javaCompile.source + ecjArgs += variant.javaCompileProvider.get().source // Add the Android jar to the classpath inherited from the task FileCollection ecjClasspath = files('@@target_platform@@/android.jar', - variant.javaCompile.classpath) + variant.javaCompileProvider.get().classpath) // Running the JavaExec task, which requires the main class to run, // the classpath, and the list of arguments @@ -76,14 +95,11 @@ android { } dependencies { - compileOnly files('@@tools_folder@@/../modes/java/mode/org.eclipse.jdt.core.jar') + compileOnly files('@@mode_folder@@/org.eclipse.jdt.core.jar') implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:@@support_version@@' - implementation 'com.android.support:design:@@support_version@@' - implementation 'com.google.android.support:wearable:@@wear_version@@' - compileOnly 'com.google.android.wearable:wearable:@@wear_version@@' - implementation 'com.google.vr:sdk-audio:@@gvr_version@@' - implementation 'com.google.vr:sdk-base:@@gvr_version@@' + implementation 'androidx.appcompat:appcompat:@@appcompat_version@@' + implementation project('libs:google-vr') implementation files('libs/processing-core.jar') implementation files('libs/vr.jar') + implementation 'com.google.protobuf.nano:protobuf-javanano:3.1.0' } diff --git a/mode/templates/VRManifest.xml.tmpl b/processing/mode/templates/VRManifest.xml.tmpl similarity index 82% rename from mode/templates/VRManifest.xml.tmpl rename to processing/mode/templates/VRManifest.xml.tmpl index 568c9b21c..0c550cfa0 100644 --- a/mode/templates/VRManifest.xml.tmpl +++ b/processing/mode/templates/VRManifest.xml.tmpl @@ -2,8 +2,7 @@ - + android:versionName="1.0"> @@ -12,16 +11,18 @@ + android:resizeableActivity="false" + android:exported="true"> - + + diff --git a/mode/templates/WallpaperManifest.xml.tmpl b/processing/mode/templates/WallpaperManifest.xml.tmpl similarity index 80% rename from mode/templates/WallpaperManifest.xml.tmpl rename to processing/mode/templates/WallpaperManifest.xml.tmpl index f05b0a590..baec25eb5 100644 --- a/mode/templates/WallpaperManifest.xml.tmpl +++ b/processing/mode/templates/WallpaperManifest.xml.tmpl @@ -2,20 +2,20 @@ - + android:versionName="1.0"> + android:icon="@mipmap/ic_launcher"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="true"> - + diff --git a/mode/templates/WallpaperService.java.tmpl b/processing/mode/templates/WallpaperService.java.tmpl similarity index 100% rename from mode/templates/WallpaperService.java.tmpl rename to processing/mode/templates/WallpaperService.java.tmpl diff --git a/mode/templates/WatchFaceManifest.xml.tmpl b/processing/mode/templates/WatchFaceManifest.xml.tmpl similarity index 91% rename from mode/templates/WatchFaceManifest.xml.tmpl rename to processing/mode/templates/WatchFaceManifest.xml.tmpl index 39edfca81..28d8119da 100644 --- a/mode/templates/WatchFaceManifest.xml.tmpl +++ b/processing/mode/templates/WatchFaceManifest.xml.tmpl @@ -2,18 +2,18 @@ - + android:versionName="1.0"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="true"> - variant.javaCompile.doFirst { + variant.javaCompileProvider.get().doFirst { // The main class that runs the Eclipse batch compiler String ecjMain = 'org.eclipse.jdt.internal.compiler.batch.Main' // We construct the list of arguments needed by the batch compiler // https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_batch_compiler.htm List ecjArgs = ['-nowarn', - '-source', variant.javaCompile.sourceCompatibility, - '-target', variant.javaCompile.targetCompatibility, - '-d', variant.javaCompile.destinationDir] as String[] + '-source', variant.javaCompileProvider.get().sourceCompatibility, + '-target', variant.javaCompileProvider.get().targetCompatibility, + '-d', variant.javaCompileProvider.get().destinationDir] as String[] // Set the debug attributes level according to the build target if (variant.name == 'debug') { @@ -53,11 +68,11 @@ android { } // Adding all the source files to the list of arguments - ecjArgs += variant.javaCompile.source + ecjArgs += variant.javaCompileProvider.get().source // Add the Android jar to the classpath inherited from the task FileCollection ecjClasspath = files('@@target_platform@@/android.jar', - variant.javaCompile.classpath) + variant.javaCompileProvider.get().classpath) // Running the JavaExec task, which requires the main class to run, // the classpath, and the list of arguments @@ -76,15 +91,11 @@ android { } dependencies { - compileOnly files('@@tools_folder@@/../modes/java/mode/org.eclipse.jdt.core.jar') + compileOnly files('@@mode_folder@@/org.eclipse.jdt.core.jar') implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:palette-v7:@@support_version@@' - implementation 'com.android.support:support-v4:@@support_version@@' implementation 'com.google.android.gms:play-services-wearable:@@play_services_version@@' - implementation 'com.android.support:percent:@@support_version@@' - implementation 'com.android.support:recyclerview-v7:@@support_version@@' implementation 'com.google.android.support:wearable:@@wear_version@@' - compileOnly 'com.google.android.wearable:wearable:@@wear_version@@' + compileOnly 'com.google.android.wearable:wearable:@@wear_version@@' implementation files('libs/processing-core.jar') } diff --git a/mode/templates/XMLWallpaper.xml.tmpl b/processing/mode/templates/XMLWallpaper.xml.tmpl similarity index 72% rename from mode/templates/XMLWallpaper.xml.tmpl rename to processing/mode/templates/XMLWallpaper.xml.tmpl index 63775a0d5..4c5057c56 100644 --- a/mode/templates/XMLWallpaper.xml.tmpl +++ b/processing/mode/templates/XMLWallpaper.xml.tmpl @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/mode/templates/XMLWatchFace.xml.tmpl b/processing/mode/templates/XMLWatchFace.xml.tmpl similarity index 100% rename from mode/templates/XMLWatchFace.xml.tmpl rename to processing/mode/templates/XMLWatchFace.xml.tmpl diff --git a/mode/theme/completion/class_obj-1x.png b/processing/mode/theme/completion/class_obj-1x.png similarity index 100% rename from mode/theme/completion/class_obj-1x.png rename to processing/mode/theme/completion/class_obj-1x.png diff --git a/mode/theme/completion/class_obj-2x.png b/processing/mode/theme/completion/class_obj-2x.png similarity index 100% rename from mode/theme/completion/class_obj-2x.png rename to processing/mode/theme/completion/class_obj-2x.png diff --git a/mode/theme/completion/field_default_obj-1x.png b/processing/mode/theme/completion/field_default_obj-1x.png similarity index 100% rename from mode/theme/completion/field_default_obj-1x.png rename to processing/mode/theme/completion/field_default_obj-1x.png diff --git a/mode/theme/completion/field_default_obj-2x.png b/processing/mode/theme/completion/field_default_obj-2x.png similarity index 100% rename from mode/theme/completion/field_default_obj-2x.png rename to processing/mode/theme/completion/field_default_obj-2x.png diff --git a/mode/theme/completion/field_protected_obj-1x.png b/processing/mode/theme/completion/field_protected_obj-1x.png similarity index 100% rename from mode/theme/completion/field_protected_obj-1x.png rename to processing/mode/theme/completion/field_protected_obj-1x.png diff --git a/mode/theme/completion/field_protected_obj-2x.png b/processing/mode/theme/completion/field_protected_obj-2x.png similarity index 100% rename from mode/theme/completion/field_protected_obj-2x.png rename to processing/mode/theme/completion/field_protected_obj-2x.png diff --git a/mode/theme/completion/methpub_obj-1x.png b/processing/mode/theme/completion/methpub_obj-1x.png similarity index 100% rename from mode/theme/completion/methpub_obj-1x.png rename to processing/mode/theme/completion/methpub_obj-1x.png diff --git a/mode/theme/completion/methpub_obj-2x.png b/processing/mode/theme/completion/methpub_obj-2x.png similarity index 100% rename from mode/theme/completion/methpub_obj-2x.png rename to processing/mode/theme/completion/methpub_obj-2x.png diff --git a/mode/theme/debug/breakpoint-enabled-1x.png b/processing/mode/theme/debug/breakpoint-enabled-1x.png similarity index 100% rename from mode/theme/debug/breakpoint-enabled-1x.png rename to processing/mode/theme/debug/breakpoint-enabled-1x.png diff --git a/mode/theme/debug/breakpoint-enabled-2x.png b/processing/mode/theme/debug/breakpoint-enabled-2x.png similarity index 100% rename from mode/theme/debug/breakpoint-enabled-2x.png rename to processing/mode/theme/debug/breakpoint-enabled-2x.png diff --git a/mode/theme/debug/continue-enabled-1x.png b/processing/mode/theme/debug/continue-enabled-1x.png similarity index 100% rename from mode/theme/debug/continue-enabled-1x.png rename to processing/mode/theme/debug/continue-enabled-1x.png diff --git a/mode/theme/debug/continue-enabled-2x.png b/processing/mode/theme/debug/continue-enabled-2x.png similarity index 100% rename from mode/theme/debug/continue-enabled-2x.png rename to processing/mode/theme/debug/continue-enabled-2x.png diff --git a/mode/theme/debug/step-enabled-1x.png b/processing/mode/theme/debug/step-enabled-1x.png similarity index 100% rename from mode/theme/debug/step-enabled-1x.png rename to processing/mode/theme/debug/step-enabled-1x.png diff --git a/mode/theme/debug/step-enabled-2x.png b/processing/mode/theme/debug/step-enabled-2x.png similarity index 100% rename from mode/theme/debug/step-enabled-2x.png rename to processing/mode/theme/debug/step-enabled-2x.png diff --git a/mode/theme/variables-1x.png b/processing/mode/theme/variables-1x.png similarity index 100% rename from mode/theme/variables-1x.png rename to processing/mode/theme/variables-1x.png diff --git a/mode/theme/variables-2x.png b/processing/mode/theme/variables-2x.png similarity index 100% rename from mode/theme/variables-2x.png rename to processing/mode/theme/variables-2x.png diff --git a/processing/mode/tools/SDKUpdater/.classpath b/processing/mode/tools/SDKUpdater/.classpath new file mode 100644 index 000000000..e79c28a0d --- /dev/null +++ b/processing/mode/tools/SDKUpdater/.classpath @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/mode/tools/SDKUpdater/.gitignore b/processing/mode/tools/SDKUpdater/.gitignore similarity index 100% rename from mode/tools/SDKUpdater/.gitignore rename to processing/mode/tools/SDKUpdater/.gitignore diff --git a/processing/mode/tools/SDKUpdater/.project b/processing/mode/tools/SDKUpdater/.project new file mode 100644 index 000000000..730c03fa8 --- /dev/null +++ b/processing/mode/tools/SDKUpdater/.project @@ -0,0 +1,34 @@ + + + android-mode-sdkupdater + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + + + 1675640664206 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/processing/mode/tools/SDKUpdater/.settings/org.eclipse.buildship.core.prefs b/processing/mode/tools/SDKUpdater/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 000000000..e8895216f --- /dev/null +++ b/processing/mode/tools/SDKUpdater/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/mode/.settings/org.eclipse.jdt.core.prefs b/processing/mode/tools/SDKUpdater/.settings/org.eclipse.jdt.core.prefs similarity index 78% rename from mode/.settings/org.eclipse.jdt.core.prefs rename to processing/mode/tools/SDKUpdater/.settings/org.eclipse.jdt.core.prefs index d17b6724d..6558ab78f 100644 --- a/mode/.settings/org.eclipse.jdt.core.prefs +++ b/processing/mode/tools/SDKUpdater/.settings/org.eclipse.jdt.core.prefs @@ -1,12 +1,12 @@ 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.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.compliance=17 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.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.source=17 diff --git a/processing/mode/tools/SDKUpdater/build.gradle b/processing/mode/tools/SDKUpdater/build.gradle new file mode 100644 index 000000000..a47fffbb1 --- /dev/null +++ b/processing/mode/tools/SDKUpdater/build.gradle @@ -0,0 +1,62 @@ +import java.nio.file.Files +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +configurations { + implementation.extendsFrom implementationCopy +} + +dependencies { + // implementation group: "org.processing", name: "pde", version: "${processingVersion}" + // implementation group: "org.processing", name: "java-mode", version: "${processingVersion}" + implementation fileTree(include: ["AndroidMode.jar", "pde.jar", "JavaMode.jar"], dir: '../../mode') + + implementationCopy group: "com.android.tools", name: "sdklib", version: "${toolsLibVersion}" + implementationCopy group: "com.android.tools", name: "repository", version: "${toolsLibVersion}" + implementationCopy group: "com.android.tools", name: "common", version: "${toolsLibVersion}" +} + +// This task copies the gradle tooling jar into the mode folder +task copyToLib(type: Copy) { + from configurations.implementationCopy.files { + + include '**/common-*jar' + include '**/commons-compress-*jar' + include '**/guava-*jar' + include '**/httpcore-*jar' + include '**/istack-*jar' + include '**/jakarta.activation-api-*jar' + include '**/jakarta.xml.bind-api-*jar' + include '**/jaxb-runtime-*jar' + include '**/kotlin-stdlib-1*jar' + include '**/shared-*jar' + include '**/protos-*jar' + include '**/protob*jar' + include '**/sdklib-*jar' + include '**/repository-*jar' + + } + into "lib" +} +build.dependsOn(copyToLib) +compileJava.dependsOn(':mode:copyToLib') + +sourceSets { + main { + java { + srcDirs = ["src/"] + } + } +} + +clean.doFirst { + delete "tool" + delete "lib" +} + +build.doLast { + // Copy jar file to tool folder + File toolJar = file("tool/SDKUpdater.jar"); + toolJar.mkdirs(); + Files.copy(file("$buildDir/libs/SDKUpdater.jar").toPath(), + toolJar.toPath(), REPLACE_EXISTING); +} diff --git a/mode/tools/SDKUpdater/src/processing/mode/android/tools/SDKUpdater.java b/processing/mode/tools/SDKUpdater/src/processing/mode/android/tools/SDKUpdater.java similarity index 67% rename from mode/tools/SDKUpdater/src/processing/mode/android/tools/SDKUpdater.java rename to processing/mode/tools/SDKUpdater/src/processing/mode/android/tools/SDKUpdater.java index 9b3b54ffb..685b6716c 100644 --- a/mode/tools/SDKUpdater/src/processing/mode/android/tools/SDKUpdater.java +++ b/processing/mode/tools/SDKUpdater/src/processing/mode/android/tools/SDKUpdater.java @@ -4,7 +4,7 @@ Part of the Processing project - http://processing.org Copyright (c) 2017 The Processing Foundation - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. @@ -23,13 +23,12 @@ import com.android.repository.api.*; import com.android.repository.impl.meta.RepositoryPackages; -import com.android.repository.io.FileOpUtils; -import com.android.repository.io.impl.FileSystemFileOp; import com.android.repository.util.InstallerUtil; import com.android.sdklib.repository.AndroidSdkHandler; import com.android.sdklib.repository.installer.SdkInstallerUtil; import com.android.sdklib.repository.legacy.LegacyDownloader; import com.android.sdklib.tool.sdkmanager.SdkManagerCli; +import com.android.prefs.AndroidLocationsSingleton; import processing.app.Base; import processing.app.Preferences; @@ -59,17 +58,22 @@ @SuppressWarnings("serial") public class SDKUpdater extends JFrame implements PropertyChangeListener, Tool { - final static private int NUM_ROWS = 10; + final static private int NUM_ROWS = 10; final static private int COL_WIDTH = Toolkit.zoom(220); - + final static private int BORDER = Toolkit.zoom(13); final static private int GAP = Toolkit.zoom(13); final static private int INSET = Toolkit.zoom(1); - final static private int BUTTON_WIDTH = Toolkit.zoom(75); + final static private int BUTTON_WIDTH = Toolkit.zoom(85); final static private int BUTTON_HEIGHT = Toolkit.zoom(25); - + + // private final Vector columns = new Vector<>(Arrays.asList( +// AndroidMode.getTextString("sdk_updater.name_column"), +// AndroidMode.getTextString("sdk_updater.version_column"), +// AndroidMode.getTextString("sdk_updater.available_column"))); private final Vector columns = new Vector<>(Arrays.asList( - "Package name", "Installed version", "Available update")); + "Package name", "Installed version", "Available update" )); + private static final String PROPERTY_CHANGE_QUERY = "query"; private File sdkFolder; @@ -79,67 +83,77 @@ public class SDKUpdater extends JFrame implements PropertyChangeListener, Tool { private boolean downloadTaskRunning; private Vector> packageList; - private DefaultTableModel packageTable; + private DefaultTableModel packageTable; private int numUpdates; private JProgressBar progressBar; private JLabel status; + private JLabel statusSecondary; private JButton actionButton; private JTable table; - + @Override public void init(Base base) { createLayout(base.getActiveEditor() == null); } - + @Override public void run() { setVisible(true); String path = Preferences.get("android.sdk.path"); - sdkFolder = new File(path); + sdkFolder = new File(path); queryTask = new QueryTask(); queryTask.addPropertyChangeListener(this); queryTask.execute(); - status.setText("Querying packages..."); +// status.setText(AndroidMode.getTextString("sdk_updater.query_message")); + status.setText("Querying packages..."); + statusSecondary.setText(""); } - + @Override - public String getMenuTitle() { + public String getMenuTitle() { +// return AndroidMode.getTextString("menu.android.sdk_updater"); return "SDK Updater"; } - - + + @Override public void propertyChange(PropertyChangeEvent evt) { switch (evt.getPropertyName()) { - case PROPERTY_CHANGE_QUERY: - progressBar.setIndeterminate(false); - if (numUpdates == 0) { - actionButton.setEnabled(false); - status.setText("No updates available"); - } else { - actionButton.setEnabled(true); - if (numUpdates == 1) { - status.setText("1 update found!"); - } else { - status.setText(numUpdates + " updates found!"); - } - } - break; + case PROPERTY_CHANGE_QUERY: + progressBar.setIndeterminate(false); + if (numUpdates == 0) { + actionButton.setEnabled(false); +// status.setText(AndroidMode.getTextString("sdk_updater.no_updates_message")); + status.setText("No updates available"); + statusSecondary.setText(""); + } else { + actionButton.setEnabled(true); + if (numUpdates == 1) { +// status.setText(AndroidMode.getTextString("sdk_updater.one_updates_message")); + status.setText("1 update found!"); + statusSecondary.setText(""); + } else { +// status.setText(AndroidMode.getTextString("sdk_updater.many_updates_message", numUpdates)); + status.setText(numUpdates + " updates found!"); + statusSecondary.setText(""); + } + } + break; } } class QueryTask extends SwingWorker { ProgressIndicator progress; - + QueryTask() { super(); - progress = new ConsoleProgressIndicator(); + progress = new CustomProgressIndicatorToMonitor(); } - + @Override protected Object doInBackground() throws Exception { numUpdates = 0; @@ -148,11 +162,10 @@ protected Object doInBackground() throws Exception { /* Following code is from listPackages() of com.android.sdklib.tool.SdkManagerCli with some changes */ - AndroidSdkHandler mHandler = AndroidSdkHandler.getInstance(sdkFolder); - - FileSystemFileOp fop = (FileSystemFileOp) FileOpUtils.create(); + AndroidSdkHandler mHandler = AndroidSdkHandler.getInstance(AndroidLocationsSingleton.INSTANCE, sdkFolder.toPath()); + RepoManager mRepoManager = mHandler.getSdkManager(progress); - mRepoManager.loadSynchronously(0, progress, new LegacyDownloader(fop, new SettingsController() { + mRepoManager.loadSynchronously(0, progress, new LegacyDownloader(new SettingsController() { @Override public boolean getForceHttp() { return false; @@ -165,9 +178,18 @@ public void setForceHttp(boolean b) { } public Channel getChannel() { return null; } + + @Override + public boolean getDisableSdkPatches() { + return false; + } + + @Override + public void setDisableSdkPatches(boolean arg0) { + } }), null); - RepositoryPackages packages = mRepoManager.getPackages(); + RepositoryPackages packages = mRepoManager.getPackages(); HashMap> installed = new HashMap>(); for (LocalPackage local : packages.getLocalPackages().values()) { String path = local.getPath(); @@ -183,17 +205,17 @@ public Channel getChannel() { String major = ver.substring(0, maj); int pos = name.indexOf(major); if (-1 < pos) { - name = name.substring(0, pos).trim(); + name = name.substring(0, pos).trim(); } } installed.put(path, Arrays.asList(name, ver)); } HashMap> updated = new HashMap>(); - for (UpdatablePackage update : packages.getUpdatedPkgs()) { + for (UpdatablePackage update : packages.getUpdatedPkgs()) { String path = update.getPath(); String loc = update.getLocal().getVersion().toString(); - String rem = update.getRemote().getVersion().toString(); + String rem = update.getRemote().getVersion().toString(); updated.put(path, Arrays.asList(loc, rem)); } @@ -204,12 +226,12 @@ public Channel getChannel() { info.add(locInfo.get(1)); if (updated.containsKey(path)) { String upVer = updated.get(path).get(1); - info.add(upVer); + info.add(upVer); numUpdates++; } else { info.add(""); } - packageList.add(info); + packageList.add(info); } return null; @@ -222,7 +244,7 @@ protected void done() { try { get(); firePropertyChange(PROPERTY_CHANGE_QUERY, "query", "SUCCESS"); - + if (packageList != null) { packageTable.setDataVector(packageList, columns); packageTable.fireTableDataChanged(); @@ -232,7 +254,7 @@ protected void done() { } catch (ExecutionException e) { this.cancel(true); JOptionPane.showMessageDialog(null, - e.getCause().toString(), "Error", JOptionPane.ERROR_MESSAGE); + e.getCause().toString(), "Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } } @@ -242,10 +264,10 @@ class DownloadTask extends SwingWorker { ProgressIndicator progress; DownloadTask() { - super(); - progress = new ConsoleProgressIndicator(); + super(); + progress = new CustomProgressIndicatorToMonitor(); } - + @Override protected Object doInBackground() throws Exception { downloadTaskRunning = true; @@ -253,36 +275,37 @@ protected Object doInBackground() throws Exception { /* Following code is from installPackages() of com.android.sdklib.tool.SdkManagerCli with some changes */ - AndroidSdkHandler mHandler = AndroidSdkHandler.getInstance(sdkFolder); + AndroidSdkHandler mHandler = AndroidSdkHandler.getInstance(AndroidLocationsSingleton.INSTANCE, sdkFolder.toPath()); - FileSystemFileOp fop = (FileSystemFileOp) FileOpUtils.create(); CustomSettings settings = new CustomSettings(); - Downloader downloader = new LegacyDownloader(fop, settings); + Downloader downloader = new LegacyDownloader(settings); RepoManager mRepoManager = mHandler.getSdkManager(progress); mRepoManager.loadSynchronously(0, progress, downloader, settings); - + List remotes = new ArrayList<>(); for (String path : settings.getPaths(mRepoManager)) { RemotePackage p = mRepoManager.getPackages().getRemotePackages().get(path); if (p == null) { +// progress.logWarning(AndroidMode.getTextString("sdk_updater.warning_failed_finding_package", path)); progress.logWarning("Failed to find package " + path); throw new SdkManagerCli.CommandFailedException(); } remotes.add(p); } remotes = InstallerUtil.computeRequiredPackages( - remotes, mRepoManager.getPackages(), progress); + remotes, mRepoManager.getPackages(), progress); if (remotes != null) { for (RemotePackage p : remotes) { Installer installer = SdkInstallerUtil.findBestInstallerFactory(p, mHandler) - .createInstaller(p, mRepoManager, downloader, mHandler.getFileOp()); + .createInstaller(p, mRepoManager, downloader); if (!(installer.prepare(progress) && installer.complete(progress))) { // there was an error, abort. throw new SdkManagerCli.CommandFailedException(); } } } else { +// progress.logWarning(AndroidMode.getTextString("sdk_updater.warning_failed_computing_dependency_list")); progress.logWarning("Unable to compute a complete list of dependencies."); throw new SdkManagerCli.CommandFailedException(); } @@ -297,7 +320,9 @@ protected void done() { try { get(); actionButton.setEnabled(false); +// status.setText(AndroidMode.getTextString("sdk_updater.refresh_package_message")); status.setText("Refreshing packages..."); + statusSecondary.setText(""); queryTask = new QueryTask(); queryTask.addPropertyChangeListener(SDKUpdater.this); queryTask.execute(); @@ -306,7 +331,7 @@ protected void done() { } catch (ExecutionException e) { this.cancel(true); JOptionPane.showMessageDialog(null, - e.getCause().toString(), "Error", JOptionPane.ERROR_MESSAGE); + e.getCause().toString(), "Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } finally { downloadTaskRunning = false; @@ -334,18 +359,29 @@ public Channel getChannel() { public java.util.List getPaths(RepoManager mgr) { List updates = new ArrayList<>(); for(UpdatablePackage upd : mgr.getPackages().getUpdatedPkgs()) { - if(!upd.getRemote().obsolete()) { + if (!upd.getRemote().obsolete()) { updates.add(upd.getRepresentative().getPath()); } } return updates; } + + @Override + public boolean getDisableSdkPatches() { + return false; + } + + @Override + public void setDisableSdkPatches(boolean arg0) { + // TODO Auto-generated method stub + + } } } private void createLayout(final boolean standalone) { setTitle(getMenuTitle()); - + Container outer = getContentPane(); outer.removeAll(); @@ -371,20 +407,20 @@ public Class getColumnClass(int columnIndex) { return String.class; } }; - + table = new JTable(packageTable) { @Override public String getColumnName(int column) { return columns.get(column); } - }; + }; table.setFillsViewportHeight(true); table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); table.setRowHeight(Toolkit.zoom(table.getRowHeight())); - Dimension dim = new Dimension(table.getColumnCount() * COL_WIDTH, - table.getRowHeight() * NUM_ROWS); + Dimension dim = new Dimension(table.getColumnCount() * COL_WIDTH, + table.getRowHeight() * NUM_ROWS); table.setPreferredScrollableViewportSize(dim); - + packagesPanel.add(new JScrollPane(table)); JPanel controlPanel = new JPanel(); @@ -393,20 +429,26 @@ public String getColumnName(int column) { GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(INSET, INSET, INSET, INSET); - + status = new JLabel(); status.setText("Starting up..."); gbc.gridx = 0; gbc.gridy = 0; controlPanel.add(status, gbc); - // Using an indeterminate progress bar from now until we learn + statusSecondary = new JLabel(); + statusSecondary.setText("Getting Detailed Information Here..."); + statusSecondary.setFont(new Font("Calibri", Font.PLAIN, 12)); + gbc.gridx = 0; + gbc.gridy = 1; + controlPanel.add(statusSecondary, gbc); + + // Using an indeterminate progress bar from now until we learn // how to update the fraction of the query/download process: // https://github.com/processing/processing-android/issues/362 progressBar = new JProgressBar(); - progressBar.setIndeterminate(true); gbc.gridx = 0; - gbc.gridy = 1; + gbc.gridy = 2; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL; controlPanel.add(progressBar, gbc); @@ -418,24 +460,13 @@ public void actionPerformed(ActionEvent e) { cancelTasks(); } else { // i.e button state is Update downloadTask = new DownloadTask(); - progressBar.setIndeterminate(true); downloadTask.execute(); - // getFraction() always returns 0.0, needs to be set somewhere (??) -// Thread update = new Thread() { -// @Override -// public void run() { -// while (downloadTaskRunning) { -// try { -// Thread.sleep(100); -// } catch (InterruptedException e) { } -// System.out.println("Updating: " + downloadTask.progress.getFraction()); -// } -// } -// }; -// update.start(); - + +// status.setText(AndroidMode.getTextString("sdk_updater.download_package_message")); status.setText("Downloading available updates..."); + statusSecondary.setText(""); +// actionButton.setText(AndroidMode.getTextString("sdk_updater.cancel_button_label")); actionButton.setText("Cancel"); } } @@ -443,9 +474,9 @@ public void actionPerformed(ActionEvent e) { actionButton.setEnabled(false); actionButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT)); gbc.gridx = 1; - gbc.gridy = 0; + gbc.gridy = 0; gbc.weightx = 0.0; - gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.fill = GridBagConstraints.HORIZONTAL; controlPanel.add(actionButton, gbc); ActionListener disposer = new ActionListener() { @@ -458,7 +489,8 @@ public void actionPerformed(ActionEvent actionEvent) { } } }; - + +// JButton closeButton = new JButton(AndroidMode.getTextString("sdk_updater.close_button_label")); JButton closeButton = new JButton("Close"); closeButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT)); closeButton.addActionListener(disposer); @@ -486,26 +518,31 @@ public void windowClosing(WindowEvent e) { super.windowClosing(e); } }); - - registerWindowCloseKeys(getRootPane(), disposer); - + + registerWindowCloseKeys(getRootPane(), disposer); + setLocationRelativeTo(null); setResizable(false); setVisible(false); } - + public void cancelTasks() { queryTask.cancel(true); if (downloadTaskRunning) { downloadTask.cancel(true); +// status.setText(AndroidMode.getTextString("sdk_updater.download_canceled_message")); status.setText("Download canceled"); + statusSecondary.setText(""); JOptionPane.showMessageDialog(null, - "Download canceled", "Warning", JOptionPane.WARNING_MESSAGE); +// AndroidMode.getTextString("sdk_updater.download_canceled_message"), + "Download canceled", + "Warning", JOptionPane.WARNING_MESSAGE); +// actionButton.setText(AndroidMode.getTextString("sdk_updater.update_button_label")); actionButton.setText("Update"); } } - - + + /** * Registers key events for a Ctrl-W and ESC with an ActionListener * that will take care of disposing the window. @@ -514,11 +551,104 @@ static public void registerWindowCloseKeys(JRootPane root, ActionListener disposer) { KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); root.registerKeyboardAction(disposer, stroke, - JComponent.WHEN_IN_FOCUSED_WINDOW); + JComponent.WHEN_IN_FOCUSED_WINDOW); - int modifiers = java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + int modifiers = java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); stroke = KeyStroke.getKeyStroke('W', modifiers); root.registerKeyboardAction(disposer, stroke, - JComponent.WHEN_IN_FOCUSED_WINDOW); - } + JComponent.WHEN_IN_FOCUSED_WINDOW); + } + + class CustomProgressIndicatorToMonitor implements com.android.repository.api.ProgressIndicator{ + + int percentage = 0; + String progressTextData = ""; + String progressTextDataDetailed = ""; + + @Override + public void setText(String progressText) { + progressTextData = progressText; + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void cancel() { + + } + + @Override + public void setCancellable(boolean cancellable) { + + } + + @Override + public boolean isCancellable() { + return false; + } + + @Override + public void setIndeterminate(boolean indeterminate) { + + } + + @Override + public boolean isIndeterminate() { + return false; + } + + @Override + public void setFraction(double progress) { + if(progress<=1 && progress>=0) { + percentage = ((int)(progress*100)); + // System.out.println("CustomProgressIndicatorToMonitor:Progress Bar Percentage:"+percentage); + progressBar.setValue(percentage); + status.setText(progressTextData+" "+percentage+" % "); + statusSecondary.setText(progressTextDataDetailed); + } + } + + @Override + public double getFraction() { + return 0; + } + + @Override + public void setSecondaryText(String s) { + progressTextDataDetailed = s; + // System.out.println("CustomProgressIndicatorToMonitor:SecondaryText:"+s); + } + + @Override + public void logWarning(String s) { + progressTextDataDetailed = s; + // System.out.println("CustomProgressIndicatorToMonitor:WarningText:"+s); + } + + @Override + public void logWarning(String s, Throwable e) { + + } + + @Override + public void logError(String s) { + progressTextDataDetailed = s; + // System.out.println("CustomProgressIndicatorToMonitor:ErrorText:"+s); + } + + @Override + public void logError(String s, Throwable e) { + + } + + @Override + public void logInfo(String s) { + progressTextDataDetailed = s; + // System.out.println("CustomProgressIndicatorToMonitor:LogInfoText:"+s); + } + } + } diff --git a/processing/mode/version.properties b/processing/mode/version.properties new file mode 100644 index 000000000..fbf85d18d --- /dev/null +++ b/processing/mode/version.properties @@ -0,0 +1,29 @@ +# Basics +android-platform = 33 +android-platform-wear = 30 +android-platform-wear-arm = 24 +android-toolslib = 30.3.0 +android-gradle-plugin = 7.1.0 +gradle-wrapper = 7.4.2 + +# Minimum SDK versions for each type of project +android-min-app = 17 +android-min-wallpaper = 17 +android-min-vr = 19 +android-min-ar = 24 +android-min-wear = 25 + +# Dependencies. Latest versions could be found at: +# https://mvnrepository.com +# https://repo.gradle.org +# The format below is group%artifact +androidx.appcompat%appcompat = 1.6.0 +androidx.legacy%legacy-support-v4 = 1.0.0 +com.google.android.support%wearable = 2.9.0 +com.google.android.gms%play-services-wearable = 18.0.0 +com.google.vr = 1.180.0 +com.google.ar = 1.37.0 +org.processing = 4.0.0b7 +org.gradle%gradle-tooling-api = 7.2 +org.slf4j = 1.7.30 +org.eclipse.jdt = 3.19.300 diff --git a/processing/scripts/publish-module.gradle b/processing/scripts/publish-module.gradle new file mode 100644 index 000000000..5d3fdcb89 --- /dev/null +++ b/processing/scripts/publish-module.gradle @@ -0,0 +1,55 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + artifact(libJar) + artifact(libSrc) + artifact(libMd5) + pom { + groupId = "org.p5android" + artifactId = "${libName}" + version = "${libVersion}" + packaging = "jar" + licenses { + license { + name = "GNU Lesser General Public License, version 2.1" + url = "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt" + distribution = "repo" + } + } + scm { + connection = 'scm:git:github.com/processing/processing-android.git' + developerConnection = 'scm:git:ssh://github.com/processing/processing-android.git' + url = 'https://github.com/processing/processing-android/tree/master' + } + } + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + libDependencies.each { + def dependencyNode = dependenciesNode.appendNode('dependency') + if (it.name == 'android') { + dependencyNode.appendNode('artifactId', 'android') + dependencyNode.appendNode('scope', 'runtime') + } else { + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + dependencyNode.appendNode('scope', 'implementation') + } + } + } + } + } + } +} + +ext["signing.keyId"] = rootProject.ext["signing.keyId"] +ext["signing.password"] = rootProject.ext["signing.password"] +ext["signing.secretKeyRingFile"] = rootProject.ext["signing.secretKeyRingFile"] + +signing { + sign publishing.publications +} \ No newline at end of file diff --git a/processing/scripts/publish-root.gradle b/processing/scripts/publish-root.gradle new file mode 100644 index 000000000..166c3bace --- /dev/null +++ b/processing/scripts/publish-root.gradle @@ -0,0 +1,36 @@ +// Create variables with empty default values +ext["signing.keyId"] = '' +ext["signing.password"] = '' +ext["signing.secretKeyRingFile"] = '' +ext["ossrhUsername"] = '' +ext["ossrhPassword"] = '' +ext["sonatypeStagingProfileId"] = '' + +File secretPropsFile = project.rootProject.file('local.properties') +if (secretPropsFile.exists()) { + // Read local.properties file first if it exists + Properties p = new Properties() + new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) } + p.each { name, value -> ext[name] = value } +} else { + // Use system environment variables + ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') + ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') + ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') + ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') + ext["signing.password"] = System.getenv('SIGNING_PASSWORD') + ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') +} + +// Set up Sonatype repository +nexusPublishing { + repositories { + sonatype { + stagingProfileId = sonatypeStagingProfileId + username = ossrhUsername + password = ossrhPassword + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + } + } +} \ No newline at end of file diff --git a/processing/settings.gradle b/processing/settings.gradle new file mode 100644 index 000000000..9e3fad4ab --- /dev/null +++ b/processing/settings.gradle @@ -0,0 +1,2 @@ +include ':mode', ':core', ':mode:libraries:vr', ':mode:libraries:ar', 'mode:tools:SDKUpdater' + diff --git a/settings.gradle b/settings.gradle index d02116a4f..193a03378 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1 @@ -include ':core', ':mode:libraries:vr', 'mode:tools:SDKUpdater', ':mode' - +include ':libs:processing-core', 'libs:google-vr',':libs:processing-vr', ':libs:processing-ar', ':apps:simple', ':apps:wallpaper', ':apps:arscene', ':apps:watchface', ':apps:fast2d', ':apps:armarkers' \ No newline at end of file diff --git a/studio/apps/fast2d/src/main/assets/balmer_developers_poster.png b/studio/apps/fast2d/src/main/assets/balmer_developers_poster.png deleted file mode 100644 index 49859d914..000000000 Binary files a/studio/apps/fast2d/src/main/assets/balmer_developers_poster.png and /dev/null differ diff --git a/studio/apps/fast2d/src/main/java/fast2d/MainActivity.java b/studio/apps/fast2d/src/main/java/fast2d/MainActivity.java deleted file mode 100644 index b3795fed1..000000000 --- a/studio/apps/fast2d/src/main/java/fast2d/MainActivity.java +++ /dev/null @@ -1,57 +0,0 @@ -package fast2d; - -import android.os.Bundle; -import android.content.Intent; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.support.v7.app.AppCompatActivity; - -import processing.android.PFragment; -import processing.android.CompatUtils; -import processing.core.PApplet; - -public class MainActivity extends AppCompatActivity { - private PApplet sketch; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - FrameLayout frame = new FrameLayout(this); - frame.setId(CompatUtils.getUniqueViewId()); - setContentView(frame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - sketch = new Sketch(); - PFragment fragment = new PFragment(sketch); - fragment.setView(frame, this); - } - - @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/studio/apps/simple/build.gradle b/studio/apps/simple/build.gradle deleted file mode 100644 index 56b31a5e3..000000000 --- a/studio/apps/simple/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 26 - defaultConfig { - applicationId "processing.tests.simple" - minSdkVersion 17 - targetSdkVersion 26 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - productFlavors { - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' - implementation project(':libs:processing-core') - implementation 'com.android.support:appcompat-v7:26.0.2' -} diff --git a/studio/apps/vrcube/src/main/java/vrcube/MainActivity.java b/studio/apps/vrcube/src/main/java/vrcube/MainActivity.java deleted file mode 100644 index 7c6228ce6..000000000 --- a/studio/apps/vrcube/src/main/java/vrcube/MainActivity.java +++ /dev/null @@ -1,16 +0,0 @@ -package vrcube; - -import android.os.Bundle; - -import processing.vr.PVR; -import processing.core.PApplet; - -public class MainActivity extends PVR { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PApplet sketch = new Sketch(); - sketch.setExternal(true); - setSketch(sketch); - } -} \ No newline at end of file diff --git a/studio/apps/vrcube/src/main/java/vrcube/Sketch.java b/studio/apps/vrcube/src/main/java/vrcube/Sketch.java deleted file mode 100644 index a2cab9532..000000000 --- a/studio/apps/vrcube/src/main/java/vrcube/Sketch.java +++ /dev/null @@ -1,22 +0,0 @@ -package vrcube; - -import processing.core.PApplet; -import processing.vr.*; - -public class Sketch extends PApplet { - - public void settings() { - fullScreen(STEREO); - } - - public void setup() { } - - public void draw() { - background(157); - lights(); - translate(width/2, height/2); - rotateX(frameCount * 0.01f); - rotateY(frameCount * 0.01f); - box(350); - } -} diff --git a/studio/build.gradle b/studio/build.gradle deleted file mode 100644 index 5cbb6cde8..000000000 --- a/studio/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - jcenter() - google() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - // There is a problem in JCenter with some Android pacakges, using the following maven repo - // fixes the issue: - // https://stackoverflow.com/questions/50563338/could-not-find-runtime-jar-android-arch-lifecycleruntime1-0-0/50564224 - maven { url "https://maven.google.com" } - - jcenter() - google() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/studio/libs/processing-core/AndroidManifest.xml b/studio/libs/processing-core/AndroidManifest.xml deleted file mode 100755 index 5587f2ed2..000000000 --- a/studio/libs/processing-core/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/studio/libs/processing-core/build.gradle b/studio/libs/processing-core/build.gradle deleted file mode 100644 index 6ce79d3bb..000000000 --- a/studio/libs/processing-core/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion 26 - defaultConfig { - minSdkVersion 17 - targetSdkVersion 21 - } - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['../../../core/src'] - assets.srcDirs = ['../../../core/src/assets'] - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - productFlavors { - } -} - -dependencies { - implementation "com.android.support:support-v4:26.0.2" - implementation 'com.google.android.support:wearable:2.1.0' -} \ No newline at end of file diff --git a/studio/libs/processing-core/gradle.properties b/studio/libs/processing-core/gradle.properties deleted file mode 100755 index f93e4dd7f..000000000 --- a/studio/libs/processing-core/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -POM_NAME=Processing for Android Core Library -POM_ARTIFACT_ID=processing-core -POM_PACKAGING=aar \ No newline at end of file diff --git a/studio/libs/processing-core/project.properties b/studio/libs/processing-core/project.properties deleted file mode 100755 index 36f15941e..000000000 --- a/studio/libs/processing-core/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-15 -android.library=true diff --git a/studio/libs/processing-vr/AndroidManifest.xml b/studio/libs/processing-vr/AndroidManifest.xml deleted file mode 100755 index bd9f8c46f..000000000 --- a/studio/libs/processing-vr/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/studio/libs/processing-vr/build.gradle b/studio/libs/processing-vr/build.gradle deleted file mode 100755 index 58b2f1f93..000000000 --- a/studio/libs/processing-vr/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion 26 - defaultConfig { - minSdkVersion 19 - targetSdkVersion 26 - } - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['../../../mode/libraries/vr/src'] - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - productFlavors { - } -} - -dependencies { - implementation project(':libs:processing-core') - implementation 'com.google.vr:sdk-audio:1.150.0' - implementation 'com.google.vr:sdk-base:1.150.0' -} \ No newline at end of file diff --git a/studio/libs/processing-vr/gradle.properties b/studio/libs/processing-vr/gradle.properties deleted file mode 100755 index 947083428..000000000 --- a/studio/libs/processing-vr/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -POM_NAME=Processing for Android VR Library -POM_ARTIFACT_ID=processing-vr -POM_PACKAGING=aar \ No newline at end of file diff --git a/studio/libs/processing-vr/project.properties b/studio/libs/processing-vr/project.properties deleted file mode 100755 index 91d2b0246..000000000 --- a/studio/libs/processing-vr/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-19 -android.library=true diff --git a/studio/settings.gradle b/studio/settings.gradle deleted file mode 100644 index 8a0424609..000000000 --- a/studio/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':apps:simple', ':apps:wallpaper', ':apps:vrcube', ':apps:watchface', ':apps:fast2d', ':libs:processing-core', ':libs:processing-vr'