aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java
diff options
context:
space:
mode:
Diffstat (limited to 'platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java')
-rw-r--r--platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java260
1 files changed, 260 insertions, 0 deletions
diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java
new file mode 100644
index 0000000000..e4aaf5784b
--- /dev/null
+++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2018 Google LLC All Rights Reserved.
+ *
+ * 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 com.google.skar.examples.helloskar.app;
+
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.util.Log;
+
+import com.google.skar.examples.helloskar.helpers.TapHelper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.util.ArrayList;
+
+public class FingerPainting {
+ public static class BuiltPath {
+ public Path path;
+ public int color;
+
+ BuiltPath(Path p, int c) {
+ this.path = p;
+ this.color = c;
+ }
+ }
+
+ // Points obtained by touching the screen. The first point is always brought to (0,0).
+ // All subsequent points are translated by the same amount.
+ private ArrayList<PointF> points = new ArrayList<>();
+
+ // Indices in points array that indicate the start of a new path. E.g: if 5 is added to
+ // jumpPoints, then index 5 of the points array is the start of a new path (use moveTo())
+ private ArrayList<Integer> jumpPoints = new ArrayList<>();
+
+ // Map from index (start of path) to color of path
+ private Map<Integer, Integer> indexColors = new HashMap<>();
+
+ // List of built paths (reset each frame)
+ private ArrayList<BuiltPath> paths = new ArrayList<>();
+
+ // Previous point added to the path. This points belongs to the path in local space.
+ private float[] previousLocalPoint = new float[2];
+
+ // Previous point added to the path. This points belongs to the path in global space (i.e Pose)
+ private float[] previousGlobalPoint = new float[2];
+
+ // Holds the model matrix of the first point added to such that the path can be drawn at the
+ // model location (i.e on the Plane)
+ private float[] modelMatrix;
+
+ // Currently selected color in the UI
+ private int color = Color.RED;
+
+ // True if path should be drawn using buildSmoothFromTo()
+ private boolean isSmooth;
+
+ public FingerPainting(boolean smooth) {
+ this.isSmooth = smooth;
+ }
+
+ public void setSmoothness(boolean smooth) {
+ isSmooth = smooth;
+ }
+
+ /**
+ * Given a hit location in Global space (e.g on a Plane), and the associated ScrollEvent,
+ * construct the next point in the path in Local space. The first point of the Finger Painting
+ * must be at (0,0)
+ * @param hitLocation (x, y) coordinates of the hit position in Global space
+ * @param holdTap ScrollEvent associated with the hit test that calls this function
+ * @return true if point was computed and added. False otherwise.
+ */
+ public boolean computeNextPoint(float[] hitLocation, TapHelper.ScrollEvent holdTap) {
+ if (isEmpty()) {
+ // If finger painting is empty, then first point is origin. Model matrix
+ // of the finger painting is the model matrix of the first point
+ addPoint(new PointF(0, 0), true);
+
+ // Get model matrix of first point
+ setModelMatrix(modelMatrix);
+ } else {
+ // Else, construct next point given its distance from previous point
+ float localDistanceScale = 1000;
+ PointF distance = new PointF(hitLocation[0] - previousGlobalPoint[0],
+ hitLocation[2] - previousGlobalPoint[1]);
+
+ if (distance.length() < 0.01f) {
+ // If distance between previous stored point and current point is too
+ // small, skip it
+ return false;
+ }
+
+ // New point is distance + old point
+ PointF p = new PointF(distance.x * localDistanceScale + previousLocalPoint[0],
+ distance.y * localDistanceScale + previousLocalPoint[1]);
+
+ addPoint(p, holdTap.isStartOfScroll);
+ }
+ previousGlobalPoint[0] = hitLocation[0];
+ previousGlobalPoint[1] = hitLocation[1];
+ return true;
+ }
+
+ /**
+ * Constructs the Paths to be drawn (populates the paths List). Call this before drawing this
+ * Finger Painting (every frame).
+ */
+ public void buildPath() {
+ if (points.size() <= 1) {
+ // Don't build anything if the path only contains one point
+ return;
+ }
+
+ paths = new ArrayList<>();
+
+ if (isSmooth) {
+ buildSmooth();
+ } else {
+ buildRough();
+ }
+ }
+
+ /**
+ * @return 16-float matrix that takes a point from Local space to Global space (onto the Plane)
+ */
+ public float[] getModelMatrix() {
+ return modelMatrix;
+ }
+
+ /**
+ * Change currently selected color. Preferably called through a UI element (a menu)
+ * @param color color to be selected this frame
+ */
+ public void setColor(int color) {
+ this.color = color;
+ }
+
+ /**
+ * @return List of built paths contained within this Finger Painting
+ */
+ public List<BuiltPath> getPaths() {
+ return paths;
+ }
+
+ /**
+ * Clears data contained within this Finger Painting
+ */
+ public void reset() {
+ points.clear();
+ jumpPoints.clear();
+ paths.clear();
+ indexColors.clear();
+ }
+
+ /********************** PRIVATE HELPERS **************************************/
+
+ // Adds another point to the path in Local space
+ private void addPoint(PointF p, boolean jumpPoint) {
+ points.add(p);
+ if (jumpPoint) {
+ jumpPoints.add(points.size() - 1);
+ indexColors.put(points.size() - 1, color);
+ }
+ previousLocalPoint[0] = p.x;
+ previousLocalPoint[1] = p.y;
+ }
+
+ // Builds paths of this Finger Painting using the rough algorithm
+ private void buildRough() {
+ int start = 0; // starting index of each path. 1st path starts at index 0 points list
+ for (int j = 1; j < jumpPoints.size(); j++) {
+ int finish = jumpPoints.get(j); // finishing index of current path
+ buildRoughFromTo(start, finish);
+ start = finish;
+ }
+
+ buildRoughFromTo(start, points.size());
+ }
+
+ // Builds paths of this Finger Painting using the smooth algorithm
+ private void buildSmooth() {
+ int start = 0;
+ for (int j = 1; j < jumpPoints.size(); j++) {
+ int finish = jumpPoints.get(j);
+ buildSmoothFromTo(start, finish);
+ start = finish;
+ }
+
+ buildSmoothFromTo(start, points.size());
+ }
+
+ // Builds a rough path that starts at index (start) of the points List, and ends at (finish - 1)
+ // of the points List
+ private void buildRoughFromTo(int start, int finish) {
+ Path p = new Path();
+ int c = indexColors.get(start);
+ p.moveTo(points.get(start).x, points.get(start).y);
+
+ for (int i = start + 1; i < finish; i++) {
+ p.lineTo(points.get(i).x, points.get(i).y);
+ }
+
+ BuiltPath bp = new BuiltPath(p, c);
+ paths.add(bp);
+ }
+
+ // Builds a smooth path that starts at index (start) of the points List, and ends at (finish - 1)
+ // of the points List
+ private void buildSmoothFromTo(int start, int finish) {
+ Path p = new Path();
+ int c = indexColors.get(start);
+
+ int nbPts = finish - start; // # of points within this path (not including the finish index)
+
+ // If only 2 points in path, draw a line between them
+ if (nbPts == 2) {
+ p.moveTo(points.get(start).x, points.get(start).y);
+ p.lineTo(points.get(start + 1).x, points.get(start + 1).y);
+ } else if (nbPts >= 3) {
+ // Else (3 pts +), essentially run deCasteljau
+ p.moveTo(points.get(start).x, points.get(start).y);
+ p.lineTo((points.get(start).x + points.get(start + 1).x) / 2,
+ (points.get(start).y + points.get(start + 1).y) / 2);
+
+ for (int i = start + 1; i < finish - 1; i++) {
+ PointF p1 = points.get(i);
+ PointF p2 = points.get(i + 1);
+ p.quadTo(p1.x, p1.y, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
+ }
+
+ p.lineTo(points.get(finish - 1).x, points.get(finish - 1).y);
+ }
+
+ BuiltPath bp = new BuiltPath(p, c);
+ paths.add(bp);
+ }
+
+ private boolean isEmpty() { return points.isEmpty(); }
+
+ private void setModelMatrix(float[] m) {
+ modelMatrix = m;
+ }
+}