/** * Copyright 2008 - 2011 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loon * @author cping * @email:javachenpeng@yahoo.com * @version 0.1 */ package loon.geom; import loon.component.layout.BoxSize; import loon.utils.MathUtils; import loon.utils.StringUtils; public class RectBox extends Shape implements BoxSize { /** * */ private static final long serialVersionUID = 1L; public float maxX() { return x() + width(); } public float maxY() { return y() + height(); } public boolean isEmpty() { return getWidth() <= 0 || height() <= 0; } public void setEmpty() { this.x = 0; this.y = 0; this.width = 0; this.height = 0; } public int width; public int height; public void offset(Vector2f offset) { x += offset.x; y += offset.y; } public void offset(int offsetX, int offsetY) { x += offsetX; y += offsetY; } public void offset(Point point) { x += point.x; y += point.y; } public void offset(PointF point) { x += point.x; y += point.y; } public void offset(PointI point) { x += point.x; y += point.y; } public void add(float px, float py) { float x1 = MathUtils.min(x, px); float x2 = MathUtils.max(x + width, px); float y1 = MathUtils.min(y, py); float y2 = MathUtils.max(y + height, py); setBounds(x1, y1, x2 - x1, y2 - y1); } public void add(Vector2f v) { add(v.x, v.y); } public void add(RectBox r) { int tx2 = this.width; int ty2 = this.height; if ((tx2 | ty2) < 0) { setBounds(r.x, r.y, r.width, r.height); } int rx2 = r.width; int ry2 = r.height; if ((rx2 | ry2) < 0) { return; } float tx1 = this.x; float ty1 = this.y; tx2 += tx1; ty2 += ty1; float rx1 = r.x; float ry1 = r.y; rx2 += rx1; ry2 += ry1; if (tx1 > rx1) { tx1 = rx1; } if (ty1 > ry1) { ty1 = ry1; } if (tx2 < rx2) { tx2 = rx2; } if (ty2 < ry2) { ty2 = ry2; } tx2 -= tx1; ty2 -= ty1; if (tx2 > Integer.MAX_VALUE) { tx2 = Integer.MAX_VALUE; } if (ty2 > Integer.MAX_VALUE) { ty2 = Integer.MAX_VALUE; } setBounds(tx1, ty1, tx2, ty2); } public int Left() { return this.x(); } public int Right() { return (int) (this.x + this.width); } public int Top() { return this.y(); } public int Bottom() { return (int) (this.y + this.height); } public RectBox() { setBounds(0, 0, 0, 0); } public RectBox(int x, int y, int width, int height) { setBounds(x, y, width, height); } public RectBox(float x, float y, float width, float height) { setBounds(x, y, width, height); } public RectBox(double x, double y, double width, double height) { setBounds(x, y, width, height); } public RectBox(RectBox rect) { setBounds(rect.x, rect.y, rect.width, rect.height); } public void setBoundsFromCenter(float centerX, float centerY, float cornerX, float cornerY) { float halfW = MathUtils.abs(cornerX - centerX); float halfH = MathUtils.abs(cornerY - centerY); setBounds(centerX - halfW, centerY - halfH, halfW * 2.0, halfH * 2.0); } public void setBounds(RectBox rect) { setBounds(rect.x, rect.y, rect.width, rect.height); } public void setBounds(double x, double y, double width, double height) { setBounds((float) x, (float) y, (float) width, (float) height); } public void setBounds(float x, float y, float width, float height) { this.x = x; this.y = y; this.width = (int) width; this.height = (int) height; this.minX = x; this.minY = y; this.maxX = x + width; this.maxY = y + height; this.pointsDirty = true; this.checkPoints(); } public void inflate(int horizontalValue, int verticalValue) { this.x -= horizontalValue; this.y -= verticalValue; this.width += horizontalValue * 2; this.height += verticalValue * 2; } public void setLocation(RectBox r) { this.x = r.x; this.y = r.y; } public void setLocation(Point r) { this.x = r.x; this.y = r.y; } public void setLocation(int x, int y) { this.x = x; this.y = y; } public void grow(float h, float v) { setX(getX() - h); setY(getY() - v); setWidth(getWidth() + (h * 2)); setHeight(getHeight() + (v * 2)); } public void scaleGrow(float h, float v) { grow(getWidth() * (h - 1), getHeight() * (v - 1)); } public void setScale(float sx, float sy) { if (scaleX != sx || scaleY != sy) { setSize(width * (scaleX = sx), height * (scaleY * sy)); } } public void setSize(float width, float height) { setWidth(width); setHeight(height); } public boolean overlaps(RectBox rectangle) { return !(x > rectangle.x + rectangle.width || x + width < rectangle.x || y > rectangle.y + rectangle.height || y + height < rectangle.y); } private int _ox, _oy, _ow, _oh; private Matrix4 _matrix; public Matrix4 getMatrix() { if (_matrix == null) { _matrix = new Matrix4(); } if (this._ox != this.x || this._oy != this.y || this._ow != this.width || this._oh != this.height) { return _matrix.setToOrtho2D(this.x, this.y, this.width, this.height); } return _matrix; } public int x() { return (int) x; } public int y() { return (int) y; } public int width() { return width; } public int height() { return height; } public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } public void copy(RectBox other) { this.x = other.x; this.y = other.y; this.width = other.width; this.height = other.height; } @Override public float getMinX() { return getX(); } @Override public float getMinY() { return getY(); } @Override public float getMaxX() { return this.x + this.width; } @Override public float getMaxY() { return this.y + this.height; } public float getRight() { return getMaxX(); } public float getBottom() { return getMaxY(); } public float getMiddleX() { return this.x + this.width / 2; } public float getMiddleY() { return this.y + this.height / 2; } public float getCenterX() { return x + width / 2f; } public float getCenterY() { return y + height / 2f; } public static RectBox getIntersection(RectBox a, RectBox b) { float a_x = a.getX(); float a_r = a.getRight(); float a_y = a.getY(); float a_t = a.getBottom(); float b_x = b.getX(); float b_r = b.getRight(); float b_y = b.getY(); float b_t = b.getBottom(); float i_x = MathUtils.max(a_x, b_x); float i_r = MathUtils.min(a_r, b_r); float i_y = MathUtils.max(a_y, b_y); float i_t = MathUtils.min(a_t, b_t); return i_x < i_r && i_y < i_t ? new RectBox(i_x, i_y, i_r - i_x, i_t - i_y) : null; } public static RectBox getIntersection(RectBox a, RectBox b, RectBox result) { float a_x = a.getX(); float a_r = a.getRight(); float a_y = a.getY(); float a_t = a.getBottom(); float b_x = b.getX(); float b_r = b.getRight(); float b_y = b.getY(); float b_t = b.getBottom(); float i_x = MathUtils.max(a_x, b_x); float i_r = MathUtils.min(a_r, b_r); float i_y = MathUtils.max(a_y, b_y); float i_t = MathUtils.min(a_t, b_t); if (i_x < i_r && i_y < i_t) { result.setBounds(i_x, i_y, i_r - i_x, i_t - i_y); return result; } return null; } public float[] toFloat() { return new float[] { x, y, width, height }; } @Override public RectBox getRect() { return this; } @Override public float getHeight() { return height; } public void setHeight(float height) { this.height = (int) height; } @Override public float getWidth() { return width; } public void setWidth(float width) { this.width = (int) width; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof RectBox) { RectBox rect = (RectBox) obj; return equals(rect.x, rect.y, rect.width, rect.height); } else { return false; } } public boolean equals(float x, float y, float width, float height) { return (this.x == x && this.y == y && this.width == width && this.height == height); } public int getArea() { return width * height; } /** * 检查是否包含指定坐标 * * @param x * @param y * @return */ @Override public boolean contains(float x, float y) { return contains(x, y, 0, 0); } /** * 检查是否包含指定坐标 * * @param x * @param y * @param width * @param height * @return */ public boolean contains(float x, float y, float width, float height) { return (x >= this.x && y >= this.y && ((x + width) <= (this.x + this.width)) && ((y + height) <= (this.y + this.height))); } /** * 检查是否包含指定坐标 * * @param rect * @return */ public boolean contains(RectBox rect) { return contains(rect.x, rect.y, rect.width, rect.height); } public boolean contains(Circle circle) { float xmin = circle.x - circle.radius; float xmax = xmin + 2f * circle.radius; float ymin = circle.y - circle.radius; float ymax = ymin + 2f * circle.radius; return ((xmin > x && xmin < x + width) && (xmax > x && xmax < x + width)) && ((ymin > y && ymin < y + height) && (ymax > y && ymax < y + height)); } public boolean contains(Vector2f v) { return contains(v.x, v.y); } public boolean contains(Point point) { if (this.x < point.x && this.x + this.width > point.x && this.y < point.y && this.y + this.height > point.y) { return true; } return false; } public boolean contains(PointF point) { if (this.x < point.x && this.x + this.width > point.x && this.y < point.y && this.y + this.height > point.y) { return true; } return false; } public boolean contains(PointI point) { if (this.x < point.x && this.x + this.width > point.x && this.y < point.y && this.y + this.height > point.y) { return true; } return false; } /** * 设定矩形选框交集 * * @param rect * @return */ public boolean intersects(RectBox rect) { return intersects(rect.x, rect.y, rect.width, rect.height); } public boolean intersects(float x, float y) { return intersects(0, 0, width, height); } /** * 设定矩形选框交集 * * @param x * @param y * @param width * @param height * @return */ public boolean intersects(float x, float y, float width, float height) { return x + width > this.x && x < this.x + this.width && y + height > this.y && y < this.y + this.height; } /** * 设定矩形选框交集 * * @param rect */ public void intersection(RectBox rect) { intersection(rect.x, rect.y, rect.width, rect.height); } /** * 设定矩形选框交集 * * @param x * @param y * @param width * @param height */ public void intersection(float x, float y, float width, float height) { int x1 = (int) MathUtils.max(this.x, x); int y1 = (int) MathUtils.max(this.y, y); int x2 = (int) MathUtils.min(this.x + this.width - 1, x + width - 1); int y2 = (int) MathUtils.min(this.y + this.height - 1, y + height - 1); setBounds(x1, y1, MathUtils.max(0, x2 - x1 + 1), MathUtils.max(0, y2 - y1 + 1)); } /** * 判定指定坐标是否位于当前RectBox内部 * * @param x * @param y * @return */ public boolean inside(int x, int y) { return (x >= this.x) && ((x - this.x) < this.width) && (y >= this.y) && ((y - this.y) < this.height); } /** * 返回当前的矩形选框交集 * * @param rect * @return */ public RectBox getIntersection(RectBox rect) { int x1 = (int) MathUtils.max(x, rect.x); int x2 = (int) MathUtils.min(x + width, rect.x + rect.width); int y1 = (int) MathUtils.max(y, rect.y); int y2 = (int) MathUtils.min(y + height, rect.y + rect.height); return new RectBox(x1, y1, x2 - x1, y2 - y1); } /** * 合并矩形选框 * * @param rect */ public void union(RectBox rect) { union(rect.x, rect.y, rect.width, rect.height); } /** * 合并矩形选框 * * @param x * @param y * @param width * @param height */ public void union(float x, float y, float width, float height) { int x1 = (int) MathUtils.min(this.x, x); int y1 = (int) MathUtils.min(this.y, y); int x2 = (int) MathUtils.max(this.x + this.width - 1, x + width - 1); int y2 = (int) MathUtils.max(this.y + this.height - 1, y + height - 1); setBounds(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } protected void createPoints() { float useWidth = width; float useHeight = height; points = new float[8]; points[0] = x; points[1] = y; points[2] = x + useWidth; points[3] = y; points[4] = x + useWidth; points[5] = y + useHeight; points[6] = x; points[7] = y + useHeight; maxX = points[2]; maxY = points[5]; minX = points[0]; minY = points[1]; findCenter(); calculateRadius(); } public Shape transform(Matrix3 transform) { checkPoints(); Polygon resultPolygon = new Polygon(); float result[] = new float[points.length]; transform.transform(points, 0, result, 0, points.length / 2); resultPolygon.points = result; resultPolygon.findCenter(); resultPolygon.checkPoints(); return resultPolygon; } /** * 水平移动X坐标执行长度 * * @param xMod */ public final void modX(float xMod) { x += xMod; } /** * 水平移动Y坐标指定长度 * * @param yMod */ public final void modY(float yMod) { y += yMod; } /** * 水平移动Width指定长度 * * @param w */ public void modWidth(float w) { this.width += w; } /** * 水平移动Height指定长度 * * @param h */ public void modHeight(float h) { this.height += h; } /** * 判断指定坐标是否在一条直线上 * * @param x1 * @param y1 * @param x2 * @param y2 * @return */ public final boolean intersectsLine(final float x1, final float y1, final float x2, final float y2) { return contains(x1, y1) || contains(x2, y2); } /** * 判定指定坐标是否位于当前RectBox内部 * * @param x * @param y * @return */ public boolean inside(float x, float y) { return (x >= this.x) && ((x - this.x) < this.width) && (y >= this.y) && ((y - this.y) < this.height); } public RectBox cpy() { return new RectBox(this.x, this.y, this.width, this.height); } public RectBox createIntersection(RectBox rectBox) { RectBox dest = new RectBox(); dest.intersection(rectBox); intersect(this, rectBox, dest); return dest; } public static void intersect(RectBox src1, RectBox src2, RectBox dest) { float x1 = MathUtils.max(src1.getMinX(), src2.getMinX()); float y1 = MathUtils.max(src1.getMinY(), src2.getMinY()); float x2 = MathUtils.min(src1.getMaxX(), src2.getMaxX()); float y2 = MathUtils.min(src1.getMaxY(), src2.getMaxY()); dest.setBounds(x1, y1, x2 - x1, y2 - y1); } @Override public String toString() { return StringUtils.format("RectBox [x:{0},y:{1},width:{2},height:{3}]", x, y, width, height); } }