从不在矩形中间的点以角度查找矩形边界点

时间:2015-10-19 04:11:21

标签: javascript algorithm math graphics geometry

我试图在矩形中显示玩家瞄准我的游戏的位置。他在这样的广场上:

image description

我找到了algorithm to find rectangle point by angle,应用它并且结果错误(真正的游戏截图):

image description

我很快意识到这是因为玩家不在矩形的中心。所以我用我的观点替换了algorighm中的中心点。但结果只是移动了一点距离:

image description

所以我猜我当前的算法需要的不仅仅是一个变化。我的代码:

  function boudaryatangle(theta, point) {
    // reduce the theta  
    theta =  theta % 360; 
    // force it to be the positive remainder, so that 0 <= theta < 360  
    theta = (theta + 360) % 360;  
    // force into the minimum absolute value residue class, so that -180 < theta <= 180  
    if (theta > 180)  
        theta -= 360;  
    // Convert to radians
    theta = (theta * Math.PI / 180);
    // Get a rectangle that has width and height properties
    var rect = this.game.pixels;
    var width = rect.width;
    var height = rect.height;
    // If refference point wasn't provided as second argument
    //TODO: MAKE IT WORK WITH OTHER THEN RECTANGLE CENTRE POINT!
    if(typeof point=="undefined") {
      point = new Float32Array(2);
      point[0] = width/2;
      point[1] = height/2;
    }
    // Here be mysterious math and stuff - all bellow explained here
    var rectAtan = Math.atan2(height, width);
    var tanTheta = Math.tan(theta);
    // By default, region 1 and 3
    var region = true;

    var xFactor = 1;
    var yFactor = 1;

    if ((theta > -rectAtan) && (theta <= rectAtan)) {
        yFactor = -1;  // REGION 1
    } else if ((theta > rectAtan) && (theta <= (Math.PI - rectAtan))) {
        yFactor = -1;  // REGION 2
        region = false;
    } else if ((theta > (Math.PI - rectAtan)) || (theta <= -(Math.PI - rectAtan))) {
        xFactor = -1;  // REGION 3
    } else {
        xFactor = -1;  // REGION 4
        region = false;
    }

    // If region 1, 3
    if (region) {
      point[0] += xFactor * (width / 2.);                                     // "Z0"
      point[1] += yFactor * (width / 2.) * tanTheta;
    } else {
      point[0] += xFactor * (height / (2. * tanTheta));                        // "Z1"
      point[1] += yFactor * (height /  2.);
    }
    return point;
  }

我还需要在哪里应用参考点位置才能使其工作?如果点超出矩形,则此函数返回合理的结果

3 个答案:

答案 0 :(得分:1)

我们有:
- 指向(px,py)坐标(相对于矩形角)
- 方向向量(dx,dy)其中dx = Cos(theta), dy = Sin(theta)
- 宽度,矩形高度。

我们需要找到与矩形相交的光线的第一个点。所以做一些方程式:

if dx = 0 then
  t = Infinity
else 
if dx > 0 then
  px + dx * t = width
else
  px + dx * t = 0

if dy = 0 then
  u = Infinity
else 
if dy > 0 then
  py + dy * u = height
else
  py + dy * u = 0

为t和u求解这些方程。不要忘记从t和u找到最小值:v = Min(t, u) - 这是第一个交叉点。它的坐标是: (px + v * dx, py + v * dy)

示例

P = (10, 20) 
theta = 135deg = 3 * Pi/4 => D = (-0.707, 0.707)
W = 40, H = 50

t ~ 14
u ~ 42
v = Min(t,u) = 14
Int.X = 10 - 14 * 0.707 = 0 
// really there is is not need to calculate this value, 
//because we determined that the first intersection occurs for left edge
Int.Y = 20 + 14 * 0.707 = 30

答案 1 :(得分:1)

玩家&#34;看到&#34;在拐角处,根据他瞄准角落的哪一侧,碰撞发生在垂直或水平侧。

让我们来表示c = cos Θs = sin Θ。方向(x, y)中来自(c, s)的行的等式为(X - x) s - (Y - y) c = 0。左侧的表达式在行的一侧是正的而在另一侧是负的。

在第一个象限(c, s > 0)中,角落为(w, h),V / H决定取决于(w - x) s - (h - y) c的符号。如果交叉点是垂直边,我们有

X = w, (w - x) s - (Y - y) c = 0, or Y = y + (w - x) s / c.

对于水平面,

Y = h, (X - x) s - (h - y) c = 0, or X = x + (h - y) c / s.

我们可以写

Corner (w, h):
    if (w - x) s < (h - y) c:
        X= w; Y= y + (w - x) s / c
    else:
        X= x + (h - y) c / s; Y= h

请注意,如果c==0s==0,解决方案仍然有效,因为将采用的分支不会出现除零。在比较中是使用<还是<=没有区别(在相同的情况下,角落本身被找到,两种方式)。

这可以微观优化为

Corner (w, h):
    S= (w - x) s; C= (h - y) c
    if S < C:
        X= w; Y= y + S / c
    else:
        X= x + C / s; Y= h

可以对四个象限重复相同的讨论,将w和/或h替换为0。最终的代码结构是

if s >= 0:
    if c >= 0:
        Corner (w, h)
    else:
        Corner (0, h)
else:
    if c >= 0:
        Corner (w, 0)
    else:
        Corner (0, 0)

注意为四个象限正确调整比较方向。

选择此配方以尝试达到最小量的计算,同时保持数值可靠。我们列出

  • 选择象限的两个符号测试,
  • 涉及两种产品和两种添加物的比较,
  • 交叉计算的一个加法和一个除法。

答案 2 :(得分:0)

算法的许多部分,例如height / 2.,表明您仍然认为该点位于矩形的中心。如果您的点可以位于矩形中的任何位置,则必须调整所有数字,以便您使用yheight - y,具体取决于您的光线向上还是向下指示。

当你的点是任意的时,你不能再做的简化是四角之间的角度之间存在对称性。这意味着你不能使用你的区域方法;至少它会更复杂。

另一种方法是计算光线与四个边界的交点,看看它们是否位于矩形上:

function borderPoint(rect, pt, angle) {

    // catch cases where point is outside rectangle
    if (pt.x < rect.left) return null;
    if (pt.x > rect.left + rect.width) return null;
    if (pt.y < rect.top) return null;
    if (pt.y > rect.top + rect.height) return null;

    var dx = Math.cos(angle);
    var dy = Math.sin(angle);

    if (dx < 1.0e-16) {         // left border
        var y = (rect.left - pt.x) * dy / dx + pt.y;

        if (y >= rect.top && y <= rect.top + rect.height) {
            return {x: rect.left, y: y};
        }
    }

    if (dx > 1.0e-16) {         // right border
        var y = (rect.left + rect.width - pt.x) * dy / dx + pt.y;

        if (y >= rect.top && y <= rect.top + rect.height) {
            return {x: rect.left + rect.width, y: y};
        }
    }

    if (dy < 1.0e-16) {         // top border
        var x = (rect.top - pt.y) * dx / dy + pt.x;

        if (x >= rect.left && x <= rect.left + rect.width) {
            return {x: x, y: rect.top};
        }
    }

    if (dy > 1.0e-16) {         // bottom border
        var x = (rect.top + rect.height - pt.y) * dx / dy + pt.x;

        if (x >= rect.left && x <= rect.left + rect.width) {
            return {x: x, y: rect.top + rect.height};
        }
    }

    return null;
}

该代码使用角度的数学定义,即0为东,π/ 2为北,π为西,3·π/ 2为南。如果使用其他定义,则应调整代码。

当点位于矩形之外或者找不到解决方案时,代码返回null