查找线段 - 矩形交叉点

时间:2012-05-18 17:27:50

标签: unity3d geometry intersection unityscript

简单地说,我有:

  • 一个视口矩形,其中(0,0)是左下角,(1,1)是右上角,(0.5,0.5)是屏幕的中心。
  • 矩形之外的点(a,b)。

这是在视口坐标中所以+ X是正确的,+ Y在屏幕上。

我需要一个函数,它接受这些参数并返回矩形边缘上的点(矩形中心(0.5,0.5)和点(a,b)之间)与之相交的点。 / p>

我知道如何在给定坐标的纸上做到这一点,但是当我谈到代码时我无法弄明白。此外,我意识到这样的问题已在不同的线程中得到解决 - 但我无法在任何地方找到输出函数的简单输入。

我在Unity3D引擎中这样做,所以最好用Javascript,但任何语言或伪代码都会有很大的帮助,因为我可以手动转换它。

修改 为了澄清,我正在寻找类似的东西:

function IntersectFromViewportCenter(x : float, y : float) {
    ...
    return Point(x1, y1);
}

其中(x,y)是圆外的点,(x1,y1)是交点。

由于

5 个答案:

答案 0 :(得分:2)

将所有系统移至中心点(0,0)。使用方框(-1,-1) - (1,1)从原点到(移位)点(x',y')的光线交点。缩放并转回。我没有考虑盒子里面的琐事(是否需要?)

x = x - 0.5
y = y - 0.5
if Abs(x) >= Abs(y) then  //vertical box edge
  y1 = y/x  //care with case both y and x = 0
  x1 = Sign(x) //+-1
else   // horizontal edge
  x1 = x/y
  y1 = Sign(y)

x1 = 0.5*x1 + 0.5
y1 = 0.5*y1 + 0.5

答案 1 :(得分:1)

MBo有正确的想法。这是在Unity中实现的一种方法。我认为UnityScript不值得使用 - 特别是它不支持扩展方法 - 所以你真的应该切换语言。 (另外,Unity实际上并未命名为Unity3D。)

此脚本可以在项目中的任何位置:

using UnityEngine;

public static class UnityEngineExtensions {

public static Vector2 Abs(this Vector2 vector) {
    for (int i = 0; i < 2; ++i) vector[i] = Mathf.Abs(vector[i]);
    return vector;
}   

public static Vector2 DividedBy(this Vector2 vector, Vector2 divisor) {
    for (int i = 0; i < 2; ++i) vector[i] /= divisor[i];
    return vector;
}

public static Vector2 Max(this Rect rect) {
    return new Vector2(rect.xMax, rect.yMax);
}

public static Vector2 IntersectionWithRayFromCenter(this Rect rect, Vector2 pointOnRay) {
    Vector2 pointOnRay_local = pointOnRay - rect.center;
    Vector2 edgeToRayRatios = (rect.Max() - rect.center).DividedBy(pointOnRay_local.Abs());
    return (edgeToRayRatios.x < edgeToRayRatios.y) ?
        new Vector2(pointOnRay_local.x > 0 ? rect.xMax : rect.xMin, 
            pointOnRay_local.y * edgeToRayRatios.x + rect.center.y) :
        new Vector2(pointOnRay_local.x * edgeToRayRatios.y + rect.center.x, 
            pointOnRay_local.y > 0 ? rect.yMax : rect.yMin);
}

}

将此其他脚本附加到游戏对象,并在检查器中设置其变量。

#pragma warning disable 0649
using System;
using UnityEngine;

public class VisualizeRectIntersectionWithRayFromCenter : MonoBehaviour {

[SerializeField] Rect rect;
[SerializeField] Vector2 point;

[Serializable] class Colors {
    public Color rect, point, intersection;
} [SerializeField] Colors colors;

void OnDrawGizmos() {
    Gizmos.color = colors.rect;
    Vector2[] corners = {new Vector2(rect.xMin, rect.yMin), new Vector2(rect.xMin, rect.yMax),
        rect.Max(), new Vector2(rect.xMax, rect.yMin)};
    int i = 0;
    while (i < 3) Gizmos.DrawLine(corners[i], corners[++i]);
    Gizmos.DrawLine(corners[3], corners[0]);

    Gizmos.color = colors.point;
    Gizmos.DrawLine(rect.center, point);

    Gizmos.color = colors.intersection;
    Gizmos.DrawLine(rect.center, rect.IntersectionWithRayFromCenter(pointOnRay: point));
}

}

答案 2 :(得分:1)

由于已经提供了一些常规的“线/矩形”方法,因此该方法经过优化,可以避免在琐碎情况下(完全在Rect内部或完全在Rect外部)进行射线投射:https://gist.github.com/JohannesMP/50dad3175bf2925df508b642091e41c4

它还有效地提供了入口点和出口点(在我的用例中是必需的): enter image description here

这是此方法的基本概述:

  1. 按以下方式将rect周围的区域划分为Sector(其中S4是rect本身):

    S0| S1 |S2
    --+----+--   ^
    S3| S4 |S5   |
    --+----+--   y
    S6| S7 |S8    x-->
    
  2. 鉴于线段开始和结束的扇区,我们知道需要执行哪些射线广播(例如:S0-S2不需要射线广播,而S4-S1只需要射线广播顶部边缘,等等)

  3. 预先计算此数据并将其存储在9x9小型静态数组中。基本上,我们使用了一些静态内存来避免条件检查。
  4. 在运行时,找到直线起点和终点所占据的扇形,然后将结果用于仅对Rect的必要边缘进行光线投射。

另外,如果分别处理垂直线和水平线,则可以将射线投射简化为一维。


在我的个人用例(很多线段,其中大部分完全位于Rect内部或完全位于Rect外部)中,这种方法比一般情况下要快,因为仅在必要时才执行Raycasts。

答案 3 :(得分:0)

$scope.setTotals = function(item){
        if (item){
            item.total = item.quantity * item.unitCost;
            $scope.invoiceCount += item.quantity;

        }
    }

答案 4 :(得分:0)

@chakmeshma,您的解决方案几乎是正确的,但您还必须检查交叉点是否在矩形内以避免边界情况:

private static bool LineRectIntersection(Vector2 lineStartPoint, Vector2 lineEndPoint, Rect rectangle, ref Vector2 result)
    {
        Vector2 minXLinePoint = lineStartPoint.x <= lineEndPoint.x ? lineStartPoint : lineEndPoint;
        Vector2 maxXLinePoint = lineStartPoint.x <= lineEndPoint.x ? lineEndPoint : lineStartPoint;
        Vector2 minYLinePoint = lineStartPoint.y <= lineEndPoint.y ? lineStartPoint : lineEndPoint;
        Vector2 maxYLinePoint = lineStartPoint.y <= lineEndPoint.y ? lineEndPoint : lineStartPoint;

        double rectMaxX = rectangle.xMax;
        double rectMinX = rectangle.xMin;
        double rectMaxY = rectangle.yMax;
        double rectMinY = rectangle.yMin;

        if (minXLinePoint.x <= rectMaxX && rectMaxX <= maxXLinePoint.x)
        {
            double m = (maxXLinePoint.y - minXLinePoint.y) / (maxXLinePoint.x - minXLinePoint.x);

            double intersectionY = ((rectMaxX - minXLinePoint.x) * m) + minXLinePoint.y;

            if (minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y
                && rectMinY <= intersectionY && intersectionY <= rectMaxY)
            {
                result = new Vector2((float)rectMaxX, (float)intersectionY);

                return true;
            }
        }

        if (minXLinePoint.x <= rectMinX && rectMinX <= maxXLinePoint.x)
        {
            double m = (maxXLinePoint.y - minXLinePoint.y) / (maxXLinePoint.x - minXLinePoint.x);

            double intersectionY = ((rectMinX - minXLinePoint.x) * m) + minXLinePoint.y;

            if (minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y
                && rectMinY <= intersectionY && intersectionY <= rectMaxY)
            {
                result = new Vector2((float)rectMinX, (float)intersectionY);

                return true;
            }
        }

        if (minYLinePoint.y <= rectMaxY && rectMaxY <= maxYLinePoint.y)
        {
            double rm = (maxYLinePoint.x - minYLinePoint.x) / (maxYLinePoint.y - minYLinePoint.y);

            double intersectionX = ((rectMaxY - minYLinePoint.y) * rm) + minYLinePoint.x;

            if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x
                && rectMinX <= intersectionX && intersectionX <= rectMaxX)
            {
                result = new Vector2((float)intersectionX, (float)rectMaxY);

                return true;
            }
        }

        if (minYLinePoint.y <= rectMinY && rectMinY <= maxYLinePoint.y)
        {
            double rm = (maxYLinePoint.x - minYLinePoint.x) / (maxYLinePoint.y - minYLinePoint.y);

            double intersectionX = ((rectMinY - minYLinePoint.y) * rm) + minYLinePoint.x;

            if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x
                && rectMinX <= intersectionX && intersectionX <= rectMaxX)
            {
                result = new Vector2((float)intersectionX, (float)rectMinY);

                return true;
            }
        }

        return false;
    }
}