diff options
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.java | 260 |
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; + } +} |