• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.github.mikephil.charting.utils;
3 
4 import android.graphics.Matrix;
5 import android.graphics.Path;
6 import android.graphics.RectF;
7 
8 import com.github.mikephil.charting.data.CandleEntry;
9 import com.github.mikephil.charting.data.Entry;
10 import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet;
11 import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet;
12 import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
13 import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet;
14 
15 import java.util.List;
16 
17 /**
18  * Transformer class that contains all matrices and is responsible for
19  * transforming values into pixels on the screen and backwards.
20  *
21  * @author Philipp Jahoda
22  */
23 public class Transformer {
24 
25     /**
26      * matrix to map the values to the screen pixels
27      */
28     protected Matrix mMatrixValueToPx = new Matrix();
29 
30     /**
31      * matrix for handling the different offsets of the chart
32      */
33     protected Matrix mMatrixOffset = new Matrix();
34 
35     protected ViewPortHandler mViewPortHandler;
36 
Transformer(ViewPortHandler viewPortHandler)37     public Transformer(ViewPortHandler viewPortHandler) {
38         this.mViewPortHandler = viewPortHandler;
39     }
40 
41     /**
42      * Prepares the matrix that transforms values to pixels. Calculates the
43      * scale factors from the charts size and offsets.
44      *
45      * @param xChartMin
46      * @param deltaX
47      * @param deltaY
48      * @param yChartMin
49      */
prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin)50     public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) {
51 
52         float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX);
53         float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY);
54 
55         if (Float.isInfinite(scaleX)) {
56             scaleX = 0;
57         }
58         if (Float.isInfinite(scaleY)) {
59             scaleY = 0;
60         }
61 
62         // setup all matrices
63         mMatrixValueToPx.reset();
64         mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin);
65         mMatrixValueToPx.postScale(scaleX, -scaleY);
66     }
67 
68     /**
69      * Prepares the matrix that contains all offsets.
70      *
71      * @param inverted
72      */
prepareMatrixOffset(boolean inverted)73     public void prepareMatrixOffset(boolean inverted) {
74 
75         mMatrixOffset.reset();
76 
77         // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom);
78 
79         if (!inverted)
80             mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(),
81                     mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom());
82         else {
83             mMatrixOffset
84                     .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop());
85             mMatrixOffset.postScale(1.0f, -1.0f);
86         }
87     }
88 
89     protected float[] valuePointsForGenerateTransformedValuesScatter = new float[1];
90 
91     /**
92      * Transforms an List of Entry into a float array containing the x and
93      * y values transformed with all matrices for the SCATTERCHART.
94      *
95      * @param data
96      * @return
97      */
generateTransformedValuesScatter(IScatterDataSet data, float phaseX, float phaseY, int from, int to)98     public float[] generateTransformedValuesScatter(IScatterDataSet data, float phaseX,
99                                                     float phaseY, int from, int to) {
100 
101         final int count = (int) ((to - from) * phaseX + 1) * 2;
102 
103         if (valuePointsForGenerateTransformedValuesScatter.length != count) {
104             valuePointsForGenerateTransformedValuesScatter = new float[count];
105         }
106         float[] valuePoints = valuePointsForGenerateTransformedValuesScatter;
107 
108         for (int j = 0; j < count; j += 2) {
109 
110             Entry e = data.getEntryForIndex(j / 2 + from);
111 
112             if (e != null) {
113                 valuePoints[j] = e.getX();
114                 valuePoints[j + 1] = e.getY() * phaseY;
115             } else {
116                 valuePoints[j] = 0;
117                 valuePoints[j + 1] = 0;
118             }
119         }
120 
121         getValueToPixelMatrix().mapPoints(valuePoints);
122 
123         return valuePoints;
124     }
125 
126     protected float[] valuePointsForGenerateTransformedValuesBubble = new float[1];
127 
128     /**
129      * Transforms an List of Entry into a float array containing the x and
130      * y values transformed with all matrices for the BUBBLECHART.
131      *
132      * @param data
133      * @return
134      */
generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to)135     public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to) {
136 
137         final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2;
138 
139         if (valuePointsForGenerateTransformedValuesBubble.length != count) {
140             valuePointsForGenerateTransformedValuesBubble = new float[count];
141         }
142         float[] valuePoints = valuePointsForGenerateTransformedValuesBubble;
143 
144         for (int j = 0; j < count; j += 2) {
145 
146             Entry e = data.getEntryForIndex(j / 2 + from);
147 
148             if (e != null) {
149                 valuePoints[j] = e.getX();
150                 valuePoints[j + 1] = e.getY() * phaseY;
151             } else {
152                 valuePoints[j] = 0;
153                 valuePoints[j + 1] = 0;
154             }
155         }
156 
157         getValueToPixelMatrix().mapPoints(valuePoints);
158 
159         return valuePoints;
160     }
161 
162     protected float[] valuePointsForGenerateTransformedValuesLine = new float[1];
163 
164     /**
165      * Transforms an List of Entry into a float array containing the x and
166      * y values transformed with all matrices for the LINECHART.
167      *
168      * @param data
169      * @return
170      */
generateTransformedValuesLine(ILineDataSet data, float phaseX, float phaseY, int min, int max)171     public float[] generateTransformedValuesLine(ILineDataSet data,
172                                                  float phaseX, float phaseY,
173                                                  int min, int max) {
174 
175         final int count = ((int) ((max - min) * phaseX) + 1) * 2;
176 
177         if (valuePointsForGenerateTransformedValuesLine.length != count) {
178             valuePointsForGenerateTransformedValuesLine = new float[count];
179         }
180         float[] valuePoints = valuePointsForGenerateTransformedValuesLine;
181 
182         for (int j = 0; j < count; j += 2) {
183 
184             Entry e = data.getEntryForIndex(j / 2 + min);
185 
186             if (e != null) {
187                 valuePoints[j] = e.getX();
188                 valuePoints[j + 1] = e.getY() * phaseY;
189             } else {
190                 valuePoints[j] = 0;
191                 valuePoints[j + 1] = 0;
192             }
193         }
194 
195         getValueToPixelMatrix().mapPoints(valuePoints);
196 
197         return valuePoints;
198     }
199 
200     protected float[] valuePointsForGenerateTransformedValuesCandle = new float[1];
201 
202     /**
203      * Transforms an List of Entry into a float array containing the x and
204      * y values transformed with all matrices for the CANDLESTICKCHART.
205      *
206      * @param data
207      * @return
208      */
generateTransformedValuesCandle(ICandleDataSet data, float phaseX, float phaseY, int from, int to)209     public float[] generateTransformedValuesCandle(ICandleDataSet data,
210                                                    float phaseX, float phaseY, int from, int to) {
211 
212         final int count = (int) ((to - from) * phaseX + 1) * 2;
213 
214         if (valuePointsForGenerateTransformedValuesCandle.length != count) {
215             valuePointsForGenerateTransformedValuesCandle = new float[count];
216         }
217         float[] valuePoints = valuePointsForGenerateTransformedValuesCandle;
218 
219         for (int j = 0; j < count; j += 2) {
220 
221             CandleEntry e = data.getEntryForIndex(j / 2 + from);
222 
223             if (e != null) {
224                 valuePoints[j] = e.getX();
225                 valuePoints[j + 1] = e.getHigh() * phaseY;
226             } else {
227                 valuePoints[j] = 0;
228                 valuePoints[j + 1] = 0;
229             }
230         }
231 
232         getValueToPixelMatrix().mapPoints(valuePoints);
233 
234         return valuePoints;
235     }
236 
237     /**
238      * transform a path with all the given matrices VERY IMPORTANT: keep order
239      * to value-touch-offset
240      *
241      * @param path
242      */
pathValueToPixel(Path path)243     public void pathValueToPixel(Path path) {
244 
245         path.transform(mMatrixValueToPx);
246         path.transform(mViewPortHandler.getMatrixTouch());
247         path.transform(mMatrixOffset);
248     }
249 
250     /**
251      * Transforms multiple paths will all matrices.
252      *
253      * @param paths
254      */
pathValuesToPixel(List<Path> paths)255     public void pathValuesToPixel(List<Path> paths) {
256 
257         for (int i = 0; i < paths.size(); i++) {
258             pathValueToPixel(paths.get(i));
259         }
260     }
261 
262     /**
263      * Transform an array of points with all matrices. VERY IMPORTANT: Keep
264      * matrix order "value-touch-offset" when transforming.
265      *
266      * @param pts
267      */
pointValuesToPixel(float[] pts)268     public void pointValuesToPixel(float[] pts) {
269 
270         mMatrixValueToPx.mapPoints(pts);
271         mViewPortHandler.getMatrixTouch().mapPoints(pts);
272         mMatrixOffset.mapPoints(pts);
273     }
274 
275     /**
276      * Transform a rectangle with all matrices.
277      *
278      * @param r
279      */
rectValueToPixel(RectF r)280     public void rectValueToPixel(RectF r) {
281 
282         mMatrixValueToPx.mapRect(r);
283         mViewPortHandler.getMatrixTouch().mapRect(r);
284         mMatrixOffset.mapRect(r);
285     }
286 
287     /**
288      * Transform a rectangle with all matrices with potential animation phases.
289      *
290      * @param r
291      * @param phaseY
292      */
rectToPixelPhase(RectF r, float phaseY)293     public void rectToPixelPhase(RectF r, float phaseY) {
294 
295         // multiply the height of the rect with the phase
296         r.top *= phaseY;
297         r.bottom *= phaseY;
298 
299         mMatrixValueToPx.mapRect(r);
300         mViewPortHandler.getMatrixTouch().mapRect(r);
301         mMatrixOffset.mapRect(r);
302     }
303 
rectToPixelPhaseHorizontal(RectF r, float phaseY)304     public void rectToPixelPhaseHorizontal(RectF r, float phaseY) {
305 
306         // multiply the height of the rect with the phase
307         r.left *= phaseY;
308         r.right *= phaseY;
309 
310         mMatrixValueToPx.mapRect(r);
311         mViewPortHandler.getMatrixTouch().mapRect(r);
312         mMatrixOffset.mapRect(r);
313     }
314 
315     /**
316      * Transform a rectangle with all matrices with potential animation phases.
317      *
318      * @param r
319      */
rectValueToPixelHorizontal(RectF r)320     public void rectValueToPixelHorizontal(RectF r) {
321 
322         mMatrixValueToPx.mapRect(r);
323         mViewPortHandler.getMatrixTouch().mapRect(r);
324         mMatrixOffset.mapRect(r);
325     }
326 
327     /**
328      * Transform a rectangle with all matrices with potential animation phases.
329      *
330      * @param r
331      * @param phaseY
332      */
rectValueToPixelHorizontal(RectF r, float phaseY)333     public void rectValueToPixelHorizontal(RectF r, float phaseY) {
334 
335         // multiply the height of the rect with the phase
336         r.left *= phaseY;
337         r.right *= phaseY;
338 
339         mMatrixValueToPx.mapRect(r);
340         mViewPortHandler.getMatrixTouch().mapRect(r);
341         mMatrixOffset.mapRect(r);
342     }
343 
344     /**
345      * transforms multiple rects with all matrices
346      *
347      * @param rects
348      */
rectValuesToPixel(List<RectF> rects)349     public void rectValuesToPixel(List<RectF> rects) {
350 
351         Matrix m = getValueToPixelMatrix();
352 
353         for (int i = 0; i < rects.size(); i++)
354             m.mapRect(rects.get(i));
355     }
356 
357     protected Matrix mPixelToValueMatrixBuffer = new Matrix();
358 
359     /**
360      * Transforms the given array of touch positions (pixels) (x, y, x, y, ...)
361      * into values on the chart.
362      *
363      * @param pixels
364      */
pixelsToValue(float[] pixels)365     public void pixelsToValue(float[] pixels) {
366 
367         Matrix tmp = mPixelToValueMatrixBuffer;
368         tmp.reset();
369 
370         // invert all matrixes to convert back to the original value
371         mMatrixOffset.invert(tmp);
372         tmp.mapPoints(pixels);
373 
374         mViewPortHandler.getMatrixTouch().invert(tmp);
375         tmp.mapPoints(pixels);
376 
377         mMatrixValueToPx.invert(tmp);
378         tmp.mapPoints(pixels);
379     }
380 
381     /**
382      * buffer for performance
383      */
384     float[] ptsBuffer = new float[2];
385 
386     /**
387      * Returns a recyclable MPPointD instance.
388      * returns the x and y values in the chart at the given touch point
389      * (encapsulated in a MPPointD). This method transforms pixel coordinates to
390      * coordinates / values in the chart. This is the opposite method to
391      * getPixelForValues(...).
392      *
393      * @param x
394      * @param y
395      * @return
396      */
getValuesByTouchPoint(float x, float y)397     public MPPointD getValuesByTouchPoint(float x, float y) {
398 
399         MPPointD result = MPPointD.getInstance(0, 0);
400         getValuesByTouchPoint(x, y, result);
401         return result;
402     }
403 
getValuesByTouchPoint(float x, float y, MPPointD outputPoint)404     public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint) {
405 
406         ptsBuffer[0] = x;
407         ptsBuffer[1] = y;
408 
409         pixelsToValue(ptsBuffer);
410 
411         outputPoint.x = ptsBuffer[0];
412         outputPoint.y = ptsBuffer[1];
413     }
414 
415     /**
416      * Returns a recyclable MPPointD instance.
417      * Returns the x and y coordinates (pixels) for a given x and y value in the chart.
418      *
419      * @param x
420      * @param y
421      * @return
422      */
getPixelForValues(float x, float y)423     public MPPointD getPixelForValues(float x, float y) {
424 
425         ptsBuffer[0] = x;
426         ptsBuffer[1] = y;
427 
428         pointValuesToPixel(ptsBuffer);
429 
430         double xPx = ptsBuffer[0];
431         double yPx = ptsBuffer[1];
432 
433         return MPPointD.getInstance(xPx, yPx);
434     }
435 
getValueMatrix()436     public Matrix getValueMatrix() {
437         return mMatrixValueToPx;
438     }
439 
getOffsetMatrix()440     public Matrix getOffsetMatrix() {
441         return mMatrixOffset;
442     }
443 
444     private Matrix mMBuffer1 = new Matrix();
445 
getValueToPixelMatrix()446     public Matrix getValueToPixelMatrix() {
447         mMBuffer1.set(mMatrixValueToPx);
448         mMBuffer1.postConcat(mViewPortHandler.mMatrixTouch);
449         mMBuffer1.postConcat(mMatrixOffset);
450         return mMBuffer1;
451     }
452 
453     private Matrix mMBuffer2 = new Matrix();
454 
getPixelToValueMatrix()455     public Matrix getPixelToValueMatrix() {
456         getValueToPixelMatrix().invert(mMBuffer2);
457         return mMBuffer2;
458     }
459 }
460