package lecho.lib.hellocharts.model; import android.os.Parcel; import android.os.Parcelable; /** * Partial copy of android.graphics.Rect but here the top should be greater then the bottom. Viewport holds 4 float * coordinates for a chart extremes. The viewport is represented by the coordinates of its 4 edges (left, top, right * bottom). These fields can be accessed directly. Use width() and height() to retrieve the viewport's width and height. * Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left is less than right and * bottom is less than top). Viewport implements Parcerable. */ public class Viewport implements Parcelable { public float left; public float top; public float right; public float bottom; public static final Parcelable.Creator<Viewport> CREATOR = new Parcelable.Creator<Viewport>() { /** * Return a new viewport from the data in the specified parcel. */ public Viewport createFromParcel(Parcel in) { Viewport v = new Viewport(); v.readFromParcel(in); return v; } /** * Return an array of viewports of the specified size. */ public Viewport[] newArray(int size) { return new Viewport[size]; } }; /** * Create a new empty Viewport. All coordinates are initialized to 0. */ public Viewport() { } /** * Create a new viewport with the specified coordinates. Note: no range checking is performed, so the caller must * ensure that left is less than right and bottom is less than top. * * @param left The X coordinate of the left side of the viewport * @param top The Y coordinate of the top of the viewport * @param right The X coordinate of the right side of the viewport * @param bottom The Y coordinate of the bottom of the viewport */ public Viewport(float left, float top, float right, float bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } /** * Create a new viewport, initialized with the values in the specified viewport (which is left unmodified). * * @param v The viewport whose coordinates are copied into the new viewport. */ public Viewport(Viewport v) { if (v == null) { left = top = right = bottom = 0.0f; } else { left = v.left; top = v.top; right = v.right; bottom = v.bottom; } } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Viewport other = (Viewport) obj; if (Float.floatToIntBits(bottom) != Float.floatToIntBits(other.bottom)) return false; if (Float.floatToIntBits(left) != Float.floatToIntBits(other.left)) return false; if (Float.floatToIntBits(right) != Float.floatToIntBits(other.right)) return false; if (Float.floatToIntBits(top) != Float.floatToIntBits(other.top)) return false; return true; } /** * Returns true if the viewport is empty {@code left >= right or bottom >= top} */ public final boolean isEmpty() { return left >= right || bottom >= top; } /** * Set the viewport to (0,0,0,0) */ public void setEmpty() { left = right = top = bottom = 0; } /** * @return the viewport's width. This does not check for a valid viewport (i.e. {@code left <= right}) so the * result may be negative. */ public final float width() { return right - left; } /** * @return the viewport's height. This does not check for a valid viewport (i.e. {@code top <= bottom}) so the * result may be negative. */ public final float height() { return top - bottom; } /** * @return the horizontal center of the viewport. This does not check for a valid viewport (i.e. {@code left <= * right}) */ public final float centerX() { return (left + right) * 0.5f; } /** * @return the vertical center of the viewport. This does not check for a valid viewport (i.e. {@code bottom <= * top}) */ public final float centerY() { return (top + bottom) * 0.5f; } /** * Set the viewport's coordinates to the specified values. Note: no range checking is performed, so it is up to the * caller to ensure that {@code left <= right and bottom <= top}. * * @param left The X coordinate of the left side of the viewport * @param top The Y coordinate of the top of the viewport * @param right The X coordinate of the right side of the viewport * @param bottom The Y coordinate of the bottom of the viewport */ public void set(float left, float top, float right, float bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } /** * Copy the coordinates from src into this viewport. * * @param src The viewport whose coordinates are copied into this viewport. */ public void set(Viewport src) { this.left = src.left; this.top = src.top; this.right = src.right; this.bottom = src.bottom; } /** * Offset the viewport by adding dx to its left and right coordinates, and adding dy to its top and bottom * coordinates. * * @param dx The amount to add to the viewport's left and right coordinates * @param dy The amount to add to the viewport's top and bottom coordinates */ public void offset(float dx, float dy) { left += dx; top += dy; right += dx; bottom += dy; } /** * Offset the viewport to a specific (left, top) position, keeping its width and height the same. * * @param newLeft The new "left" coordinate for the viewport * @param newTop The new "top" coordinate for the viewport */ public void offsetTo(float newLeft, float newTop) { right += newLeft - left; bottom += newTop - top; left = newLeft; top = newTop; } /** * Inset the viewport by (dx,dy). If dx is positive, then the sides are moved inwards, making the viewport narrower. * If dx is negative, then the sides are moved outwards, making the viewport wider. The same holds true for dy and * the top and bottom. * * @param dx The amount to add(subtract) from the viewport's left(right) * @param dy The amount to add(subtract) from the viewport's top(bottom) */ public void inset(float dx, float dy) { left += dx; top -= dy; right -= dx; bottom += dy; } /** * Returns true if (x,y) is inside the viewport. The left and top are considered to be inside, while the right and * bottom are not. This means that for a x,y to be contained: {@code left <= x < right and bottom <= y < top}. An * empty viewport never contains any point. * * @param x The X coordinate of the point being tested for containment * @param y The Y coordinate of the point being tested for containment * @return true iff (x,y) are contained by the viewport, where containment means {@code left <= x < right and top <= * y < bottom} */ public boolean contains(float x, float y) { return left < right && bottom < top // check for empty first && x >= left && x < right && y >= bottom && y < top; } /** * Returns true iff the 4 specified sides of a viewport are inside or equal to this viewport. i.e. is this viewport * a superset of the specified viewport. An empty viewport never contains another viewport. * * @param left The left side of the viewport being tested for containment * @param top The top of the viewport being tested for containment * @param right The right side of the viewport being tested for containment * @param bottom The bottom of the viewport being tested for containment * @return true iff the the 4 specified sides of a viewport are inside or equal to this viewport */ public boolean contains(float left, float top, float right, float bottom) { // check for empty first return this.left < this.right && this.bottom < this.top // now check for containment && this.left <= left && this.top >= top && this.right >= right && this.bottom <= bottom; } /** * Returns true iff the specified viewport r is inside or equal to this viewport. An empty viewport never contains * another viewport. * * @param v The viewport being tested for containment. * @return true iff the specified viewport r is inside or equal to this viewport */ public boolean contains(Viewport v) { // check for empty first return this.left < this.right && this.bottom < this.top // now check for containment && left <= v.left && top >= v.top && right >= v.right && bottom <= v.bottom; } /** * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is * done. If this viewport is empty it is set to the specified viewport. * * @param left The left edge being unioned with this viewport * @param top The top edge being unioned with this viewport * @param right The right edge being unioned with this viewport * @param bottom The bottom edge being unioned with this viewport */ public void union(float left, float top, float right, float bottom) { if ((left < right) && (bottom < top)) { if ((this.left < this.right) && (this.bottom < this.top)) { if (this.left > left) this.left = left; if (this.top < top) this.top = top; if (this.right < right) this.right = right; if (this.bottom > bottom) this.bottom = bottom; } else { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } } } /** * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is * done. If this viewport is empty it is set to the specified viewport. * * @param v The viewport being unioned with this viewport */ public void union(Viewport v) { union(v.left, v.top, v.right, v.bottom); } /** * If the viewport specified by left,top,right,bottom intersects this viewport, return true and set this viewport to * that intersection, otherwise return false and do not change this viewport. No check is performed to see if either * viewport is empty. Note: To just test for intersection, use intersects() * * @param left The left side of the viewport being intersected with this viewport * @param top The top of the viewport being intersected with this viewport * @param right The right side of the viewport being intersected with this viewport. * @param bottom The bottom of the viewport being intersected with this viewport. * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that * intersection) else return false and do not change this viewport. */ public boolean intersect(float left, float top, float right, float bottom) { if (this.left < right && left < this.right && this.bottom < top && bottom < this.top) { if (this.left < left) { this.left = left; } if (this.top > top) { this.top = top; } if (this.right > right) { this.right = right; } if (this.bottom < bottom) { this.bottom = bottom; } return true; } return false; } /** * If the specified viewport intersects this viewport, return true and set this viewport to that intersection, * otherwise return false and do not change this viewport. No check is performed to see if either viewport is empty. * To just test for intersection, use intersects() * * @param v The viewport being intersected with this viewport. * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that * intersection) else return false and do not change this viewport. */ public boolean intersect(Viewport v) { return intersect(v.left, v.top, v.right, v.bottom); } @Override public String toString() { return "Viewport [left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom + "]"; } // ** PARCERABLE ** @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Float.floatToIntBits(bottom); result = prime * result + Float.floatToIntBits(left); result = prime * result + Float.floatToIntBits(right); result = prime * result + Float.floatToIntBits(top); return result; } /** * Parcelable interface methods */ public int describeContents() { return 0; } /** * Write this viewport to the specified parcel. To restore a viewport from a parcel, use readFromParcel() * * @param out The parcel to write the viewport's coordinates into */ public void writeToParcel(Parcel out, int flags) { out.writeFloat(left); out.writeFloat(top); out.writeFloat(right); out.writeFloat(bottom); } /** * Set the viewport's coordinates from the data stored in the specified parcel. To write a viewport to a parcel, * call writeToParcel(). * * @param in The parcel to read the viewport's coordinates from */ public void readFromParcel(Parcel in) { left = in.readFloat(); top = in.readFloat(); right = in.readFloat(); bottom = in.readFloat(); } }