iphone - 在与中心点成给定角度的直线相交的 UIView 矩形上找到 CGPoint

标签 iphone ios math graphics geometry

在 iOS 中,我试图确定一个矩形上的点,该点与从中心点到矩形周边的假想线以预定角度相交。

假设我知道中心点、矩形的大小和角度(从东边的 0 度开始,逆时针方向经过北边的 90 度、西边的 180 度和南边的 270 度,再到东边的 360 度)。我需要知道相交点的坐标。

Finding points on a rectangle at a given angle 上的(对我而言)有点令人困惑的数学但大概是准确的答案让我尝试下面的代码,但它不能正常工作。这个问题与那个问题类似,但我正在寻找一种更正的 Objective-C/iOS 方法,而不是一般的数学响应。

我认为部分代码问题与使用单个 0 到 360 度角(以弧度表示,不可能为负数)输入有关,但可能还有其他问题。下面的代码主要使用 the answer from belisarius 中定义的符号,包括我尝试为此处定义的四个区域计算交点。

这段代码在我的 UIImageView 子类中:

- (CGPoint) startingPointGivenAngleInDegrees:(double)angle {
    double angleInRads = angle/180.0*M_PI;
    float height = self.frame.size.height;
    float width = self.frame.size.width;
    float x0 = self.center.x;
    float y0 = self.center.y;
    // region 1 
    if (angleInRads >= -atan2(height, width) && angleInRads <= atan2(height, width)) {
        return CGPointMake(x0 + width/2, y0 + width/2 * tan(angleInRads));
    }
    // region 2
    if (angleInRads >= atan2(height, width) && angleInRads <= M_PI - atan2(height, width)) {
        return CGPointMake(x0 + height / (2*tan(angleInRads)),y0+height/2);
    }
    // region 3
    if (angleInRads >= M_PI - atan2(height, width) && angleInRads <= M_PI + atan2(height, width)) {
        return CGPointMake(x0 - width/2, y0 + width/2 * tan(angleInRads));
    }
    // region 4
    return CGPointMake(x0 + height / (2*tan(angleInRads)),y0-height/2);    
}

最佳答案

我不会调试您的代码,而只是解释我将如何做到这一点。

首先,几个术语。让我们将 xRadius 定义为框架宽度的一半,将 yRadius 定义为框架高度的一半。

现在考虑框架的四个边,并将它们延伸为无限长的线。在这四条线的顶部,放置一条以您指定的角度穿过框架中心的线:

diagram of rectangle with overlaid line

假设框架以原点为中心 - 框架的中心位于坐标 (0,0)。我们可以很容易地计算出对角线与框架右边缘相交的位置:坐标为 (xRadius, xRadius * tan(angle))。我们可以很容易地计算出对角线与框架顶部边缘相交的位置:坐标是 (-yRadius/tan(angle), -yRadius).

(为什么我们否定顶边交点的坐标?因为 UIView 坐标系是从正常的数学坐标系翻转过来的。在数学中,y 坐标向页面顶部增加. 在 UIView 中,y 坐标向 View 底部增加。)

所以我们可以简单地计算直线与框架右边缘的交点。如果该交点在框架之外,那么我们知道该线必须先与上边缘相交,然后再与右边缘相交。。我们如何判断右边缘交点是否越界?如果它的 y 坐标 (xRadius * tan(angle)) 大于 yRadius(或小于 -yRadius),则它超出范围。

因此,为了将它们放在一个方法中,我们首先计算 xRadiusyRadius:

- (CGPoint)radialIntersectionWithConstrainedRadians:(CGFloat)radians {
    // This method requires 0 <= radians < 2 * π.

    CGRect frame = self.frame;
    CGFloat xRadius = frame.size.width / 2;
    CGFloat yRadius = frame.size.height / 2;

然后我们计算与右边缘相交的 y 坐标:

    CGPoint pointRelativeToCenter;
    CGFloat tangent = tanf(radians);
    CGFloat y = xRadius * tangent;

我们检查交点是否在框架中:

    if (fabsf(y) <= yRadius) {

一旦我们知道它在框架中,我们就必须弄清楚我们想要与右边缘还是左边缘相交。如果角度小于 π/2 (90°) 或大于 3π/2 (270°),我们需要右边缘。否则我们需要左边。

        if (radians < (CGFloat)M_PI_2 || radians > (CGFloat)(M_PI + M_PI_2)) {
            pointRelativeToCenter = CGPointMake(xRadius, y);
        } else {
            pointRelativeToCenter = CGPointMake(-xRadius, -y);
        }

如果右边缘交点的 y 坐标 • 是*越界,我们计算与底部边缘交点的 x 坐标。

    } else {
        CGFloat x = yRadius / tangent;

接下来我们要弄清楚我们是想要顶部边缘还是底部边缘。如果角度小于 π (180°),我们需要底边。否则,我们需要顶部边缘。

        if (radians < (CGFloat)M_PI) {
            pointRelativeToCenter = CGPointMake(x, yRadius);
        } else {
            pointRelativeToCenter = CGPointMake(-x, -yRadius);
        }
    }

最后,我们将计算出的点偏移框架的实际中心并将其返回。

    return CGPointMake(pointRelativeToCenter.x + CGRectGetMidX(frame),
        pointRelativeToCenter.y + CGRectGetMidY(frame));
}

在这里测试项目:https://github.com/mayoff/stackoverflow-radial-intersection

看起来像这样:

edgepoint screen shot

关于iphone - 在与中心点成给定角度的直线相交的 UIView 矩形上找到 CGPoint,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9526726/

相关文章:

iphone - 权利 'application-identifier' 具有配置文件不允许的值 - TestFlight

ios - 通过 swift 集成新的 facebook SDK

iphone - 如何在 APNS iPhone 应用程序的 php 服务器中保存所有 DeviceToken?

objective-c - 有什么方法可以禁用 'message to nil not throwing an error' 行为(即我想要错误)?

math - CUDA tgamma函数: is documentation wrong or tgamma isn't a gamma?

jquery - $(document).on 点击​​处理程序和标准点击处理程序在 iOS 上的行为不同

ios - 无法从展开的 segue Action 中弹出

iphone - SFHFKeychainUtils。 iOS 钥匙串(keychain)。弧兼容

math - 从二维变换矩阵中提取旋转、缩放值

javascript - 非弹性碰撞仅使物体向右移动