使用左下角点、宽度和高度,找到两个矩形的交集的Java方法?

12

我已经找到了解决方案,但想确保我的逻辑是最有效的。我感觉有更好的方法。我有两个矩形的左下角坐标、高度和宽度,并且需要返回一个它们相交的第三个矩形。我不想贴出代码,因为我觉得那是作弊。

  1. 我确定图表中最左侧和最高的图形。
  2. 我检查一个是否完全重叠在另一个上,然后反向检查另一个是否完全重叠在第一个矩形的 X 轴上。
  3. 我检查 X 轴上的部分交集。
  4. 我基本上重复步骤 2 和 3 来处理 Y 轴。
  5. 我进行一些数学计算并根据这些条件获取矩形的点。

我可能过于思考和编写低效的代码。我已经提交了一个可工作的程序,但希望能找到最好的方式以增加自己的知识。如果有人可以同意或指引我正确的方向,那就太好了!


我不知道这是否有帮助,但它涉及确定移动物体的碰撞点... 示例 - MadProgrammer
非常感谢!我认为那可能与此相关。我在互联网上找不到任何能将其缩小到我具体示例的东西。希望我所做的足够高效。谢谢! - Doug B
3个回答

21

为什么不使用JDK API来代替您完成这个任务呢?

Rectangle rect1 = new Rectangle(100, 100, 200, 240);
Rectangle rect2 = new Rectangle(120, 80, 80, 120);
Rectangle intersection = rect1.intersection(rect2);

要使用 java.awt.Rectangle 类,构造函数的参数为:x,y,width,height,其中 x,y 是矩形的左上角。您可以轻松地将左下点转换为左上角。


我建议使用上述方法,但如果您真的想自己做,可以按照以下步骤操作:

(x1, y1),(x2, y2) 分别是 Rect1 的左下角和右下角,(x3, y3),(x4, y4) 分别是 Rect2 的左下角和右下角。

  • 找到 x1x3 中较大的一个,x2x4 中较小的一个,分别记作 xLxR
    • 如果 xL >= xR,则返回无交集
  • 找到 y1y3 中较大的一个,y2y4 中较小的一个,分别记作 yTyB
    • 如果 yT >= yB,则返回无交集
    • 返回 (xL,yB,xR-xL,yB-yT)

更具 Java 风格的伪代码:

// Two rectangles, assume the class name is `Rect`
Rect r1 = new Rect(x1, y2, w1, h1);
Rect r2 = new Rect(x3, y4, w2, h2);

// get the coordinates of other points needed later:
int x2 = x1 + w1;
int x4 = x3 + w2;
int y1 = y2 - h1;
int y3 = y4 - h2;

// find intersection:
int xL = Math.max(x1, x3);
int xR = Math.min(x2, x4);
if (xR <= xL)
    return null;
else {
    int yT = Math.max(y1, y3);
    int yB = Math.min(y2, y4);
    if (yB <= yT)
        return null;
    else
        return new Rect(xL, yB, xR-xL, yB-yT);
}

就像您看到的那样,如果您的矩形最初是由两个对角线角落定义的,那么它会更容易,您只需要执行// 查找交点 部分。


我必须为了一个作业而这样做,否则我会直接使用JDK API。那个想法和我的类似,但是做得更好更简单。感谢您的建议! - Doug B
我认为int xR = Math.max(x2, x4)和int yB = Math.max(y2, y4)都应该改为Math.min(...)。 - user800183
@WilliamMorrison,抱歉,原始伪代码与内联步骤不兼容,我已经更正了它。 - shuangwhywhy
@user800183,对不起,原伪代码与内嵌步骤不兼容,我已经更正了它。 - shuangwhywhy
应该添加警告,以便用户知道如果两个矩形不相交,则此方法将无法正常工作。结果会得到一个奇怪形状的矩形。例如,可能会得到高度和/或宽度为负数的矩形。在尝试获取交集之前,始终先使用r1.intersects(r2)确保两个矩形相交! - Chthonic One

13

我对确定两个矩形相交的变体进行了小型实用功能的编写。

//returns true when intersection is found, false otherwise.
//when returning true, rectangle 'out' holds the intersection of r1 and r2.
private static boolean intersection2(Rectangle r1, Rectangle r2,
        Rectangle out) {
    float xmin = Math.max(r1.x, r2.x);
    float xmax1 = r1.x + r1.width;
    float xmax2 = r2.x + r2.width;
    float xmax = Math.min(xmax1, xmax2);
    if (xmax > xmin) {
        float ymin = Math.max(r1.y, r2.y);
        float ymax1 = r1.y + r1.height;
        float ymax2 = r2.y + r2.height;
        float ymax = Math.min(ymax1, ymax2);
        if (ymax > ymin) {
            out.x = xmin;
            out.y = ymin;
            out.width = xmax - xmin;
            out.height = ymax - ymin;
            return true;
        }
    }
    return false;
}

@rob 请仔细阅读。如果存在交集,则out包含交集。甚至有文档解释这一点...此外,由于变量名称更清晰,它更易读。如果您还有任何疑问,请告诉我,我可以为您澄清其他事项。 - William Morrison
啊!刚看到了out参数,如果你想做一个名义上的改变,我会取消我的反对票。我仍然认为它不是那么容易阅读(记住,你自己的代码总是容易阅读的,但这并不意味着其他人也能读懂),但这是一个边缘的宗教争论,我们正在谈论数学。 - rjzii
这绝对是真的。我已经编辑了我的答案,删除了关于可读性的评论@rob。 - William Morrison
很酷,虽然这个答案是正确的,但它比被接受的答案更好的原因还不太明显,你可能需要进一步阐述。 - rjzii
你能否添加一些解释,说明为什么被接受的答案是不正确的,以及你的答案如何不同,从而使其正确? - Tot Zam
显示剩余3条评论

5

你还可以使用矩形源代码与自己的算法进行比较:

/**
 * Computes the intersection of this <code>Rectangle</code> with the
 * specified <code>Rectangle</code>. Returns a new <code>Rectangle</code>
 * that represents the intersection of the two rectangles.
 * If the two rectangles do not intersect, the result will be
 * an empty rectangle.
 *
 * @param     r   the specified <code>Rectangle</code>
 * @return    the largest <code>Rectangle</code> contained in both the
 *            specified <code>Rectangle</code> and in
 *            this <code>Rectangle</code>; or if the rectangles
 *            do not intersect, an empty rectangle.
 */
public Rectangle intersection(Rectangle r) {
    int tx1 = this.x;
    int ty1 = this.y;
    int rx1 = r.x;
    int ry1 = r.y;
    long tx2 = tx1; tx2 += this.width;
    long ty2 = ty1; ty2 += this.height;
    long rx2 = rx1; rx2 += r.width;
    long ry2 = ry1; ry2 += r.height;
    if (tx1 < rx1) tx1 = rx1;
    if (ty1 < ry1) ty1 = ry1;
    if (tx2 > rx2) tx2 = rx2;
    if (ty2 > ry2) ty2 = ry2;
    tx2 -= tx1;
    ty2 -= ty1;
    // tx2,ty2 will never overflow (they will never be
    // larger than the smallest of the two source w,h)
    // they might underflow, though...
    if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
    if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;
    return new Rectangle(tx1, ty1, (int) tx2, (int) ty2);
}

1
谢谢。我可以轻松地复制和粘贴这个来满足我的需求。+1。 - rayryeng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接