-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathUtils.java
More file actions
335 lines (271 loc) · 10.4 KB
/
Copy pathUtils.java
File metadata and controls
335 lines (271 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
package com.github.mikephil.charting.utils;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import com.github.mikephil.charting.formatter.DefaultValueFormatter;
import com.github.mikephil.charting.formatter.IValueFormatter;
import androidx.annotation.NonNull;
/**
* Utilities class that has some helper methods. Needs to be initialized by
* calling Utils.init(...) before usage. Inside the Chart.init() method, this is
* done, if the Utils are used before that, Utils.init(...) needs to be called
* manually.
*
* @author Philipp Jahoda
*/
@SuppressWarnings("JavaDoc")
public abstract class Utils {
public static int minimumFlingVelocity = 50;
public static int maximumFlingVelocity = 8000;
public final static double DEG2RAD = (Math.PI / 180.0);
public final static float FDEG2RAD = ((float) Math.PI / 180.f);
@SuppressWarnings("unused")
public final static double DOUBLE_EPSILON = Double.longBitsToDouble(1);
@SuppressWarnings("unused")
public final static float FLOAT_EPSILON = Float.intBitsToFloat(1);
/**
* initialize method, called inside the Chart.init() method.
*/
@SuppressWarnings("deprecation")
public static void init(@NonNull Context context) {
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
minimumFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
maximumFlingVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
}
/**
* calculates the approximate width of a text, depending on a demo text
* avoid repeated calls (e.g. inside drawing methods)
*/
public static int calcTextWidth(Paint paint, String demoText) {
return (int) paint.measureText(demoText);
}
private static final Rect mCalcTextHeightRect = new Rect();
/**
* calculates the approximate height of a text, depending on a demo text
* avoid repeated calls (e.g. inside drawing methods)
*/
public static int calcTextHeight(Paint paint, String demoText) {
Rect r = mCalcTextHeightRect;
r.set(0, 0, 0, 0);
paint.getTextBounds(demoText, 0, demoText.length(), r);
return r.height();
}
private static final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
public static float getLineHeight(Paint paint) {
return getLineHeight(paint, mFontMetrics);
}
public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics) {
paint.getFontMetrics(fontMetrics);
return fontMetrics.descent - fontMetrics.ascent;
}
public static float getLineSpacing(Paint paint) {
return getLineSpacing(paint, mFontMetrics);
}
public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics) {
paint.getFontMetrics(fontMetrics);
return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom;
}
/**
* Returns a recyclable FSize instance.
* calculates the approximate size of a text, depending on a demo text
* avoid repeated calls (e.g. inside drawing methods)
*
* @param paint
* @param demoText
* @return A Recyclable FSize instance
*/
public static FSize calcTextSize(Paint paint, String demoText) {
FSize result = FSize.Companion.getInstance(0, 0);
calcTextSize(paint, demoText, result);
return result;
}
private static final Rect mCalcTextSizeRect = new Rect();
/**
* calculates the approximate size of a text, depending on a demo text
* avoid repeated calls (e.g. inside drawing methods)
*
* @param paint
* @param demoText
* @param outputFSize An output variable, modified by the function.
*/
public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) {
Rect r = mCalcTextSizeRect;
r.set(0, 0, 0, 0);
paint.getTextBounds(demoText, 0, demoText.length(), r);
outputFSize.setWidth(r.width());
outputFSize.setHeight(r.height());
}
/**
* Math.pow(...) is very expensive, so avoid calling it and create it
* yourself.
*/
static final int[] POW_10 = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
};
private static final IValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter();
private static IValueFormatter generateDefaultValueFormatter() {
return new DefaultValueFormatter(1);
}
/// - returns: The default value formatter used for all chart components that needs a default
public static IValueFormatter getDefaultValueFormatter() {
return mDefaultValueFormatter;
}
/**
* Returns a recyclable MPPointF instance.
* Calculates the position around a center point, depending on the distance
* from the center, and the angle of the position around the center.
*
* @param center
* @param dist
* @param angle in degrees, converted to radians internally
* @return
*/
public static MPPointF getPosition(MPPointF center, float dist, float angle) {
MPPointF p = MPPointF.Companion.getInstance(0, 0);
getPosition(center, dist, angle, p);
return p;
}
public static void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) {
outputPoint.setX((float) (center.getX() + dist * Math.cos(Math.toRadians(angle))));
outputPoint.setY((float) (center.getY() + dist * Math.sin(Math.toRadians(angle))));
}
public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev, VelocityTracker tracker) {
// Check the dot product of current velocities.
// If the pointer that left was opposing another velocity vector, clear.
tracker.computeCurrentVelocity(1000, maximumFlingVelocity);
final int upIndex = ev.getActionIndex();
final int id1 = ev.getPointerId(upIndex);
final float x1 = tracker.getXVelocity(id1);
final float y1 = tracker.getYVelocity(id1);
for (int i = 0, count = ev.getPointerCount(); i < count; i++) {
if (i == upIndex) {
continue;
}
final int id2 = ev.getPointerId(i);
final float x = x1 * tracker.getXVelocity(id2);
final float y = y1 * tracker.getYVelocity(id2);
final float dot = x + y;
if (dot < 0) {
tracker.clear();
break;
}
}
}
/**
* returns an angle between 0.f < 360.f (not less than zero, less than 360)
*/
public static float getNormalizedAngle(float angle) {
while (angle < 0.f) {
angle += 360.f;
}
return angle % 360.f;
}
private static final Rect mDrawableBoundsCache = new Rect();
public static void drawImage(Canvas canvas, Drawable drawable, int x, int y) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
MPPointF drawOffset1 = MPPointF.Companion.getInstance();
MPPointF drawOffset2 = MPPointF.Companion.getInstance();
drawOffset2.setX(x - (width / 2));
drawOffset2.setY(y - (height / 2));
drawOffset1.setX(x - (width / 2f));
drawOffset1.setY(y - (height / 2f));
Log.d("drawOffsetOriginal", drawOffset2.toString());
Log.d("drawOffsetCalc", drawOffset1.toString());
drawable.copyBounds(mDrawableBoundsCache);
drawable.setBounds(
mDrawableBoundsCache.left,
mDrawableBoundsCache.top,
mDrawableBoundsCache.left + width,
mDrawableBoundsCache.top + width);
int saveId = canvas.save();
// translate to the correct position and draw
canvas.translate(drawOffset1.getX(), drawOffset1.getY());
drawable.draw(canvas);
canvas.restoreToCount(saveId);
}
private static final Rect mDrawTextRectBuffer = new Rect();
private static final Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics();
public static void drawXAxisValue(Canvas canvas, String text, float x, float y,
Paint paint,
MPPointF anchor, float angleDegrees) {
float drawOffsetX = 0.f;
float drawOffsetY = 0.f;
final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer);
paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer);
// Android sometimes has pre-padding
drawOffsetX -= mDrawTextRectBuffer.left;
// Android does not snap the bounds to line boundaries,
// and draws from bottom to top.
// And we want to normalize it.
drawOffsetY -= mFontMetricsBuffer.ascent;
// To have a consistent point of reference, we always draw left-aligned
Paint.Align originalTextAlign = paint.getTextAlign();
paint.setTextAlign(Paint.Align.LEFT);
if (angleDegrees != 0.f) {
// Move the text drawing rect in a way that it always rotates around its center
drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f;
drawOffsetY -= lineHeight * 0.5f;
float translateX = x;
float translateY = y;
// Move the "outer" rect relative to the anchor, assuming its centered
if (anchor.getX() != 0.5f || anchor.getY() != 0.5f) {
final FSize rotatedSize = getSizeOfRotatedRectangleByDegrees(
mDrawTextRectBuffer.width(),
lineHeight,
angleDegrees);
translateX -= rotatedSize.getWidth() * (anchor.getX() - 0.5f);
translateY -= rotatedSize.getHeight() * (anchor.getY() - 0.5f);
FSize.Companion.recycleInstance(rotatedSize);
}
canvas.save();
canvas.translate(translateX, translateY);
canvas.rotate(angleDegrees);
canvas.drawText(text, drawOffsetX, drawOffsetY, paint);
canvas.restore();
} else {
if (anchor.getX() != 0.f || anchor.getY() != 0.f) {
drawOffsetX -= mDrawTextRectBuffer.width() * anchor.getX();
drawOffsetY -= lineHeight * anchor.getY();
}
drawOffsetX += x;
drawOffsetY += y;
canvas.drawText(text, drawOffsetX, drawOffsetY, paint);
}
paint.setTextAlign(originalTextAlign);
}
/**
* Returns a recyclable FSize instance.
* Represents size of a rotated rectangle by degrees.
*
* @param rectangleWidth
* @param rectangleHeight
* @param degrees
* @return A Recyclable FSize instance
*/
public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float rectangleHeight, float degrees) {
final float radians = degrees * FDEG2RAD;
return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians);
}
/**
* Returns a recyclable FSize instance.
* Represents size of a rotated rectangle by radians.
*
* @param rectangleWidth
* @param rectangleHeight
* @param radians
* @return A Recyclable FSize instance
*/
public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float rectangleHeight, float radians) {
return FSize.Companion.getInstance(
Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight * (float) Math.sin(radians)),
Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight * (float) Math.cos(radians))
);
}
}