1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.incallui.widget.multiwaveview;
18 
19 import android.graphics.Canvas;
20 import android.graphics.Color;
21 import android.graphics.Paint;
22 import android.graphics.drawable.Drawable;
23 import android.util.FloatMath;
24 import android.util.Log;
25 
26 import java.util.ArrayList;
27 
28 public class PointCloud {
29     private static final float MIN_POINT_SIZE = 2.0f;
30     private static final float MAX_POINT_SIZE = 4.0f;
31     private static final int INNER_POINTS = 8;
32     private static final String TAG = "PointCloud";
33     private ArrayList<Point> mPointCloud = new ArrayList<Point>();
34     private Drawable mDrawable;
35     private float mCenterX;
36     private float mCenterY;
37     private Paint mPaint;
38     private float mScale = 1.0f;
39     private static final float PI = (float) Math.PI;
40 
41     // These allow us to have multiple concurrent animations.
42     WaveManager waveManager = new WaveManager();
43     GlowManager glowManager = new GlowManager();
44     private float mOuterRadius;
45 
46     public class WaveManager {
47         private float radius = 50;
48         private float width = 200.0f; // TODO: Make configurable
49         private float alpha = 0.0f;
setRadius(float r)50         public void setRadius(float r) {
51             radius = r;
52         }
53 
getRadius()54         public float getRadius() {
55             return radius;
56         }
57 
setAlpha(float a)58         public void setAlpha(float a) {
59             alpha = a;
60         }
61 
getAlpha()62         public float getAlpha() {
63             return alpha;
64         }
65     };
66 
67     public class GlowManager {
68         private float x;
69         private float y;
70         private float radius = 0.0f;
71         private float alpha = 0.0f;
72 
setX(float x1)73         public void setX(float x1) {
74             x = x1;
75         }
76 
getX()77         public float getX() {
78             return x;
79         }
80 
setY(float y1)81         public void setY(float y1) {
82             y = y1;
83         }
84 
getY()85         public float getY() {
86             return y;
87         }
88 
setAlpha(float a)89         public void setAlpha(float a) {
90             alpha = a;
91         }
92 
getAlpha()93         public float getAlpha() {
94             return alpha;
95         }
96 
setRadius(float r)97         public void setRadius(float r) {
98             radius = r;
99         }
100 
getRadius()101         public float getRadius() {
102             return radius;
103         }
104     }
105 
106     class Point {
107         float x;
108         float y;
109         float radius;
110 
Point(float x2, float y2, float r)111         public Point(float x2, float y2, float r) {
112             x = (float) x2;
113             y = (float) y2;
114             radius = r;
115         }
116     }
117 
PointCloud(Drawable drawable)118     public PointCloud(Drawable drawable) {
119         mPaint = new Paint();
120         mPaint.setFilterBitmap(true);
121         mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable
122         mPaint.setAntiAlias(true);
123         mPaint.setDither(true);
124 
125         mDrawable = drawable;
126         if (mDrawable != null) {
127             drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
128         }
129     }
130 
setCenter(float x, float y)131     public void setCenter(float x, float y) {
132         mCenterX = x;
133         mCenterY = y;
134     }
135 
makePointCloud(float innerRadius, float outerRadius)136     public void makePointCloud(float innerRadius, float outerRadius) {
137         if (innerRadius == 0) {
138             Log.w(TAG, "Must specify an inner radius");
139             return;
140         }
141         mOuterRadius = outerRadius;
142         mPointCloud.clear();
143         final float pointAreaRadius =  (outerRadius - innerRadius);
144         final float ds = (2.0f * PI * innerRadius / INNER_POINTS);
145         final int bands = (int) Math.round(pointAreaRadius / ds);
146         final float dr = pointAreaRadius / bands;
147         float r = innerRadius;
148         for (int b = 0; b <= bands; b++, r += dr) {
149             float circumference = 2.0f * PI * r;
150             final int pointsInBand = (int) (circumference / ds);
151             float eta = PI/2.0f;
152             float dEta = 2.0f * PI / pointsInBand;
153             for (int i = 0; i < pointsInBand; i++) {
154                 float x = r * FloatMath.cos(eta);
155                 float y = r * FloatMath.sin(eta);
156                 eta += dEta;
157                 mPointCloud.add(new Point(x, y, r));
158             }
159         }
160     }
161 
setScale(float scale)162     public void setScale(float scale) {
163         mScale  = scale;
164     }
165 
getScale()166     public float getScale() {
167         return mScale;
168     }
169 
hypot(float x, float y)170     private static float hypot(float x, float y) {
171         return FloatMath.sqrt(x*x + y*y);
172     }
173 
max(float a, float b)174     private static float max(float a, float b) {
175         return a > b ? a : b;
176     }
177 
getAlphaForPoint(Point point)178     public int getAlphaForPoint(Point point) {
179         // Contribution from positional glow
180         float glowDistance = hypot(glowManager.x - point.x, glowManager.y - point.y);
181         float glowAlpha = 0.0f;
182 
183         if (glowDistance < glowManager.radius) {
184             float cosf = FloatMath.cos(PI * 0.25f * glowDistance / glowManager.radius);
185             glowAlpha = glowManager.alpha * max(0.0f, (float) Math.pow(cosf, 10.0f));
186         }
187 
188         // Compute contribution from Wave
189         float radius = hypot(point.x, point.y);
190         float distanceToWaveRing = (radius - waveManager.radius);
191         float waveAlpha = 0.0f;
192         if (distanceToWaveRing < waveManager.width * 0.5f && distanceToWaveRing < 0.0f) {
193             float cosf = FloatMath.cos(PI * 0.25f * distanceToWaveRing / waveManager.width);
194             waveAlpha = waveManager.alpha * max(0.0f, (float) Math.pow(cosf, 20.0f));
195         }
196 
197         return (int) (max(glowAlpha, waveAlpha) * 255);
198     }
199 
interp(float min, float max, float f)200     private float interp(float min, float max, float f) {
201         return min + (max - min) * f;
202     }
203 
draw(Canvas canvas)204     public void draw(Canvas canvas) {
205         ArrayList<Point> points = mPointCloud;
206         canvas.save(Canvas.MATRIX_SAVE_FLAG);
207         canvas.scale(mScale, mScale, mCenterX, mCenterY);
208         for (int i = 0; i < points.size(); i++) {
209             Point point = points.get(i);
210             final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE,
211                     point.radius / mOuterRadius);
212             final float px = point.x + mCenterX;
213             final float py = point.y + mCenterY;
214             int alpha = getAlphaForPoint(point);
215 
216             if (alpha == 0) continue;
217 
218             if (mDrawable != null) {
219                 canvas.save(Canvas.MATRIX_SAVE_FLAG);
220                 final float cx = mDrawable.getIntrinsicWidth() * 0.5f;
221                 final float cy = mDrawable.getIntrinsicHeight() * 0.5f;
222                 final float s = pointSize / MAX_POINT_SIZE;
223                 canvas.scale(s, s, px, py);
224                 canvas.translate(px - cx, py - cy);
225                 mDrawable.setAlpha(alpha);
226                 mDrawable.draw(canvas);
227                 canvas.restore();
228             } else {
229                 mPaint.setAlpha(alpha);
230                 canvas.drawCircle(px, py, pointSize, mPaint);
231             }
232         }
233         canvas.restore();
234     }
235 
236 }
237