• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.github.mikephil.charting.utils;
3 
4 import android.graphics.Matrix;
5 import android.graphics.RectF;
6 import android.view.View;
7 
8 /**
9  * Class that contains information about the charts current viewport settings, including offsets, scale & translation
10  * levels, ...
11  *
12  * @author Philipp Jahoda
13  */
14 public class ViewPortHandler {
15 
16     /**
17      * matrix used for touch events
18      */
19     protected final Matrix mMatrixTouch = new Matrix();
20 
21     /**
22      * this rectangle defines the area in which graph values can be drawn
23      */
24     protected RectF mContentRect = new RectF();
25 
26     protected float mChartWidth = 0f;
27     protected float mChartHeight = 0f;
28 
29     /**
30      * minimum scale value on the y-axis
31      */
32     private float mMinScaleY = 1f;
33 
34     /**
35      * maximum scale value on the y-axis
36      */
37     private float mMaxScaleY = Float.MAX_VALUE;
38 
39     /**
40      * minimum scale value on the x-axis
41      */
42     private float mMinScaleX = 1f;
43 
44     /**
45      * maximum scale value on the x-axis
46      */
47     private float mMaxScaleX = Float.MAX_VALUE;
48 
49     /**
50      * contains the current scale factor of the x-axis
51      */
52     private float mScaleX = 1f;
53 
54     /**
55      * contains the current scale factor of the y-axis
56      */
57     private float mScaleY = 1f;
58 
59     /**
60      * current translation (drag distance) on the x-axis
61      */
62     private float mTransX = 0f;
63 
64     /**
65      * current translation (drag distance) on the y-axis
66      */
67     private float mTransY = 0f;
68 
69     /**
70      * offset that allows the chart to be dragged over its bounds on the x-axis
71      */
72     private float mTransOffsetX = 0f;
73 
74     /**
75      * offset that allows the chart to be dragged over its bounds on the x-axis
76      */
77     private float mTransOffsetY = 0f;
78 
79     /**
80      * Constructor - don't forget calling setChartDimens(...)
81      */
ViewPortHandler()82     public ViewPortHandler() {
83 
84     }
85 
86     /**
87      * Sets the width and height of the chart.
88      *
89      * @param width
90      * @param height
91      */
92 
setChartDimens(float width, float height)93     public void setChartDimens(float width, float height) {
94 
95         float offsetLeft = this.offsetLeft();
96         float offsetTop = this.offsetTop();
97         float offsetRight = this.offsetRight();
98         float offsetBottom = this.offsetBottom();
99 
100         mChartHeight = height;
101         mChartWidth = width;
102 
103         restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom);
104     }
105 
hasChartDimens()106     public boolean hasChartDimens() {
107         if (mChartHeight > 0 && mChartWidth > 0)
108             return true;
109         else
110             return false;
111     }
112 
restrainViewPort(float offsetLeft, float offsetTop, float offsetRight, float offsetBottom)113     public void restrainViewPort(float offsetLeft, float offsetTop, float offsetRight,
114                                  float offsetBottom) {
115         mContentRect.set(offsetLeft, offsetTop, mChartWidth - offsetRight, mChartHeight
116                 - offsetBottom);
117     }
118 
offsetLeft()119     public float offsetLeft() {
120         return mContentRect.left;
121     }
122 
offsetRight()123     public float offsetRight() {
124         return mChartWidth - mContentRect.right;
125     }
126 
offsetTop()127     public float offsetTop() {
128         return mContentRect.top;
129     }
130 
offsetBottom()131     public float offsetBottom() {
132         return mChartHeight - mContentRect.bottom;
133     }
134 
contentTop()135     public float contentTop() {
136         return mContentRect.top;
137     }
138 
contentLeft()139     public float contentLeft() {
140         return mContentRect.left;
141     }
142 
contentRight()143     public float contentRight() {
144         return mContentRect.right;
145     }
146 
contentBottom()147     public float contentBottom() {
148         return mContentRect.bottom;
149     }
150 
contentWidth()151     public float contentWidth() {
152         return mContentRect.width();
153     }
154 
contentHeight()155     public float contentHeight() {
156         return mContentRect.height();
157     }
158 
getContentRect()159     public RectF getContentRect() {
160         return mContentRect;
161     }
162 
getContentCenter()163     public MPPointF getContentCenter() {
164         return MPPointF.getInstance(mContentRect.centerX(), mContentRect.centerY());
165     }
166 
getChartHeight()167     public float getChartHeight() {
168         return mChartHeight;
169     }
170 
getChartWidth()171     public float getChartWidth() {
172         return mChartWidth;
173     }
174 
175     /**
176      * Returns the smallest extension of the content rect (width or height).
177      *
178      * @return
179      */
getSmallestContentExtension()180     public float getSmallestContentExtension() {
181         return Math.min(mContentRect.width(), mContentRect.height());
182     }
183 
184     /**
185      * ################ ################ ################ ################
186      */
187     /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */
188 
189     /**
190      * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom
191      * center.
192      *
193      * @param x
194      * @param y
195      */
zoomIn(float x, float y)196     public Matrix zoomIn(float x, float y) {
197 
198         Matrix save = new Matrix();
199         zoomIn(x, y, save);
200         return save;
201     }
202 
zoomIn(float x, float y, Matrix outputMatrix)203     public void zoomIn(float x, float y, Matrix outputMatrix) {
204         outputMatrix.reset();
205         outputMatrix.set(mMatrixTouch);
206         outputMatrix.postScale(1.4f, 1.4f, x, y);
207     }
208 
209     /**
210      * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom
211      * center.
212      */
zoomOut(float x, float y)213     public Matrix zoomOut(float x, float y) {
214 
215         Matrix save = new Matrix();
216         zoomOut(x, y, save);
217         return save;
218     }
219 
zoomOut(float x, float y, Matrix outputMatrix)220     public void zoomOut(float x, float y, Matrix outputMatrix) {
221         outputMatrix.reset();
222         outputMatrix.set(mMatrixTouch);
223         outputMatrix.postScale(0.7f, 0.7f, x, y);
224     }
225 
226     /**
227      * Zooms out to original size.
228      * @param outputMatrix
229      */
resetZoom(Matrix outputMatrix)230     public void resetZoom(Matrix outputMatrix) {
231         outputMatrix.reset();
232         outputMatrix.set(mMatrixTouch);
233         outputMatrix.postScale(1.0f, 1.0f, 0.0f, 0.0f);
234     }
235 
236     /**
237      * Post-scales by the specified scale factors.
238      *
239      * @param scaleX
240      * @param scaleY
241      * @return
242      */
zoom(float scaleX, float scaleY)243     public Matrix zoom(float scaleX, float scaleY) {
244 
245         Matrix save = new Matrix();
246         zoom(scaleX, scaleY, save);
247         return save;
248     }
249 
zoom(float scaleX, float scaleY, Matrix outputMatrix)250     public void zoom(float scaleX, float scaleY, Matrix outputMatrix) {
251         outputMatrix.reset();
252         outputMatrix.set(mMatrixTouch);
253         outputMatrix.postScale(scaleX, scaleY);
254     }
255 
256     /**
257      * Post-scales by the specified scale factors. x and y is pivot.
258      *
259      * @param scaleX
260      * @param scaleY
261      * @param x
262      * @param y
263      * @return
264      */
zoom(float scaleX, float scaleY, float x, float y)265     public Matrix zoom(float scaleX, float scaleY, float x, float y) {
266 
267         Matrix save = new Matrix();
268         zoom(scaleX, scaleY, x, y, save);
269         return save;
270     }
271 
zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix)272     public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix) {
273         outputMatrix.reset();
274         outputMatrix.set(mMatrixTouch);
275         outputMatrix.postScale(scaleX, scaleY, x, y);
276     }
277 
278     /**
279      * Sets the scale factor to the specified values.
280      *
281      * @param scaleX
282      * @param scaleY
283      * @return
284      */
setZoom(float scaleX, float scaleY)285     public Matrix setZoom(float scaleX, float scaleY) {
286 
287         Matrix save = new Matrix();
288         setZoom(scaleX, scaleY, save);
289         return save;
290     }
291 
setZoom(float scaleX, float scaleY, Matrix outputMatrix)292     public void setZoom(float scaleX, float scaleY, Matrix outputMatrix) {
293         outputMatrix.reset();
294         outputMatrix.set(mMatrixTouch);
295         outputMatrix.setScale(scaleX, scaleY);
296     }
297 
298     /**
299      * Sets the scale factor to the specified values. x and y is pivot.
300      *
301      * @param scaleX
302      * @param scaleY
303      * @param x
304      * @param y
305      * @return
306      */
setZoom(float scaleX, float scaleY, float x, float y)307     public Matrix setZoom(float scaleX, float scaleY, float x, float y) {
308 
309         Matrix save = new Matrix();
310         save.set(mMatrixTouch);
311 
312         save.setScale(scaleX, scaleY, x, y);
313 
314         return save;
315     }
316 
317     protected float[] valsBufferForFitScreen = new float[9];
318 
319     /**
320      * Resets all zooming and dragging and makes the chart fit exactly it's
321      * bounds.
322      */
fitScreen()323     public Matrix fitScreen() {
324 
325         Matrix save = new Matrix();
326         fitScreen(save);
327         return save;
328     }
329 
330     /**
331      * Resets all zooming and dragging and makes the chart fit exactly it's
332      * bounds.  Output Matrix is available for those who wish to cache the object.
333      */
fitScreen(Matrix outputMatrix)334     public void fitScreen(Matrix outputMatrix) {
335         mMinScaleX = 1f;
336         mMinScaleY = 1f;
337 
338         outputMatrix.set(mMatrixTouch);
339 
340         float[] vals = valsBufferForFitScreen;
341         for (int i = 0; i < 9; i++) {
342             vals[i] = 0;
343         }
344 
345         outputMatrix.getValues(vals);
346 
347         // reset all translations and scaling
348         vals[Matrix.MTRANS_X] = 0f;
349         vals[Matrix.MTRANS_Y] = 0f;
350         vals[Matrix.MSCALE_X] = 1f;
351         vals[Matrix.MSCALE_Y] = 1f;
352 
353         outputMatrix.setValues(vals);
354     }
355 
356     /**
357      * Post-translates to the specified points.  Less Performant.
358      *
359      * @param transformedPts
360      * @return
361      */
translate(final float[] transformedPts)362     public Matrix translate(final float[] transformedPts) {
363 
364         Matrix save = new Matrix();
365         translate(transformedPts, save);
366         return save;
367     }
368 
369     /**
370      * Post-translates to the specified points.  Output matrix allows for caching objects.
371      *
372      * @param transformedPts
373      * @return
374      */
translate(final float[] transformedPts, Matrix outputMatrix)375     public void translate(final float[] transformedPts, Matrix outputMatrix) {
376         outputMatrix.reset();
377         outputMatrix.set(mMatrixTouch);
378         final float x = transformedPts[0] - offsetLeft();
379         final float y = transformedPts[1] - offsetTop();
380         outputMatrix.postTranslate(-x, -y);
381     }
382 
383     protected Matrix mCenterViewPortMatrixBuffer = new Matrix();
384 
385     /**
386      * Centers the viewport around the specified position (x-index and y-value)
387      * in the chart. Centering the viewport outside the bounds of the chart is
388      * not possible. Makes most sense in combination with the
389      * setScaleMinima(...) method.
390      *
391      * @param transformedPts the position to center view viewport to
392      * @param view
393      * @return save
394      */
centerViewPort(final float[] transformedPts, final View view)395     public void centerViewPort(final float[] transformedPts, final View view) {
396 
397         Matrix save = mCenterViewPortMatrixBuffer;
398         save.reset();
399         save.set(mMatrixTouch);
400 
401         final float x = transformedPts[0] - offsetLeft();
402         final float y = transformedPts[1] - offsetTop();
403 
404         save.postTranslate(-x, -y);
405 
406         refresh(save, view, true);
407     }
408 
409     /**
410      * buffer for storing the 9 matrix values of a 3x3 matrix
411      */
412     protected final float[] matrixBuffer = new float[9];
413 
414     /**
415      * call this method to refresh the graph with a given matrix
416      *
417      * @param newMatrix
418      * @return
419      */
refresh(Matrix newMatrix, View chart, boolean invalidate)420     public Matrix refresh(Matrix newMatrix, View chart, boolean invalidate) {
421 
422         mMatrixTouch.set(newMatrix);
423 
424         // make sure scale and translation are within their bounds
425         limitTransAndScale(mMatrixTouch, mContentRect);
426 
427         if (invalidate)
428             chart.invalidate();
429 
430         newMatrix.set(mMatrixTouch);
431         return newMatrix;
432     }
433 
434     /**
435      * limits the maximum scale and X translation of the given matrix
436      *
437      * @param matrix
438      */
limitTransAndScale(Matrix matrix, RectF content)439     public void limitTransAndScale(Matrix matrix, RectF content) {
440 
441         matrix.getValues(matrixBuffer);
442 
443         float curTransX = matrixBuffer[Matrix.MTRANS_X];
444         float curScaleX = matrixBuffer[Matrix.MSCALE_X];
445 
446         float curTransY = matrixBuffer[Matrix.MTRANS_Y];
447         float curScaleY = matrixBuffer[Matrix.MSCALE_Y];
448 
449         // min scale-x is 1f
450         mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX);
451 
452         // min scale-y is 1f
453         mScaleY = Math.min(Math.max(mMinScaleY, curScaleY), mMaxScaleY);
454 
455         float width = 0f;
456         float height = 0f;
457 
458         if (content != null) {
459             width = content.width();
460             height = content.height();
461         }
462 
463         float maxTransX = -width * (mScaleX - 1f);
464         mTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX);
465 
466         float maxTransY = height * (mScaleY - 1f);
467         mTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY);
468 
469         matrixBuffer[Matrix.MTRANS_X] = mTransX;
470         matrixBuffer[Matrix.MSCALE_X] = mScaleX;
471 
472         matrixBuffer[Matrix.MTRANS_Y] = mTransY;
473         matrixBuffer[Matrix.MSCALE_Y] = mScaleY;
474 
475         matrix.setValues(matrixBuffer);
476     }
477 
478     /**
479      * Sets the minimum scale factor for the x-axis
480      *
481      * @param xScale
482      */
setMinimumScaleX(float xScale)483     public void setMinimumScaleX(float xScale) {
484 
485         if (xScale < 1f)
486             xScale = 1f;
487 
488         mMinScaleX = xScale;
489 
490         limitTransAndScale(mMatrixTouch, mContentRect);
491     }
492 
493     /**
494      * Sets the maximum scale factor for the x-axis
495      *
496      * @param xScale
497      */
setMaximumScaleX(float xScale)498     public void setMaximumScaleX(float xScale) {
499 
500         if (xScale == 0.f)
501             xScale = Float.MAX_VALUE;
502 
503         mMaxScaleX = xScale;
504 
505         limitTransAndScale(mMatrixTouch, mContentRect);
506     }
507 
508     /**
509      * Sets the minimum and maximum scale factors for the x-axis
510      *
511      * @param minScaleX
512      * @param maxScaleX
513      */
setMinMaxScaleX(float minScaleX, float maxScaleX)514     public void setMinMaxScaleX(float minScaleX, float maxScaleX) {
515 
516         if (minScaleX < 1f)
517             minScaleX = 1f;
518 
519         if (maxScaleX == 0.f)
520             maxScaleX = Float.MAX_VALUE;
521 
522         mMinScaleX = minScaleX;
523         mMaxScaleX = maxScaleX;
524 
525         limitTransAndScale(mMatrixTouch, mContentRect);
526     }
527 
528     /**
529      * Sets the minimum scale factor for the y-axis
530      *
531      * @param yScale
532      */
setMinimumScaleY(float yScale)533     public void setMinimumScaleY(float yScale) {
534 
535         if (yScale < 1f)
536             yScale = 1f;
537 
538         mMinScaleY = yScale;
539 
540         limitTransAndScale(mMatrixTouch, mContentRect);
541     }
542 
543     /**
544      * Sets the maximum scale factor for the y-axis
545      *
546      * @param yScale
547      */
setMaximumScaleY(float yScale)548     public void setMaximumScaleY(float yScale) {
549 
550         if (yScale == 0.f)
551             yScale = Float.MAX_VALUE;
552 
553         mMaxScaleY = yScale;
554 
555         limitTransAndScale(mMatrixTouch, mContentRect);
556     }
557 
setMinMaxScaleY(float minScaleY, float maxScaleY)558     public void setMinMaxScaleY(float minScaleY, float maxScaleY) {
559 
560         if (minScaleY < 1f)
561             minScaleY = 1f;
562 
563         if (maxScaleY == 0.f)
564             maxScaleY = Float.MAX_VALUE;
565 
566         mMinScaleY = minScaleY;
567         mMaxScaleY = maxScaleY;
568 
569         limitTransAndScale(mMatrixTouch, mContentRect);
570     }
571 
572     /**
573      * Returns the charts-touch matrix used for translation and scale on touch.
574      *
575      * @return
576      */
getMatrixTouch()577     public Matrix getMatrixTouch() {
578         return mMatrixTouch;
579     }
580 
581     /**
582      * ################ ################ ################ ################
583      */
584     /**
585      * BELOW METHODS FOR BOUNDS CHECK
586      */
587 
isInBoundsX(float x)588     public boolean isInBoundsX(float x) {
589         return isInBoundsLeft(x) && isInBoundsRight(x);
590     }
591 
isInBoundsY(float y)592     public boolean isInBoundsY(float y) {
593         return isInBoundsTop(y) && isInBoundsBottom(y);
594     }
595 
isInBounds(float x, float y)596     public boolean isInBounds(float x, float y) {
597         return isInBoundsX(x) && isInBoundsY(y);
598     }
599 
isInBoundsLeft(float x)600     public boolean isInBoundsLeft(float x) {
601         return mContentRect.left <= x + 1;
602     }
603 
isInBoundsRight(float x)604     public boolean isInBoundsRight(float x) {
605         x = (float) ((int) (x * 100.f)) / 100.f;
606         return mContentRect.right >= x - 1;
607     }
608 
isInBoundsTop(float y)609     public boolean isInBoundsTop(float y) {
610         return mContentRect.top <= y;
611     }
612 
isInBoundsBottom(float y)613     public boolean isInBoundsBottom(float y) {
614         y = (float) ((int) (y * 100.f)) / 100.f;
615         return mContentRect.bottom >= y;
616     }
617 
618     /**
619      * returns the current x-scale factor
620      */
getScaleX()621     public float getScaleX() {
622         return mScaleX;
623     }
624 
625     /**
626      * returns the current y-scale factor
627      */
getScaleY()628     public float getScaleY() {
629         return mScaleY;
630     }
631 
getMinScaleX()632     public float getMinScaleX() {
633         return mMinScaleX;
634     }
635 
getMaxScaleX()636     public float getMaxScaleX() {
637         return mMaxScaleX;
638     }
639 
getMinScaleY()640     public float getMinScaleY() {
641         return mMinScaleY;
642     }
643 
getMaxScaleY()644     public float getMaxScaleY() {
645         return mMaxScaleY;
646     }
647 
648     /**
649      * Returns the translation (drag / pan) distance on the x-axis
650      *
651      * @return
652      */
getTransX()653     public float getTransX() {
654         return mTransX;
655     }
656 
657     /**
658      * Returns the translation (drag / pan) distance on the y-axis
659      *
660      * @return
661      */
getTransY()662     public float getTransY() {
663         return mTransY;
664     }
665 
666     /**
667      * if the chart is fully zoomed out, return true
668      *
669      * @return
670      */
isFullyZoomedOut()671     public boolean isFullyZoomedOut() {
672 
673         return isFullyZoomedOutX() && isFullyZoomedOutY();
674     }
675 
676     /**
677      * Returns true if the chart is fully zoomed out on it's y-axis (vertical).
678      *
679      * @return
680      */
isFullyZoomedOutY()681     public boolean isFullyZoomedOutY() {
682         return !(mScaleY > mMinScaleY || mMinScaleY > 1f);
683     }
684 
685     /**
686      * Returns true if the chart is fully zoomed out on it's x-axis
687      * (horizontal).
688      *
689      * @return
690      */
isFullyZoomedOutX()691     public boolean isFullyZoomedOutX() {
692         return !(mScaleX > mMinScaleX || mMinScaleX > 1f);
693     }
694 
695     /**
696      * Set an offset in dp that allows the user to drag the chart over it's
697      * bounds on the x-axis.
698      *
699      * @param offset
700      */
setDragOffsetX(float offset)701     public void setDragOffsetX(float offset) {
702         mTransOffsetX = Utils.convertDpToPixel(offset);
703     }
704 
705     /**
706      * Set an offset in dp that allows the user to drag the chart over it's
707      * bounds on the y-axis.
708      *
709      * @param offset
710      */
setDragOffsetY(float offset)711     public void setDragOffsetY(float offset) {
712         mTransOffsetY = Utils.convertDpToPixel(offset);
713     }
714 
715     /**
716      * Returns true if both drag offsets (x and y) are zero or smaller.
717      *
718      * @return
719      */
hasNoDragOffset()720     public boolean hasNoDragOffset() {
721         return mTransOffsetX <= 0 && mTransOffsetY <= 0;
722     }
723 
724     /**
725      * Returns true if the chart is not yet fully zoomed out on the x-axis
726      *
727      * @return
728      */
canZoomOutMoreX()729     public boolean canZoomOutMoreX() {
730         return mScaleX > mMinScaleX;
731     }
732 
733     /**
734      * Returns true if the chart is not yet fully zoomed in on the x-axis
735      *
736      * @return
737      */
canZoomInMoreX()738     public boolean canZoomInMoreX() {
739         return mScaleX < mMaxScaleX;
740     }
741 
742     /**
743      * Returns true if the chart is not yet fully zoomed out on the y-axis
744      *
745      * @return
746      */
canZoomOutMoreY()747     public boolean canZoomOutMoreY() {
748         return mScaleY > mMinScaleY;
749     }
750 
751     /**
752      * Returns true if the chart is not yet fully zoomed in on the y-axis
753      *
754      * @return
755      */
canZoomInMoreY()756     public boolean canZoomInMoreY() {
757         return mScaleY < mMaxScaleY;
758     }
759 }
760