在UIView矩形上找到CGPoint,该矩形与从中心点以给定角度的直线相交

Vic*_*Hee 10 iphone math graphics geometry ios

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

假设我知道中心点,矩形的大小和角度(从东向0度开始,向北逆时针旋转90度,西向180度,南向270度至东经360度).我需要知道交叉点的坐标.

在给定角度的矩形查找点时,有些令人困惑的(对我来说)数学但可能是准确的答案导致我尝试以下代码,但它无法正常工作.这个问题类似于那个问题,但我正在寻找一个更正的Objective-C/iOS方法而不是一般的数学响应.

我认为代码问题的一部分与使用单个0到360度角(弧度,不可能有负数)输入有关,但可能还有其他问题.下面的代码主要使用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);    
}
Run Code Online (Sandbox Code Playgroud)

rob*_*off 21

我将解释如何执行此操作,而不是调试代码.

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

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

带有重叠线的矩形图

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

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

因此,我们可以简单地计算线与帧右边缘的交点.如果该交点位于框架之外,那么我们知道该线条与右边缘相交之前必须与顶边相交.我们如何判断右边交叉点是否超出范围?如果其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;
Run Code Online (Sandbox Code Playgroud)

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

    CGPoint pointRelativeToCenter;
    CGFloat tangent = tanf(radians);
    CGFloat y = xRadius * tangent;
Run Code Online (Sandbox Code Playgroud)

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

    if (fabsf(y) <= yRadius) {
Run Code Online (Sandbox Code Playgroud)

一旦我们知道它在框架中,我们必须弄清楚我们是否想要与右边缘或左边缘的交叉点.如果角度小于π/ 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);
        }
Run Code Online (Sandbox Code Playgroud)

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

    } else {
        CGFloat x = yRadius / tangent;
Run Code Online (Sandbox Code Playgroud)

接下来我们弄清楚我们是想要顶边还是底边.如果角度小于π(180°),我们需要底边.否则,我们想要最高优势.

        if (radians < (CGFloat)M_PI) {
            pointRelativeToCenter = CGPointMake(x, yRadius);
        } else {
            pointRelativeToCenter = CGPointMake(-x, -yRadius);
        }
    }
Run Code Online (Sandbox Code Playgroud)

最后,我们将计算点偏移到帧的实际中心并返回它.

    return CGPointMake(pointRelativeToCenter.x + CGRectGetMidX(frame),
        pointRelativeToCenter.y + CGRectGetMidY(frame));
}
Run Code Online (Sandbox Code Playgroud)

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

看起来像这样:

边缘屏幕截图


小智 5

这是罗伯(Rob)的答案,是快速的律法复制和粘贴。

func findEdgePoint(angle: CGFloat) -> CGPoint {

    let intersection: CGPoint

    let xRad = frame.width / 2
    let yRad = frame.height / 2

    let tangent = tan(angle)
    let y = xRad * CGFloat(tangent)

    if fabs(y) <= yRad {

        if angle < CGFloat.pi / 2 || angle > 3 * CGFloat.pi / 2 {
            intersection = CGPoint(x: xRad, y: y)
        } else {
            intersection = CGPoint(x: -xRad, y: -y)
        }
    } else {

        let x = yRad / CGFloat(tangent)

        if angle < CGFloat.pi {
            intersection = CGPoint(x: x, y: yRad)
        } else {
            intersection = CGPoint(x: -x, y: -yRad)
        }
    }

    return intersection
}

// heck yeah rob, he's the man
// if he can't solve it, no one can
Run Code Online (Sandbox Code Playgroud)

  • 我不同意。我已经在这里看到足够多的“如何在Swift **中做到这一点”的问题,我认为提供旧的Objective-C答案的Swift翻译会增加价值。 (4认同)