From 3c9a0c04f243f7c8ed21de8ebb666d0516ff555f Mon Sep 17 00:00:00 2001 From: ziadb Date: Fri, 27 Jul 2018 11:19:19 -0400 Subject: SkAR Java: refactoring main app class & finger painting class Main things to look at: 1) onDrawFrame changes + moving many functions as separate helpers 2) SkARFingerPainting changes (name should change too) Bug: skia: Change-Id: I5068ce6c416a2f5d6c6c389cd63d08d5350e83e6 Reviewed-on: https://skia-review.googlesource.com/143701 Reviewed-by: Mike Reed --- .../apps/skar_java/src/main/AndroidManifest.xml | 2 +- .../java/com/google/skar/CanvasMatrixUtil.java | 16 ++ .../src/main/java/com/google/skar/PaintUtil.java | 16 ++ .../skar/examples/helloskar/app/ARSurfaceView.java | 45 ---- .../helloskar/app/CanvasARSurfaceView.java | 62 +++++ .../examples/helloskar/app/FingerPainting.java | 260 ++++++++++++++++++++ .../skar/examples/helloskar/app/HelloCanvasAR.java | 264 +++++++++++---------- .../examples/helloskar/app/SkARFingerPainting.java | 155 ------------ .../helloskar/helpers/CameraPermissionHelper.java | 6 +- .../helloskar/helpers/DisplayRotationHelper.java | 6 +- .../helloskar/helpers/FullScreenHelper.java | 6 +- .../examples/helloskar/helpers/SnackbarHelper.java | 6 +- .../skar/examples/helloskar/helpers/TapHelper.java | 21 +- .../helloskar/rendering/BackgroundRenderer.java | 6 +- .../examples/helloskar/rendering/DrawManager.java | 39 ++- .../examples/helloskar/rendering/ShaderUtil.java | 6 +- .../src/main/res/layout/activity_main.xml | 10 +- 17 files changed, 557 insertions(+), 369 deletions(-) delete mode 100644 platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/ARSurfaceView.java create mode 100644 platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/CanvasARSurfaceView.java create mode 100644 platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/FingerPainting.java delete mode 100644 platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java (limited to 'platform_tools') diff --git a/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml b/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml index abfacc5fe3..efd40d18ed 100644 --- a/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml +++ b/platform_tools/android/apps/skar_java/src/main/AndroidManifest.xml @@ -33,7 +33,7 @@ tools:ignore="GoogleAppIndexingWarning"> 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 jumpPoints = new ArrayList<>(); + + // Map from index (start of path) to color of path + private Map indexColors = new HashMap<>(); + + // List of built paths (reset each frame) + private ArrayList 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 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; + } +} diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java index 718545e72b..d883c693a2 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/HelloCanvasAR.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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. @@ -20,14 +20,13 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.PointF; import android.graphics.PorterDuff; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.design.internal.BottomNavigationMenuView; + import android.support.design.widget.BottomNavigationView; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -53,12 +52,17 @@ import com.google.ar.core.PointCloud; import com.google.ar.core.Session; import com.google.ar.core.Trackable; import com.google.ar.core.TrackingState; -import com.google.ar.core.examples.java.common.helpers.CameraPermissionHelper; -import com.google.ar.core.examples.java.common.helpers.DisplayRotationHelper; -import com.google.ar.core.examples.java.common.helpers.FullScreenHelper; -import com.google.ar.core.examples.java.common.helpers.SnackbarHelper; -import com.google.ar.core.examples.java.common.helpers.TapHelper; -import com.google.ar.core.examples.java.common.rendering.BackgroundRenderer; + +import com.google.ar.core.examples.java.helloskar.R; +import com.google.skar.examples.helloskar.helpers.CameraPermissionHelper; +import com.google.skar.examples.helloskar.helpers.DisplayRotationHelper; +import com.google.skar.examples.helloskar.helpers.FullScreenHelper; +import com.google.skar.examples.helloskar.helpers.SnackbarHelper; +import com.google.skar.examples.helloskar.helpers.TapHelper; + +import com.google.skar.examples.helloskar.rendering.BackgroundRenderer; +import com.google.skar.examples.helloskar.rendering.DrawManager; + import com.google.ar.core.exceptions.CameraNotAvailableException; import com.google.ar.core.exceptions.UnavailableApkTooOldException; import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException; @@ -79,41 +83,33 @@ import javax.microedition.khronos.opengles.GL10; */ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Renderer { - public enum DrawingType { - circle, rect, text, animation - } - private static final String TAG = HelloCanvasAR.class.getSimpleName(); + private final int MAX_NUMBER_DRAWABLES = 50; // Arbitrary limit to the # of anchors to store - //Simple SurfaceView used to draw 2D objects on top of the GLSurfaceView - private ARSurfaceView arSurfaceView; - private Canvas canvas; + // Simple SurfaceView used to draw 2D objects on top of the GLSurfaceView + private CanvasARSurfaceView arSurfaceView; private SurfaceHolder holder; - //GLSurfaceView used to draw 3D objects & camera input + // GLSurfaceView used to draw 3D objects & camera input private GLSurfaceView glSurfaceView; - //ARSession + // ARSession private Session session; - private boolean installRequested; - private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper(); - private DisplayRotationHelper displayRotationHelper; - private TapHelper tapHelper; - // OpenGL background renderer private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer(); // 2D Renderer private DrawManager drawManager = new DrawManager(); - private DrawingType currentDrawabletype = DrawingType.circle; - private boolean drawSmoothPainting = true; + + private boolean installRequested; + private final SnackbarHelper messageSnackbarHelper = new SnackbarHelper(); + private DisplayRotationHelper displayRotationHelper; + private TapHelper tapHelper; // Temporary matrix allocated here to reduce number of allocations for each frame. private final float[] anchorMatrix = new float[16]; - PointF previousEvent; - // Anchors created from taps used for object placing. private final ArrayList anchors = new ArrayList<>(); @@ -127,22 +123,22 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar); - setSupportActionBar(myToolbar); - + // Menu tool bar set up + Toolbar toolbar = findViewById(R.id.main_toolbar); + setSupportActionBar(toolbar); - //hide notifications bar + // Hide notifications bar getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - arSurfaceView = findViewById(R.id.arsurfaceview); - glSurfaceView = findViewById(R.id.glsurfaceview); + // Canvas Surface View set up + arSurfaceView = findViewById(R.id.canvas_surfaceview); arSurfaceView.bringToFront(); arSurfaceView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - displayRotationHelper = new DisplayRotationHelper(/*context=*/ this); + holder = arSurfaceView.getHolder(); // Set up tap listener. - tapHelper = new TapHelper(/*context=*/ this); + tapHelper = new TapHelper(this); glSurfaceView.setOnTouchListener(tapHelper); // Set up renderer. @@ -151,9 +147,10 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending. glSurfaceView.setRenderer(this); glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - + displayRotationHelper = new DisplayRotationHelper(this); installRequested = false; + // Set up finger painting palette bar BottomNavigationView bottomNav = findViewById(R.id.palette); bottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override @@ -174,7 +171,7 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re } }); - // Animator set up + // Value Animator set up PropertyValuesHolder propertyRadius = PropertyValuesHolder.ofFloat(PROPERTY_RADIUS, 0, 0.5f); animator = new ValueAnimator(); animator.setValues(propertyRadius); @@ -310,7 +307,7 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re @Override public void onDrawFrame(GL10 gl) { - canvas = null; + Canvas canvas = null; holder = null; // Clear screen to notify driver it should not load any pixels from previous frame. @@ -319,38 +316,18 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re if (session == null) { return; } + // Notify ARCore session that the view size changed so that the perspective matrix and // the video background can be properly adjusted. displayRotationHelper.updateSessionIfNeeded(session); - try { session.setCameraTextureName(backgroundRenderer.getTextureId()); Frame frame = session.update(); Camera camera = frame.getCamera(); - MotionEvent tap = tapHelper.poll(); - if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) { - for (HitResult hit : frame.hitTest(tap)) { - // Check if any plane was hit, and if it was hit inside the plane polygon - Trackable trackable = hit.getTrackable(); - // Creates an anchor if a plane or an oriented point was hit. - if ((trackable instanceof Plane - && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()) - && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) - > 0)) - || (trackable instanceof Point - && ((Point) trackable).getOrientationMode() - == OrientationMode.ESTIMATED_SURFACE_NORMAL)) { - if (anchors.size() >= 20) { - anchors.get(0).detach(); - anchors.remove(0); - } - anchors.add(hit.createAnchor()); - break; - } - } - } + // Query information from single tap gestures to get anchors + handleSingleTaps(frame, camera); // Draw background with OpenGL. // TODO: possibly find a way to extract texture and draw on Canvas @@ -362,76 +339,26 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re } // Get projection matrix. - float[] projmtx = new float[16]; - camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f); - drawManager.updateProjectionMatrix(projmtx); + float[] projMatrix = new float[16]; + camera.getProjectionMatrix(projMatrix, 0, 0.1f, 100.0f); + drawManager.updateProjectionMatrix(projMatrix); // Get camera matrix and draw. - float[] viewmtx = new float[16]; - camera.getViewMatrix(viewmtx, 0); - drawManager.updateViewMatrix(viewmtx); + float[] viewMatrix = new float[16]; + camera.getViewMatrix(viewMatrix, 0); + drawManager.updateViewMatrix(viewMatrix); final float[] colorCorrectionRgba = new float[4]; frame.getLightEstimate().getColorCorrection(colorCorrectionRgba, 0); drawManager.updateLightColorFilter(colorCorrectionRgba); - // Building finger painting - TapHelper.ScrollEvent holdTap = tapHelper.holdPoll(); - if (holdTap != null && camera.getTrackingState() == TrackingState.TRACKING) { - for (HitResult hit : frame.hitTest(holdTap.e)) { - // Check if any plane was hit, and if it was hit inside the plane polygon - Trackable trackable = hit.getTrackable(); - // Creates an anchor if a plane or an oriented point was hit. - if ((trackable instanceof Plane - && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()) - && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) - > 0)) - || (trackable instanceof Point - && ((Point) trackable).getOrientationMode() - == OrientationMode.ESTIMATED_SURFACE_NORMAL)) { - - // Get hit point transform, apply it to the origin - float[] gm = new float[16]; - hit.getHitPose().toMatrix(gm, 0); - float[] point = {0, 0, 0, 1}; - Matrix.multiplyMV(point, 0, gm, 0, point, 0); - - if (drawManager.fingerPainting.isEmpty()) { - drawManager.fingerPainting.addPoint(new PointF(0, 0), true); - - // Get model matrix of first point - float[] m = new float[16]; - hit.getHitPose().toMatrix(m, 0); - drawManager.fingerPainting.setModelMatrix(m); - } else { - float localDistanceScale = 1000; - PointF distance = new PointF(point[0] - previousEvent.x, - point[2] - previousEvent.y); - - if (distance.length() < 0.05f) { - continue; - } - - // New point is distance + old point - PointF p = new PointF(distance.x * localDistanceScale - + drawManager.fingerPainting.previousPoint.x, - distance.y * localDistanceScale - + drawManager.fingerPainting.previousPoint.y); - - drawManager.fingerPainting.addPoint(p, holdTap.isStartOfScroll); - } - - previousEvent = new PointF(point[0], point[2]); - break; - } - } - } + // Query information from scrolling gestures to build finger paintings + handleHoldTaps(frame, camera); // Drawing on Canvas (SurfaceView) if (arSurfaceView.isRunning()) { // Lock canvas - SurfaceHolder holder = arSurfaceView.getHolder(); - Canvas canvas = holder.lockHardwareCanvas(); + canvas = holder.lockHardwareCanvas(); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // Draw point cloud @@ -472,6 +399,81 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re } } + /**************************** Gesture helpers ******************************/ + /** + * Given a Frame and a Camera, perform hit tests on stored UI touch events. If a hit test is + * successful, construct an Anchor at the hit position and add it to the set of anchors. + * @param frame Frame of this update() call + * @param camera Camera of this update() call + */ + private void handleSingleTaps(Frame frame, Camera camera) { + MotionEvent tap = tapHelper.poll(); + if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) { + for (HitResult hit : frame.hitTest(tap)) { + // Check if any plane was hit, and if it was hit inside the plane polygon + Trackable trackable = hit.getTrackable(); + // Creates an anchor if a plane or an oriented point was hit. + if ((trackable instanceof Plane + && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()) + && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) + > 0)) + || (trackable instanceof Point + && ((Point) trackable).getOrientationMode() + == OrientationMode.ESTIMATED_SURFACE_NORMAL)) { + if (anchors.size() >= MAX_NUMBER_DRAWABLES) { + anchors.get(0).detach(); + anchors.remove(0); + } + anchors.add(hit.createAnchor()); + break; + } + } + } + } + + /** + * Given a Frame and a Camera, perform hit tests on stored UI touch events. If a hit test is + * successful, construct an Anchor at the hit position and add it to the set of anchors. + * @param frame Frame of this update() call + * @param camera Camera of this update() call + */ + private void handleHoldTaps(Frame frame, Camera camera) { + // Building finger painting + TapHelper.ScrollEvent holdTap = tapHelper.holdPoll(); + if (holdTap != null && camera.getTrackingState() == TrackingState.TRACKING) { + for (HitResult hit : frame.hitTest(holdTap.e)) { + // Check if any plane was hit, and if it was hit inside the plane polygon + Trackable trackable = hit.getTrackable(); + // Creates an anchor if a plane or an oriented point was hit. + if ((trackable instanceof Plane + && ((Plane) trackable).isPoseInPolygon(hit.getHitPose()) + && (DrawManager.calculateDistanceToPlane(hit.getHitPose(), camera.getPose()) + > 0)) + || (trackable instanceof Point + && ((Point) trackable).getOrientationMode() + == OrientationMode.ESTIMATED_SURFACE_NORMAL)) { + + // Get hit point transform, apply it to the origin --> point is not in hit + // location on the plane + float[] modelMatrix = new float[16]; + hit.getHitPose().toMatrix(modelMatrix, 0); + float[] hitLocation = {0, 0, 0, 1}; + Matrix.multiplyMV(hitLocation, 0, modelMatrix, 0, + hitLocation, 0); + + if (! drawManager.fingerPainting.computeNextPoint(hitLocation, holdTap)) { + // Try to add the next point to the finger painting. If return value + // is false, then keep looping + continue; + } + + break; + } + } + } + } + + /**************************** Drawing helpers ******************************/ // Helper drawing functions that invoke drawManager private void drawPlanes(Canvas canvas, Camera camera) { drawManager.drawPlanes(canvas, camera.getPose(), session.getAllTrackables(Plane.class)); @@ -486,12 +488,11 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re if (anchor.getTrackingState() != TrackingState.TRACKING) { continue; } - // Get the current pose of an Anchor in world space. The Anchor pose is updated - // during calls to session.update() as ARCore refines its estimate of the world. + // Get the current pose of an Anchor in world space anchor.getPose().toMatrix(anchorMatrix, 0); drawManager.modelMatrices.add(0, anchorMatrix); - switch (currentDrawabletype) { + switch (drawManager.currentDrawabletype) { case circle: drawManager.drawCircle(canvas); break; @@ -512,11 +513,12 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re } private void drawFingerPainting(Canvas canvas) { - drawManager.fingerPainting.setSmoothness(drawSmoothPainting); + drawManager.fingerPainting.setSmoothness(drawManager.drawSmoothPainting); drawManager.drawFingerPainting(canvas); } - // Menu functions + /**************************** UI helpers ******************************/ + // Tool bar functions public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); @@ -529,22 +531,22 @@ public class HelloCanvasAR extends AppCompatActivity implements GLSurfaceView.Re drawManager.fingerPainting.reset(); return true; case R.id.smooth_paint: - drawSmoothPainting = true; + drawManager.drawSmoothPainting = true; return true; case R.id.rough_paint: - drawSmoothPainting = false; + drawManager.drawSmoothPainting = false; return true; case R.id.draw_circle: - currentDrawabletype = DrawingType.circle; + drawManager.currentDrawabletype = DrawManager.DrawingType.circle; return true; case R.id.draw_rect: - currentDrawabletype = DrawingType.rect; + drawManager.currentDrawabletype = DrawManager.DrawingType.rect; return true; case R.id.draw_animation: - currentDrawabletype = DrawingType.animation; + drawManager.currentDrawabletype = DrawManager.DrawingType.animation; return true; case R.id.draw_text: - currentDrawabletype = DrawingType.text; + drawManager.currentDrawabletype = DrawManager.DrawingType.text; return true; default: return super.onOptionsItemSelected(item); diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java deleted file mode 100644 index eac2a6bb33..0000000000 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/app/SkARFingerPainting.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.google.skar.examples.helloskar.app; - -import android.graphics.Color; -import android.graphics.Path; -import android.graphics.PointF; -import android.util.Log; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import java.util.ArrayList; - -public class SkARFingerPainting { - // Points obtained by touching the screen. The first point is always brough to (0,0). - // All subsequent points are translated by the same amount. - private ArrayList points = new ArrayList<>(); - private ArrayList jumpPoints = new ArrayList<>(); - private Map indexColors = new HashMap<>(); - private Map pathColors = new HashMap<>(); - private ArrayList paths = new ArrayList<>(); - private int color = Color.RED; - - // Previous point added to the path. This points belongs to the path in local space. - public PointF previousPoint; - - // 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; - - private boolean isSmooth; - - public SkARFingerPainting(boolean smooth) { - this.isSmooth = smooth; - } - - public boolean getSmoothness() { - return isSmooth; - } - - public void setSmoothness(boolean smooth) { - isSmooth = smooth; - } - - // Adds another point to the path in Local space - public void addPoint(PointF p, boolean jumpPoint) { - points.add(p); - if (jumpPoint) { - Log.i("Jumped!", Integer.toString(points.size() - 1)); - jumpPoints.add(points.size() - 1); - indexColors.put(points.size() - 1, color); - } - previousPoint = p; - } - - // Used to build a path before rendering it - public void buildPath() { - paths = new ArrayList<>(); - if (points.size() <= 1) { - return; - } - - - if (isSmooth) { - 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()); - } else { - - int start = 0; - for (int j = 1; j < jumpPoints.size(); j++) { - int finish = jumpPoints.get(j); - buildRoughFromTo(start, finish); - start = finish; - } - - buildRoughFromTo(start, points.size()); - } - } - - 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); - } - paths.add(p); - pathColors.put(p, c); - } - - private void buildSmoothFromTo(int start, int finish) { - Path p = new Path(); - int c = indexColors.get(start); - int nbPts = finish - start; - // If less than 3 points, than draw a line between the two points - if (nbPts <= 2 && nbPts > 1) { - 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, 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); - } - paths.add(p); - pathColors.put(p, c); - } - - public boolean isEmpty() { - return points.isEmpty(); - } - - public float[] getModelMatrix() { - return modelMatrix; - } - - public void setModelMatrix(float[] m) { - modelMatrix = m; - } - - public void setColor(int color) { - this.color = color; - } - - public int getPathColor(Path p) { - return pathColors.get(p); - } - - public ArrayList getPaths() { - return paths; - } - - public void reset() { - points.clear(); - jumpPoints.clear(); - paths.clear(); - pathColors.clear(); - indexColors.clear(); - } -} diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java index f8e9c33af3..9cab82901f 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/CameraPermissionHelper.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.helpers; import android.Manifest; diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java index 7e4ce81c29..0f0609480d 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/DisplayRotationHelper.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.helpers; import android.app.Activity; diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java index 3738b963dc..69514591b2 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/FullScreenHelper.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.helpers; import android.app.Activity; diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java index 02393499e9..6294f93cd6 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/SnackbarHelper.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.helpers; import android.app.Activity; diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java index bcbfe2aa98..6dcfd8fb13 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/helpers/TapHelper.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,19 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.helpers; import android.content.Context; -import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -72,7 +68,9 @@ public final class TapHelper implements OnTouchListener { if (e2.getPointerCount() == 1 && e1.getPointerCount() == 1) { previousScroll = isScrolling; isScrolling = true; - queuedFingerHold.offer(new ScrollEvent(e2, startedScrolling())); + + queuedFingerHold.offer(new ScrollEvent(e2, isStartedScrolling())); + return true; } return false; @@ -97,10 +95,6 @@ public final class TapHelper implements OnTouchListener { public ScrollEvent holdPoll() { return queuedFingerHold.poll(); } - public boolean startedScrolling() { - return isScrolling && !previousScroll; - } - @Override public boolean onTouch(View view, MotionEvent motionEvent) { boolean val = gestureDetector.onTouchEvent(motionEvent); @@ -113,4 +107,5 @@ public final class TapHelper implements OnTouchListener { } return val; } + private boolean isStartedScrolling() { return isScrolling && !previousScroll; } } diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java index b6b5a859b8..acf3dc52d9 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/BackgroundRenderer.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.rendering; import android.content.Context; diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java index 2ef85b049e..35bcf70fec 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/DrawManager.java @@ -1,3 +1,19 @@ +/* + * 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.rendering; import android.content.Context; @@ -21,7 +37,8 @@ import com.google.ar.core.Pose; import com.google.ar.core.TrackingState; import com.google.skar.CanvasMatrixUtil; import com.google.skar.PaintUtil; -import com.google.skar.SkARFingerPainting; +import com.google.skar.examples.helloskar.app.FingerPainting; + import java.io.IOException; import java.nio.FloatBuffer; import java.util.ArrayList; @@ -34,6 +51,14 @@ import java.util.Collection; */ public class DrawManager { + public enum DrawingType { + circle, rect, text, animation + } + + // App defaults + public DrawManager.DrawingType currentDrawabletype = DrawManager.DrawingType.circle; + public boolean drawSmoothPainting = true; + private float[] projectionMatrix = new float[16]; private float[] viewMatrix = new float[16]; private float viewportWidth; @@ -41,7 +66,7 @@ public class DrawManager { private ColorFilter lightFilter; private BitmapShader planeShader; public ArrayList modelMatrices = new ArrayList<>(); - public SkARFingerPainting fingerPainting = new SkARFingerPainting(false); + public FingerPainting fingerPainting = new FingerPainting(false); public void updateViewport(float width, float height) { viewportWidth = width; @@ -165,11 +190,11 @@ public class DrawManager { p.setStrokeWidth(30f); p.setAlpha(120); - for (Path path : fingerPainting.getPaths()) { - if (path.isEmpty()) { + for (FingerPainting.BuiltPath bp : fingerPainting.getPaths()) { + if (bp.path.isEmpty()) { continue; } - p.setColor(fingerPainting.getPathColor(path)); + p.setColor(bp.color); // Scaling issues appear to happen when drawing a Path and transforming the Canvas // directly with a matrix on Android versions less than P. Ideally we would @@ -179,12 +204,12 @@ public class DrawManager { // Transform applied through canvas canvas.save(); canvas.setMatrix(mvpv); - canvas.drawPath(path, p); + canvas.drawPath(bp.path, p); canvas.restore(); } else { // Transform path directly Path pathDst = new Path(); - path.transform(mvpv, pathDst); + bp.path.transform(mvpv, pathDst); // Draw dest path canvas.save(); diff --git a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java index ce33a45966..468b47d690 100644 --- a/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java +++ b/platform_tools/android/apps/skar_java/src/main/java/com/google/skar/examples/helloskar/rendering/ShaderUtil.java @@ -1,10 +1,11 @@ /* - * Copyright 2017 Google Inc. All Rights Reserved. + * 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 + * 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, @@ -12,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.google.skar.examples.helloskar.rendering; import android.content.Context; diff --git a/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml b/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml index d1737c5f36..a08144af30 100644 --- a/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml +++ b/platform_tools/android/apps/skar_java/src/main/res/layout/activity_main.xml @@ -18,14 +18,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - tools:context="com.google.ar.core.examples.java.helloskar.HelloCanvasAR"> + tools:context="com.google.skar.examples.helloskar.app.HelloCanvasAR"> -