lecho.lib.hellocharts.computator.ChartComputator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hellocharts-library Show documentation
Show all versions of hellocharts-library Show documentation
Charting library for Android compatible with API 8+(Android 2.2).
package lecho.lib.hellocharts.computator;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import lecho.lib.hellocharts.listener.DummyVieportChangeListener;
import lecho.lib.hellocharts.listener.ViewportChangeListener;
import lecho.lib.hellocharts.model.Viewport;
/**
* Computes raw points coordinates(in pixels), holds content area dimensions and chart viewport.
*/
public class ChartComputator {
/**
* Maximum chart zoom.
*/
protected static final float DEFAULT_MAXIMUM_ZOOM = 20f;
protected float maxZoom = DEFAULT_MAXIMUM_ZOOM;
protected int chartWidth;
protected int chartHeight;
//contentRectMinusAllMargins <= contentRectMinusAxesMargins <= maxContentRect
protected Rect contentRectMinusAllMargins = new Rect();
protected Rect contentRectMinusAxesMargins = new Rect();
protected Rect maxContentRect = new Rect();
/**
* This rectangle represents the currently visible chart values ranges. The currently visible chart X values are
* from this rectangle's left to its right. The currently visible chart Y values are from this rectangle's top to
* its bottom.
*/
protected Viewport currentViewport = new Viewport();
protected Viewport maxViewport = new Viewport();
protected float minViewportWidth;
protected float minViewportHeight;
/**
* Warning! Viewport listener is disabled for all charts beside preview charts to avoid additional method calls
* during animations.
*/
protected ViewportChangeListener viewportChangeListener = new DummyVieportChangeListener();
/**
* Calculates available width and height. Should be called when chart dimensions change. ContentRect is relative to
* chart view not the device's screen.
*/
public void setContentRect(int width, int height, int paddingLeft, int paddingTop, int paddingRight,
int paddingBottom) {
chartWidth = width;
chartHeight = height;
maxContentRect.set(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom);
contentRectMinusAxesMargins.set(maxContentRect);
contentRectMinusAllMargins.set(maxContentRect);
}
public void resetContentRect() {
contentRectMinusAxesMargins.set(maxContentRect);
contentRectMinusAllMargins.set(maxContentRect);
}
public void insetContentRect(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) {
contentRectMinusAxesMargins.left = contentRectMinusAxesMargins.left + deltaLeft;
contentRectMinusAxesMargins.top = contentRectMinusAxesMargins.top + deltaTop;
contentRectMinusAxesMargins.right = contentRectMinusAxesMargins.right - deltaRight;
contentRectMinusAxesMargins.bottom = contentRectMinusAxesMargins.bottom - deltaBottom;
insetContentRectByInternalMargins(deltaLeft, deltaTop, deltaRight, deltaBottom);
}
public void insetContentRectByInternalMargins(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) {
contentRectMinusAllMargins.left = contentRectMinusAllMargins.left + deltaLeft;
contentRectMinusAllMargins.top = contentRectMinusAllMargins.top + deltaTop;
contentRectMinusAllMargins.right = contentRectMinusAllMargins.right - deltaRight;
contentRectMinusAllMargins.bottom = contentRectMinusAllMargins.bottom - deltaBottom;
}
/**
* Checks if new viewport doesn't exceed max available viewport.
*/
public void constrainViewport(float left, float top, float right, float bottom) {
if (right - left < minViewportWidth) {
// Minimum width - constrain horizontal zoom!
right = left + minViewportWidth;
if (left < maxViewport.left) {
left = maxViewport.left;
right = left + minViewportWidth;
} else if (right > maxViewport.right) {
right = maxViewport.right;
left = right - minViewportWidth;
}
}
if (top - bottom < minViewportHeight) {
// Minimum height - constrain vertical zoom!
bottom = top - minViewportHeight;
if (top > maxViewport.top) {
top = maxViewport.top;
bottom = top - minViewportHeight;
} else if (bottom < maxViewport.bottom) {
bottom = maxViewport.bottom;
top = bottom + minViewportHeight;
}
}
currentViewport.left = Math.max(maxViewport.left, left);
currentViewport.top = Math.min(maxViewport.top, top);
currentViewport.right = Math.min(maxViewport.right, right);
currentViewport.bottom = Math.max(maxViewport.bottom, bottom);
viewportChangeListener.onViewportChanged(currentViewport);
}
/**
* Sets the current viewport (defined by {@link #currentViewport}) to the given X and Y positions.
*/
public void setViewportTopLeft(float left, float top) {
/**
* Constrains within the scroll range. The scroll range is simply the viewport extremes (AXIS_X_MAX,
* etc.) minus
* the viewport size. For example, if the extrema were 0 and 10, and the viewport size was 2, the scroll range
* would be 0 to 8.
*/
final float curWidth = currentViewport.width();
final float curHeight = currentViewport.height();
left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - curWidth));
top = Math.max(maxViewport.bottom + curHeight, Math.min(top, maxViewport.top));
constrainViewport(left, top, left + curWidth, top - curHeight);
}
/**
* Translates chart value into raw pixel value. Returned value is absolute pixel X coordinate. If this method
* return
* 0 that means left most pixel of the screen.
*/
public float computeRawX(float valueX) {
// TODO: (contentRectMinusAllMargins.width() / currentViewport.width()) can be recalculated only when viewport
// change.
final float pixelOffset = (valueX - currentViewport.left) * (contentRectMinusAllMargins.width() /
currentViewport.width());
return contentRectMinusAllMargins.left + pixelOffset;
}
/**
* Translates chart value into raw pixel value. Returned value is absolute pixel Y coordinate. If this method
* return
* 0 that means top most pixel of the screen.
*/
public float computeRawY(float valueY) {
final float pixelOffset = (valueY - currentViewport.bottom) * (contentRectMinusAllMargins.height() /
currentViewport.height());
return contentRectMinusAllMargins.bottom - pixelOffset;
}
/**
* Translates viewport distance int pixel distance for X coordinates.
*/
public float computeRawDistanceX(float distance) {
return distance * (contentRectMinusAllMargins.width() / currentViewport.width());
}
/**
* Translates viewport distance int pixel distance for X coordinates.
*/
public float computeRawDistanceY(float distance) {
return distance * (contentRectMinusAllMargins.height() / currentViewport.height());
}
/**
* Finds the chart point (i.e. within the chart's domain and range) represented by the given pixel coordinates, if
* that pixel is within the chart region described by {@link #contentRectMinusAllMargins}. If the point is found,
* the "dest"
* argument is set to the point and this function returns true. Otherwise, this function returns false and
* "dest" is
* unchanged.
*/
public boolean rawPixelsToDataPoint(float x, float y, PointF dest) {
if (!contentRectMinusAllMargins.contains((int) x, (int) y)) {
return false;
}
dest.set(currentViewport.left + (x - contentRectMinusAllMargins.left) * currentViewport.width() /
contentRectMinusAllMargins.width(),
currentViewport.bottom + (y - contentRectMinusAllMargins.bottom) * currentViewport.height() /
-contentRectMinusAllMargins.height());
return true;
}
/**
* Computes the current scrollable surface size, in pixels. For example, if the entire chart area is visible, this
* is simply the current size of {@link #contentRectMinusAllMargins}. If the chart is zoomed in 200% in both
* directions, the
* returned size will be twice as large horizontally and vertically.
*/
public void computeScrollSurfaceSize(Point out) {
out.set((int) (maxViewport.width() * contentRectMinusAllMargins.width() / currentViewport.width()),
(int) (maxViewport.height() * contentRectMinusAllMargins.height() / currentViewport.height()));
}
/**
* Check if given coordinates lies inside contentRectMinusAllMargins.
*/
public boolean isWithinContentRect(float x, float y, float precision) {
if (x >= contentRectMinusAllMargins.left - precision && x <= contentRectMinusAllMargins.right + precision) {
if (y <= contentRectMinusAllMargins.bottom + precision && y >= contentRectMinusAllMargins.top -
precision) {
return true;
}
}
return false;
}
/**
* Returns content rectangle in pixels.
*
* @see #setContentRect(int, int, int, int, int, int)
*/
public Rect getContentRectMinusAllMargins() {
return contentRectMinusAllMargins;
}
/**
* Returns content rectangle with chart internal margins, for example for LineChart contentRectMinusAxesMargins is
* bigger
* than contentRectMinusAllMargins by point radius, thanks to that points are not cut on edges.
*
* @see #setContentRect(int, int, int, int, int, int)
*/
public Rect getContentRectMinusAxesMargins() {
return contentRectMinusAxesMargins;
}
/**
* Returns current chart viewport, returned object is mutable but should not be modified.
*
* @return
*/
public Viewport getCurrentViewport() {
return currentViewport;
}
/**
* Set current viewport to the same values as viewport passed in parameter. This method use deep copy so parameter
* can be safely modified later. Current viewport must be equal or smaller than maximum viewport.
*
* @param viewport
*/
public void setCurrentViewport(Viewport viewport) {
constrainViewport(viewport.left, viewport.top, viewport.right, viewport.bottom);
}
/**
* Set new values for curent viewport, that will change what part of chart is visible. Current viewport must be
* equal or smaller than maximum viewport.
*/
public void setCurrentViewport(float left, float top, float right, float bottom) {
constrainViewport(left, top, right, bottom);
}
/**
* Returns maximum viewport - values ranges extremes.
*/
public Viewport getMaximumViewport() {
return maxViewport;
}
/**
* Set maximum viewport to the same values as viewport passed in parameter. This method use deep copy so parameter
* can be safely modified later.
*
* @param maxViewport
*/
public void setMaxViewport(Viewport maxViewport) {
setMaxViewport(maxViewport.left, maxViewport.top, maxViewport.right, maxViewport.bottom);
}
/**
* Set new values for maximum viewport, that will change what part of chart is visible.
*/
public void setMaxViewport(float left, float top, float right, float bottom) {
this.maxViewport.set(left, top, right, bottom);
computeMinimumWidthAndHeight();
}
/**
* Returns viewport for visible part of chart, for most charts it is equal to current viewport.
*
* @return
*/
public Viewport getVisibleViewport() {
return currentViewport;
}
public void setVisibleViewport(Viewport visibleViewport) {
setCurrentViewport(visibleViewport);
}
public float getMinimumViewportWidth() {
return minViewportWidth;
}
public float getMinimumViewportHeight() {
return minViewportHeight;
}
public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) {
if (null == viewportChangeListener) {
this.viewportChangeListener = new DummyVieportChangeListener();
} else {
this.viewportChangeListener = viewportChangeListener;
}
}
public int getChartWidth() {
return chartWidth;
}
public int getChartHeight() {
return chartHeight;
}
public float getMaxZoom() {
return maxZoom;
}
/**
* Set maximum zoom level, default is 20.
*
* @param maxZoom
*/
public void setMaxZoom(float maxZoom) {
if (maxZoom < 1) {
maxZoom = 1;
}
this.maxZoom = maxZoom;
computeMinimumWidthAndHeight();
setCurrentViewport(currentViewport);
}
private void computeMinimumWidthAndHeight() {
minViewportWidth = this.maxViewport.width() / maxZoom;
minViewportHeight = this.maxViewport.height() / maxZoom;
}
}