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 17package com.android.deskclock.widget.multiwaveview; 18 19import android.graphics.Canvas; 20import android.graphics.Color; 21import android.graphics.Paint; 22import android.graphics.drawable.Drawable; 23import android.util.FloatMath; 24import android.util.Log; 25 26import java.util.ArrayList; 27 28public 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; 50 public void setRadius(float r) { 51 radius = r; 52 } 53 54 public float getRadius() { 55 return radius; 56 } 57 58 public void setAlpha(float a) { 59 alpha = a; 60 } 61 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 73 public void setX(float x1) { 74 x = x1; 75 } 76 77 public float getX() { 78 return x; 79 } 80 81 public void setY(float y1) { 82 y = y1; 83 } 84 85 public float getY() { 86 return y; 87 } 88 89 public void setAlpha(float a) { 90 alpha = a; 91 } 92 93 public float getAlpha() { 94 return alpha; 95 } 96 97 public void setRadius(float r) { 98 radius = r; 99 } 100 101 public float getRadius() { 102 return radius; 103 } 104 } 105 106 class Point { 107 float x; 108 float y; 109 float radius; 110 111 public Point(float x2, float y2, float r) { 112 x = (float) x2; 113 y = (float) y2; 114 radius = r; 115 } 116 } 117 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 131 public void setCenter(float x, float y) { 132 mCenterX = x; 133 mCenterY = y; 134 } 135 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 162 public void setScale(float scale) { 163 mScale = scale; 164 } 165 166 public float getScale() { 167 return mScale; 168 } 169 170 private static float hypot(float x, float y) { 171 return FloatMath.sqrt(x*x + y*y); 172 } 173 174 private static float max(float a, float b) { 175 return a > b ? a : b; 176 } 177 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 200 private float interp(float min, float max, float f) { 201 return min + (max - min) * f; 202 } 203 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