4

简单地说,我有:

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

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

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

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

我在 Unity3D 引擎中执行此操作,因此最好使用 Javascript,但任何语言或伪代码都会有很大帮助,因为我可能可以手动转换它。

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

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

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

谢谢

4

5 回答 5

2

将所有系统移动到点 (0,0) 的中心。计算从原点到(移动)点 (x',y') 的光线与框 (-1,-1)-(1,1) 的交点。缩放并向后移动。我没有考虑盒子内有点的琐碎案例(需要吗?)

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
于 2012-05-18T18:33:46.327 回答
2

由于已经提供了一些通用的 Line/Rect 方法,这里有一种方法经过优化,可以避免在微不足道的情况下(完全在 Rect 内部或完全在 Rect 之外)发生光线投射:https ://gist.github.com/JohannesMP/50dad3175bf2925df508b642091e41c4

它还有效地提供了入口和出口点(在我的用例中是必需的): 在此处输入图像描述

以下是此方法的基本概述:

  1. 将矩形周围的区域划分为扇区,如下所示(其中 S4 是矩形本身):

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

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

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


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

于 2018-07-08T23:34:43.793 回答
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));
}

}
于 2012-05-18T23:29:28.933 回答
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;
    }
}
于 2016-12-13T10:47:15.413 回答
0
bool LineRectIntersection(Vector2 lineStartPoint, Vector2 lineEndPoint, Rect rectangle, ref double resultX, ref double resultY)
{
    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 - ((double)minXLinePoint.x)) * m) + ((double)minXLinePoint.y);

        if(minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y)
        {
            resultX = rectMaxX;
            resultY = intersectionY;

            return true;
        }
    }

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

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

        if (minYLinePoint.y <= intersectionY && intersectionY <= maxYLinePoint.y)
        {
            resultX = rectMinX;
            resultY = intersectionY;

            return true;
        }
    }

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

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

        if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x)
        {
            resultX = intersectionX;
            resultY = rectMaxY;

            return true;
        }
    }

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

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

        if (minXLinePoint.x <= intersectionX && intersectionX <= maxXLinePoint.x)
        {
            resultX = intersectionX;
            resultY = rectMinY;

            return true;
        }
    }

    return false;
}
于 2016-08-14T17:16:32.807 回答