149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler/* 249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * Copyright (C) 2017 The Android Open Source Project 349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * 449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * except in compliance with the License. You may obtain a copy of the License at 649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * 749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * http://www.apache.org/licenses/LICENSE-2.0 849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * 949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * Unless required by applicable law or agreed to in writing, software distributed under the 1049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * KIND, either express or implied. See the License for the specific language governing 1249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler * permissions and limitations under the License. 1349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler */ 1449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 1549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerpackage com.android.egg.octo; 1649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 1749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.animation.TimeAnimator; 1849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.content.Context; 1949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.Canvas; 2049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.ColorFilter; 2149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.DashPathEffect; 2249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.Matrix; 2349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.Paint; 2449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.Path; 2549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.PixelFormat; 2649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.PointF; 2749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.Rect; 2849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.graphics.drawable.Drawable; 2949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.support.animation.DynamicAnimation; 3049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.support.animation.SpringForce; 3149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.support.annotation.NonNull; 3249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.support.annotation.Nullable; 3349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.support.animation.SpringAnimation; 3449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerimport android.support.animation.FloatValueHolder; 3549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 3649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandlerpublic class OctopusDrawable extends Drawable { 3749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static float BASE_SCALE = 100f; 3849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public static boolean PATH_DEBUG = false; 3949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 4049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static int BODY_COLOR = 0xFF101010; 4149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static int ARM_COLOR = 0xFF101010; 4249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static int ARM_COLOR_BACK = 0xFF000000; 4349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static int EYE_COLOR = 0xFF808080; 4449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 4549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static int[] BACK_ARMS = {1, 3, 4, 6}; 4649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private static int[] FRONT_ARMS = {0, 2, 5, 7}; 4749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 4849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private Paint mPaint = new Paint(); 4949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private Arm[] mArms = new Arm[8]; 5049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final PointF point = new PointF(); 5149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private int mSizePx = 100; 5249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final Matrix M = new Matrix(); 5349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final Matrix M_inv = new Matrix(); 5449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private TimeAnimator mDriftAnimation; 5549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private boolean mBlinking; 5649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private float[] ptmp = new float[2]; 5749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private float[] scaledBounds = new float[2]; 5849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 5949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public static float randfrange(float a, float b) { 6049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return (float) (Math.random()*(b-a) + a); 6149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 6249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public static float clamp(float v, float a, float b) { 6349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return v<a?a:v>b?b:v; 6449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 6549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 6649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public OctopusDrawable(Context context) { 6749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float dp = context.getResources().getDisplayMetrics().density; 6849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler setSizePx((int) (100*dp)); 6949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setAntiAlias(true); 7049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler for (int i=0; i<mArms.length; i++) { 7149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final float bias = (float)i/(mArms.length-1) - 0.5f; 7249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mArms[i] = new Arm( 7349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 0,0, // arm will be repositioned on moveTo 7449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 10f*bias + randfrange(0,20f), randfrange(20f,50f), 7549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 40f*bias+randfrange(-60f,60f), randfrange(30f, 80f), 7649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler randfrange(-40f,40f), randfrange(-80f,40f), 7749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 14f, 2f); 7849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 7949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 8049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 8149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setSizePx(int size) { 8249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mSizePx = size; 8349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler M.setScale(mSizePx/BASE_SCALE, mSizePx/BASE_SCALE); 8449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // TaperedPathStroke.setMinStep(20f*BASE_SCALE/mSizePx); // nice little floaty circles 8549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler TaperedPathStroke.setMinStep(8f*BASE_SCALE/mSizePx); // classic tentacles 8649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler M.invert(M_inv); 8749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 8849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 8949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void startDrift() { 9049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (mDriftAnimation == null) { 9149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mDriftAnimation = new TimeAnimator(); 9249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mDriftAnimation.setTimeListener(new TimeAnimator.TimeListener() { 9349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float MAX_VY = 35f; 9449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float JUMP_VY = -100f; 9549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float MAX_VX = 15f; 9649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private float ax = 0f, ay = 30f; 9749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private float vx, vy; 9849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler long nextjump = 0; 9949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler long unblink = 0; 10049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 10149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void onTimeUpdate(TimeAnimator timeAnimator, long t, long dt) { 10249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float t_sec = 0.001f * t; 10349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float dt_sec = 0.001f * dt; 10449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (t > nextjump) { 10549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler vy = JUMP_VY; 10649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler nextjump = t + (long) randfrange(5000, 10000); 10749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 10849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (unblink > 0 && t > unblink) { 10949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler setBlinking(false); 11049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler unblink = 0; 11149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } else if (Math.random() < 0.001f) { 11249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler setBlinking(true); 11349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler unblink = t + 200; 11449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 11549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 11649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler ax = (float) (MAX_VX * Math.sin(t_sec*.25f)); 11749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 11849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler vx = clamp(vx + dt_sec * ax, -MAX_VX, MAX_VX); 11949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler vy = clamp(vy + dt_sec * ay, -100*MAX_VY, MAX_VY); 12049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 12149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // oob check 12249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (point.y - BASE_SCALE/2 > scaledBounds[1]) { 12349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler vy = JUMP_VY; 12449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } else if (point.y + BASE_SCALE < 0) { 12549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler vy = MAX_VY; 12649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 12749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 12849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.x = clamp(point.x + dt_sec * vx, 0, scaledBounds[0]); 12949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.y = point.y + dt_sec * vy; 13049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 13149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler repositionArms(); 13249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 13349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler }); 13449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 13549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mDriftAnimation.start(); 13649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 13749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 13849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void stopDrift() { 13949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mDriftAnimation.cancel(); 14049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 14149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 14249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 14349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void onBoundsChange(Rect bounds) { 14449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final float w = bounds.width(); 14549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final float h = bounds.height(); 14649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 14749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler lockArms(true); 14849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler moveTo(w/2, h/2); 14949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler lockArms(false); 15049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 15149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler scaledBounds[0] = w; 15249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler scaledBounds[1] = h; 15349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler M_inv.mapPoints(scaledBounds); 15449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 15549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 15649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // real pixel coordinates 15749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void moveTo(float x, float y) { 15849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.x = x; 15949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.y = y; 16049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mapPointF(M_inv, point); 16149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler repositionArms(); 16249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 16349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 16449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public boolean hitTest(float x, float y) { 16549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler ptmp[0] = x; 16649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler ptmp[1] = y; 16749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler M_inv.mapPoints(ptmp); 16849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return Math.hypot(ptmp[0] - point.x, ptmp[1] - point.y) < BASE_SCALE/2; 16949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 17049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 17149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private void lockArms(boolean l) { 17249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler for (Arm arm : mArms) { 17349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler arm.setLocked(l); 17449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 17549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 17649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private void repositionArms() { 17749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler for (int i=0; i<mArms.length; i++) { 17849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final float bias = (float)i/(mArms.length-1) - 0.5f; 17949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mArms[i].setAnchor( 18049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.x+bias*30f,point.y+26f); 18149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 18249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler invalidateSelf(); 18349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 18449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 18549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private void drawPupil(Canvas canvas, float x, float y, float size, boolean open, 18649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler Paint pt) { 18749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final float r = open ? size*.33f : size * .1f; 18849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawRoundRect(x - size, y - r, x + size, y + r, r, r, pt); 18949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 19049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 19149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 19249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void draw(@NonNull Canvas canvas) { 19349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.save(); 19449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler { 19549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.concat(M); 19649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 19749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // arms behind 19849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setColor(ARM_COLOR_BACK); 19949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler for (int i : BACK_ARMS) { 20049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mArms[i].draw(canvas, mPaint); 20149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 20249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 20349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // head/body/thing 20449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setColor(EYE_COLOR); 20549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(point.x, point.y, 36f, mPaint); 20649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setColor(BODY_COLOR); 20749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.save(); 20849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler { 20949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.clipOutRect(point.x - 61f, point.y + 8f, 21049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.x + 61f, point.y + 12f); 21149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawOval(point.x-40f,point.y-60f,point.x+40f,point.y+40f, mPaint); 21249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 21349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.restore(); 21449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 21549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // eyes 21649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setColor(EYE_COLOR); 21749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (mBlinking) { 21849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler drawPupil(canvas, point.x - 16f, point.y - 12f, 6f, false, mPaint); 21949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler drawPupil(canvas, point.x + 16f, point.y - 12f, 6f, false, mPaint); 22049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } else { 22149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(point.x - 16f, point.y - 12f, 6f, mPaint); 22249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(point.x + 16f, point.y - 12f, 6f, mPaint); 22349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 22449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 22549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // too much? 22649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (false) { 22749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setColor(0xFF000000); 22849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler drawPupil(canvas, point.x - 16f, point.y - 12f, 5f, true, mPaint); 22949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler drawPupil(canvas, point.x + 16f, point.y - 12f, 5f, true, mPaint); 23049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 23149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 23249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // arms in front 23349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mPaint.setColor(ARM_COLOR); 23449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler for (int i : FRONT_ARMS) { 23549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mArms[i].draw(canvas, mPaint); 23649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 23749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 23849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (PATH_DEBUG) for (Arm arm : mArms) { 23949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler arm.drawDebug(canvas); 24049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 24149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 24249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.restore(); 24349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 24449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 24549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setBlinking(boolean b) { 24649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler mBlinking = b; 24749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler invalidateSelf(); 24849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 24949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 25049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 25149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setAlpha(int i) { 25249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 25349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 25449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 25549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setColorFilter(@Nullable ColorFilter colorFilter) { 25649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 25749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 25849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 25949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 26049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public int getOpacity() { 26149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return PixelFormat.TRANSLUCENT; 26249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 26349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 26449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler static Path pathMoveTo(Path p, PointF pt) { 26549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler p.moveTo(pt.x, pt.y); 26649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return p; 26749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 26849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler static Path pathQuadTo(Path p, PointF p1, PointF p2) { 26949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler p.quadTo(p1.x, p1.y, p2.x, p2.y); 27049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return p; 27149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 27249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 27349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler static void mapPointF(Matrix m, PointF point) { 27449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float[] p = new float[2]; 27549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler p[0] = point.x; 27649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler p[1] = point.y; 27749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler m.mapPoints(p); 27849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.x = p[0]; 27949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler point.y = p[1]; 28049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 28149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 28249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private class Link // he come to town 28349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler implements DynamicAnimation.OnAnimationUpdateListener { 28449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final FloatValueHolder[] coords = new FloatValueHolder[2]; 28549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final SpringAnimation[] anims = new SpringAnimation[coords.length]; 28649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private float dx, dy; 28749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private boolean locked = false; 28849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler Link next; 28949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 29049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler Link(int index, float x1, float y1, float dx, float dy) { 29149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler coords[0] = new FloatValueHolder(x1); 29249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler coords[1] = new FloatValueHolder(y1); 29349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler this.dx = dx; 29449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler this.dy = dy; 29549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler for (int i=0; i<coords.length; i++) { 29649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler anims[i] = new SpringAnimation(coords[i]); 29749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler anims[i].setSpring(new SpringForce() 29849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) 29949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler .setStiffness( 30049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler index == 0 ? SpringForce.STIFFNESS_LOW 30149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler : index == 1 ? SpringForce.STIFFNESS_VERY_LOW 30249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler : SpringForce.STIFFNESS_VERY_LOW/2) 30349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler .setFinalPosition(0f)); 30449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler anims[i].addUpdateListener(this); 30549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 30649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 30749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setLocked(boolean locked) { 30849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler this.locked = locked; 30949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 31049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public PointF start() { 31149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return new PointF(coords[0].getValue(), coords[1].getValue()); 31249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 31349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public PointF end() { 31449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return new PointF(coords[0].getValue()+dx,coords[1].getValue()+dy); 31549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 31649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public PointF mid() { 31749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return new PointF( 31849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 0.5f*dx+(coords[0].getValue()), 31949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 0.5f*dy+(coords[1].getValue())); 32049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 32149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void animateTo(PointF target) { 32249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (locked) { 32349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler setStart(target.x, target.y); 32449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } else { 32549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler anims[0].animateToFinalPosition(target.x); 32649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler anims[1].animateToFinalPosition(target.y); 32749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 32849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 32949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler @Override 33049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float v, float v1) { 33149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler if (next != null) { 33249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler next.animateTo(end()); 33349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 33449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler OctopusDrawable.this.invalidateSelf(); 33549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 33649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 33749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setStart(float x, float y) { 33849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler coords[0].setValue(x); 33949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler coords[1].setValue(y); 34049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler onAnimationUpdate(null, 0, 0); 34149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 34249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 34349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 34449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private class Arm { 34549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final Link link1, link2, link3; 34649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float max, min; 34749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 34849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public Arm(float x, float y, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3, 34949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler float max, float min) { 35049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1 = new Link(0, x, y, dx1, dy1); 35149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2 = new Link(1, x+dx1, y+dy1, dx2, dy2); 35249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link3 = new Link(2, x+dx1+dx2, y+dy1+dy2, dx3, dy3); 35349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1.next = link2; 35449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.next = link3; 35549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 35649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1.setLocked(true); 35749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.setLocked(false); 35849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link3.setLocked(false); 35949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 36049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler this.max = max; 36149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler this.min = min; 36249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 36349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 36449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler // when the arm is locked, it moves rigidly, without physics 36549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void setLocked(boolean locked) { 36649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.setLocked(locked); 36749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link3.setLocked(locked); 36849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 36949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 37049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private void setAnchor(float x, float y) { 37149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1.setStart(x,y); 37249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 37349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 37449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public Path getPath() { 37549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler Path p = new Path(); 37649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler pathMoveTo(p, link1.start()); 37749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler pathQuadTo(p, link2.start(), link2.mid()); 37849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler pathQuadTo(p, link2.end(), link3.end()); 37949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler return p; 38049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 38149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 38249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void draw(@NonNull Canvas canvas, Paint pt) { 38349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final Path p = getPath(); 38449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler TaperedPathStroke.drawPath(canvas, p, max, min, pt); 38549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 38649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 38749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler private final Paint dpt = new Paint(); 38849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler public void drawDebug(Canvas canvas) { 38949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setStyle(Paint.Style.STROKE); 39049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setStrokeWidth(0.75f); 39149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setStrokeCap(Paint.Cap.ROUND); 39249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 39349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setAntiAlias(true); 39449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setColor(0xFF336699); 39549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 39649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler final Path path = getPath(); 39749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawPath(path, dpt); 39849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 39949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setColor(0xFFFFFF00); 40049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 40149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setPathEffect(new DashPathEffect(new float[] {2f, 2f}, 0f)); 40249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 40349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawLines(new float[] { 40449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1.end().x, link1.end().y, 40549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.start().x, link2.start().y, 40649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 40749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.end().x, link2.end().y, 40849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link3.start().x, link3.start().y, 40949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler }, dpt); 41049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setPathEffect(null); 41149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 41249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setColor(0xFF00CCFF); 41349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 41449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawLines(new float[] { 41549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1.start().x, link1.start().y, 41649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link1.end().x, link1.end().y, 41749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 41849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.start().x, link2.start().y, 41949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link2.end().x, link2.end().y, 42049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 42149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link3.start().x, link3.start().y, 42249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler link3.end().x, link3.end().y, 42349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler }, dpt); 42449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 42549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setColor(0xFFCCEEFF); 42649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(link2.start().x, link2.start().y, 2f, dpt); 42749ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(link3.start().x, link3.start().y, 2f, dpt); 42849ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 42949ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler dpt.setStyle(Paint.Style.FILL_AND_STROKE); 43049ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(link1.start().x, link1.start().y, 2f, dpt); 43149ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(link2.mid().x, link2.mid().y, 2f, dpt); 43249ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler canvas.drawCircle(link3.end().x, link3.end().y, 2f, dpt); 43349ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 43449ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler 43549ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler } 43649ddb0de55020320a4f08bd7060cb531a84fb536Dan Sandler} 437