1955a016922ea49f154d190b054a202559b41a4d3Jim Miller/* 2955a016922ea49f154d190b054a202559b41a4d3Jim Miller * Copyright (C) 2012 The Android Open Source Project 3955a016922ea49f154d190b054a202559b41a4d3Jim Miller * 4955a016922ea49f154d190b054a202559b41a4d3Jim Miller * Licensed under the Apache License, Version 2.0 (the "License"); 5955a016922ea49f154d190b054a202559b41a4d3Jim Miller * you may not use this file except in compliance with the License. 6955a016922ea49f154d190b054a202559b41a4d3Jim Miller * You may obtain a copy of the License at 7955a016922ea49f154d190b054a202559b41a4d3Jim Miller * 8955a016922ea49f154d190b054a202559b41a4d3Jim Miller * http://www.apache.org/licenses/LICENSE-2.0 9955a016922ea49f154d190b054a202559b41a4d3Jim Miller * 10955a016922ea49f154d190b054a202559b41a4d3Jim Miller * Unless required by applicable law or agreed to in writing, software 11955a016922ea49f154d190b054a202559b41a4d3Jim Miller * distributed under the License is distributed on an "AS IS" BASIS, 12955a016922ea49f154d190b054a202559b41a4d3Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13955a016922ea49f154d190b054a202559b41a4d3Jim Miller * See the License for the specific language governing permissions and 14955a016922ea49f154d190b054a202559b41a4d3Jim Miller * limitations under the License. 15955a016922ea49f154d190b054a202559b41a4d3Jim Miller */ 16955a016922ea49f154d190b054a202559b41a4d3Jim Miller 17955a016922ea49f154d190b054a202559b41a4d3Jim Millerpackage com.android.internal.widget.multiwaveview; 18955a016922ea49f154d190b054a202559b41a4d3Jim Miller 19955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport java.util.ArrayList; 20955a016922ea49f154d190b054a202559b41a4d3Jim Miller 21955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.graphics.Canvas; 22955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.graphics.Color; 23955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.graphics.Paint; 24955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.graphics.drawable.Drawable; 25955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.util.FloatMath; 26955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.util.Log; 27955a016922ea49f154d190b054a202559b41a4d3Jim Miller 28955a016922ea49f154d190b054a202559b41a4d3Jim Millerpublic class PointCloud { 29955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static final float MIN_POINT_SIZE = 2.0f; 30955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static final float MAX_POINT_SIZE = 4.0f; 31955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static final int INNER_POINTS = 8; 32955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static final String TAG = "PointCloud"; 33955a016922ea49f154d190b054a202559b41a4d3Jim Miller private ArrayList<Point> mPointCloud = new ArrayList<Point>(); 34955a016922ea49f154d190b054a202559b41a4d3Jim Miller private Drawable mDrawable; 35955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float mCenterX; 36955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float mCenterY; 37955a016922ea49f154d190b054a202559b41a4d3Jim Miller private Paint mPaint; 38955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float mScale = 1.0f; 39955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static final float PI = (float) Math.PI; 40955a016922ea49f154d190b054a202559b41a4d3Jim Miller 41955a016922ea49f154d190b054a202559b41a4d3Jim Miller // These allow us to have multiple concurrent animations. 42955a016922ea49f154d190b054a202559b41a4d3Jim Miller WaveManager waveManager = new WaveManager(); 43955a016922ea49f154d190b054a202559b41a4d3Jim Miller GlowManager glowManager = new GlowManager(); 44955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float mOuterRadius; 45955a016922ea49f154d190b054a202559b41a4d3Jim Miller 46955a016922ea49f154d190b054a202559b41a4d3Jim Miller public class WaveManager { 47955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float radius = 50; 48955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float width = 200.0f; // TODO: Make configurable 49955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float alpha = 0.0f; 50955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setRadius(float r) { 51955a016922ea49f154d190b054a202559b41a4d3Jim Miller radius = r; 52955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 53955a016922ea49f154d190b054a202559b41a4d3Jim Miller 54955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getRadius() { 55955a016922ea49f154d190b054a202559b41a4d3Jim Miller return radius; 56955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 57955a016922ea49f154d190b054a202559b41a4d3Jim Miller 58955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setAlpha(float a) { 59955a016922ea49f154d190b054a202559b41a4d3Jim Miller alpha = a; 60955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 61955a016922ea49f154d190b054a202559b41a4d3Jim Miller 62955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getAlpha() { 63955a016922ea49f154d190b054a202559b41a4d3Jim Miller return alpha; 64955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 65955a016922ea49f154d190b054a202559b41a4d3Jim Miller }; 66955a016922ea49f154d190b054a202559b41a4d3Jim Miller 67955a016922ea49f154d190b054a202559b41a4d3Jim Miller public class GlowManager { 68955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float x; 69955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float y; 70955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float radius = 0.0f; 71955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float alpha = 0.0f; 72955a016922ea49f154d190b054a202559b41a4d3Jim Miller 73955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setX(float x1) { 74955a016922ea49f154d190b054a202559b41a4d3Jim Miller x = x1; 75955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 76955a016922ea49f154d190b054a202559b41a4d3Jim Miller 77955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getX() { 78955a016922ea49f154d190b054a202559b41a4d3Jim Miller return x; 79955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 80955a016922ea49f154d190b054a202559b41a4d3Jim Miller 81955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setY(float y1) { 82955a016922ea49f154d190b054a202559b41a4d3Jim Miller y = y1; 83955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 84955a016922ea49f154d190b054a202559b41a4d3Jim Miller 85955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getY() { 86955a016922ea49f154d190b054a202559b41a4d3Jim Miller return y; 87955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 88955a016922ea49f154d190b054a202559b41a4d3Jim Miller 89955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setAlpha(float a) { 90955a016922ea49f154d190b054a202559b41a4d3Jim Miller alpha = a; 91955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 92955a016922ea49f154d190b054a202559b41a4d3Jim Miller 93955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getAlpha() { 94955a016922ea49f154d190b054a202559b41a4d3Jim Miller return alpha; 95955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 96955a016922ea49f154d190b054a202559b41a4d3Jim Miller 97955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setRadius(float r) { 98955a016922ea49f154d190b054a202559b41a4d3Jim Miller radius = r; 99955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 100955a016922ea49f154d190b054a202559b41a4d3Jim Miller 101955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getRadius() { 102955a016922ea49f154d190b054a202559b41a4d3Jim Miller return radius; 103955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 104955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 105955a016922ea49f154d190b054a202559b41a4d3Jim Miller 106955a016922ea49f154d190b054a202559b41a4d3Jim Miller class Point { 107955a016922ea49f154d190b054a202559b41a4d3Jim Miller float x; 108955a016922ea49f154d190b054a202559b41a4d3Jim Miller float y; 109955a016922ea49f154d190b054a202559b41a4d3Jim Miller float radius; 110955a016922ea49f154d190b054a202559b41a4d3Jim Miller 111955a016922ea49f154d190b054a202559b41a4d3Jim Miller public Point(float x2, float y2, float r) { 112955a016922ea49f154d190b054a202559b41a4d3Jim Miller x = (float) x2; 113955a016922ea49f154d190b054a202559b41a4d3Jim Miller y = (float) y2; 114955a016922ea49f154d190b054a202559b41a4d3Jim Miller radius = r; 115955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 116955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 117955a016922ea49f154d190b054a202559b41a4d3Jim Miller 118955a016922ea49f154d190b054a202559b41a4d3Jim Miller public PointCloud(Drawable drawable) { 119955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPaint = new Paint(); 120955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPaint.setFilterBitmap(true); 121955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable 122955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPaint.setAntiAlias(true); 123955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPaint.setDither(true); 124955a016922ea49f154d190b054a202559b41a4d3Jim Miller 125955a016922ea49f154d190b054a202559b41a4d3Jim Miller mDrawable = drawable; 126955a016922ea49f154d190b054a202559b41a4d3Jim Miller if (mDrawable != null) { 127955a016922ea49f154d190b054a202559b41a4d3Jim Miller drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); 128955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 129955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 130955a016922ea49f154d190b054a202559b41a4d3Jim Miller 131955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setCenter(float x, float y) { 132955a016922ea49f154d190b054a202559b41a4d3Jim Miller mCenterX = x; 133955a016922ea49f154d190b054a202559b41a4d3Jim Miller mCenterY = y; 134955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 135955a016922ea49f154d190b054a202559b41a4d3Jim Miller 136955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void makePointCloud(float innerRadius, float outerRadius) { 137955a016922ea49f154d190b054a202559b41a4d3Jim Miller if (innerRadius == 0) { 138955a016922ea49f154d190b054a202559b41a4d3Jim Miller Log.w(TAG, "Must specify an inner radius"); 139955a016922ea49f154d190b054a202559b41a4d3Jim Miller return; 140955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 141955a016922ea49f154d190b054a202559b41a4d3Jim Miller mOuterRadius = outerRadius; 142955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPointCloud.clear(); 143955a016922ea49f154d190b054a202559b41a4d3Jim Miller final float pointAreaRadius = (outerRadius - innerRadius); 144955a016922ea49f154d190b054a202559b41a4d3Jim Miller final float ds = (2.0f * PI * innerRadius / INNER_POINTS); 145955a016922ea49f154d190b054a202559b41a4d3Jim Miller final int bands = (int) Math.round(pointAreaRadius / ds); 146955a016922ea49f154d190b054a202559b41a4d3Jim Miller final float dr = pointAreaRadius / bands; 147955a016922ea49f154d190b054a202559b41a4d3Jim Miller float r = innerRadius; 148955a016922ea49f154d190b054a202559b41a4d3Jim Miller for (int b = 0; b <= bands; b++, r += dr) { 149955a016922ea49f154d190b054a202559b41a4d3Jim Miller float circumference = 2.0f * PI * r; 150955a016922ea49f154d190b054a202559b41a4d3Jim Miller final int pointsInBand = (int) (circumference / ds); 151955a016922ea49f154d190b054a202559b41a4d3Jim Miller float eta = PI/2.0f; 152955a016922ea49f154d190b054a202559b41a4d3Jim Miller float dEta = 2.0f * PI / pointsInBand; 153955a016922ea49f154d190b054a202559b41a4d3Jim Miller for (int i = 0; i < pointsInBand; i++) { 154955a016922ea49f154d190b054a202559b41a4d3Jim Miller float x = r * FloatMath.cos(eta); 155955a016922ea49f154d190b054a202559b41a4d3Jim Miller float y = r * FloatMath.sin(eta); 156955a016922ea49f154d190b054a202559b41a4d3Jim Miller eta += dEta; 157955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPointCloud.add(new Point(x, y, r)); 158955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 159955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 160955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 161955a016922ea49f154d190b054a202559b41a4d3Jim Miller 162955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void setScale(float scale) { 163955a016922ea49f154d190b054a202559b41a4d3Jim Miller mScale = scale; 164955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 165955a016922ea49f154d190b054a202559b41a4d3Jim Miller 166955a016922ea49f154d190b054a202559b41a4d3Jim Miller public float getScale() { 167955a016922ea49f154d190b054a202559b41a4d3Jim Miller return mScale; 168955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 169955a016922ea49f154d190b054a202559b41a4d3Jim Miller 170955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static float hypot(float x, float y) { 171955a016922ea49f154d190b054a202559b41a4d3Jim Miller return FloatMath.sqrt(x*x + y*y); 172955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 173955a016922ea49f154d190b054a202559b41a4d3Jim Miller 174955a016922ea49f154d190b054a202559b41a4d3Jim Miller private static float max(float a, float b) { 175955a016922ea49f154d190b054a202559b41a4d3Jim Miller return a > b ? a : b; 176955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 177955a016922ea49f154d190b054a202559b41a4d3Jim Miller 178955a016922ea49f154d190b054a202559b41a4d3Jim Miller public int getAlphaForPoint(Point point) { 179955a016922ea49f154d190b054a202559b41a4d3Jim Miller // Contribution from positional glow 180955a016922ea49f154d190b054a202559b41a4d3Jim Miller float glowDistance = hypot(glowManager.x - point.x, glowManager.y - point.y); 181955a016922ea49f154d190b054a202559b41a4d3Jim Miller float glowAlpha = 0.0f; 182955a016922ea49f154d190b054a202559b41a4d3Jim Miller if (glowDistance < glowManager.radius) { 18320039ad17b94873ee40fdd61c3d1d1aa590010dfJim Miller float cosf = FloatMath.cos(PI * 0.25f * glowDistance / glowManager.radius); 18420039ad17b94873ee40fdd61c3d1d1aa590010dfJim Miller glowAlpha = glowManager.alpha * max(0.0f, (float) Math.pow(cosf, 10.0f)); 185955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 186955a016922ea49f154d190b054a202559b41a4d3Jim Miller 187955a016922ea49f154d190b054a202559b41a4d3Jim Miller // Compute contribution from Wave 188955a016922ea49f154d190b054a202559b41a4d3Jim Miller float radius = hypot(point.x, point.y); 1895892e2ec253465a46b346fc813a21b412ae85e2eJim Miller float distanceToWaveRing = (radius - waveManager.radius); 190955a016922ea49f154d190b054a202559b41a4d3Jim Miller float waveAlpha = 0.0f; 1915892e2ec253465a46b346fc813a21b412ae85e2eJim Miller if (distanceToWaveRing < waveManager.width * 0.5f && distanceToWaveRing < 0.0f) { 1925892e2ec253465a46b346fc813a21b412ae85e2eJim Miller float cosf = FloatMath.cos(PI * 0.25f * distanceToWaveRing / waveManager.width); 19320039ad17b94873ee40fdd61c3d1d1aa590010dfJim Miller waveAlpha = waveManager.alpha * max(0.0f, (float) Math.pow(cosf, 20.0f)); 194955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 195955a016922ea49f154d190b054a202559b41a4d3Jim Miller 196955a016922ea49f154d190b054a202559b41a4d3Jim Miller return (int) (max(glowAlpha, waveAlpha) * 255); 197955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 198955a016922ea49f154d190b054a202559b41a4d3Jim Miller 199955a016922ea49f154d190b054a202559b41a4d3Jim Miller private float interp(float min, float max, float f) { 200955a016922ea49f154d190b054a202559b41a4d3Jim Miller return min + (max - min) * f; 201955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 202955a016922ea49f154d190b054a202559b41a4d3Jim Miller 203955a016922ea49f154d190b054a202559b41a4d3Jim Miller public void draw(Canvas canvas) { 204955a016922ea49f154d190b054a202559b41a4d3Jim Miller ArrayList<Point> points = mPointCloud; 205955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.save(Canvas.MATRIX_SAVE_FLAG); 206955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.scale(mScale, mScale, mCenterX, mCenterY); 207955a016922ea49f154d190b054a202559b41a4d3Jim Miller for (int i = 0; i < points.size(); i++) { 208955a016922ea49f154d190b054a202559b41a4d3Jim Miller Point point = points.get(i); 209955a016922ea49f154d190b054a202559b41a4d3Jim Miller final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE, 210955a016922ea49f154d190b054a202559b41a4d3Jim Miller point.radius / mOuterRadius); 21194754ef36d1ceacb9a335202585e60c82b1f4b0dJim Miller final float px = point.x + mCenterX; 21294754ef36d1ceacb9a335202585e60c82b1f4b0dJim Miller final float py = point.y + mCenterY; 213955a016922ea49f154d190b054a202559b41a4d3Jim Miller int alpha = getAlphaForPoint(point); 214955a016922ea49f154d190b054a202559b41a4d3Jim Miller 215955a016922ea49f154d190b054a202559b41a4d3Jim Miller if (alpha == 0) continue; 216955a016922ea49f154d190b054a202559b41a4d3Jim Miller 217955a016922ea49f154d190b054a202559b41a4d3Jim Miller if (mDrawable != null) { 218955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.save(Canvas.MATRIX_SAVE_FLAG); 21994754ef36d1ceacb9a335202585e60c82b1f4b0dJim Miller final float cx = mDrawable.getIntrinsicWidth() * 0.5f; 22094754ef36d1ceacb9a335202585e60c82b1f4b0dJim Miller final float cy = mDrawable.getIntrinsicHeight() * 0.5f; 22194754ef36d1ceacb9a335202585e60c82b1f4b0dJim Miller final float s = pointSize / MAX_POINT_SIZE; 222955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.scale(s, s, px, py); 22394754ef36d1ceacb9a335202585e60c82b1f4b0dJim Miller canvas.translate(px - cx, py - cy); 224955a016922ea49f154d190b054a202559b41a4d3Jim Miller mDrawable.setAlpha(alpha); 225955a016922ea49f154d190b054a202559b41a4d3Jim Miller mDrawable.draw(canvas); 226955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.restore(); 227955a016922ea49f154d190b054a202559b41a4d3Jim Miller } else { 228955a016922ea49f154d190b054a202559b41a4d3Jim Miller mPaint.setAlpha(alpha); 229955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.drawCircle(px, py, pointSize, mPaint); 230955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 231955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 232955a016922ea49f154d190b054a202559b41a4d3Jim Miller canvas.restore(); 233955a016922ea49f154d190b054a202559b41a4d3Jim Miller } 234955a016922ea49f154d190b054a202559b41a4d3Jim Miller 235955a016922ea49f154d190b054a202559b41a4d3Jim Miller} 236