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