如何使时钟滴答声粘在时钟边框上?

How to make clock ticks stuck to the clock border?

我尝试在 swift 中制作一个时钟,但现在我想制作一些奇怪的东西。我想让边框半径可设置。这是容易的部分(很容易,因为我已经做到了)。我全天候画了 60 个刻度。问题是 60 个刻度是一个完美的圆。如果我改变边界半径我得到这个时钟:

所有报价均由 NSBezierPath 生成,每个报价的计算位置代码为:

tickPath.moveToPoint(CGPoint(
            x: center.x + cos(angle) *  point1 ,
            y: center.y + sin(angle) * point1
            ))

        tickPath.lineToPoint(CGPoint(
            x: center.x + cos(angle) * point2,
            y: center.y + sin(angle) * point2
            ))

point1 和 point2 是 12 个时钟滴答的点。

我的时钟背景是用贝塞尔曲线路径制作的:

 let bezierPath = NSBezierPath(roundedRect:self.bounds, xRadius:currentRadius, yRadius:currentRadius) 

currentRadius - 是一个可设置的变量,所以我的背景是从正圆(当角半径 = 高度/2 时)到正方形(当角半径 = 0 时)。

是否有任何公式可以计算每个刻度的位置,因此对于任何边界半径,最终所有刻度与边界的距离相同?

如果不求助于图表,数学解释起来相当复杂,但基本上如果你考虑以时钟中心为原点的极坐标方法,那么有两种情况:

  • 从原点开始的辐条与正方形的直边相交的地方 - 通过三角学很容易
  • 它在拐角处碰到圆弧的地方——我们用余弦法则求解时钟圆心、拐角圆心和辐条穿过拐角的点形成的三角形。该三角形的原点方向角度为 45º - angleOfSpoke,其中两条边的长度已知。将余弦方程求解为二次方程即可。

这个函数做到了:

func radiusAtAngle(angleOfSpoke: Double, radius: Double, cornerRadius: Double) -> Double {
    // radius is the half-width of the square, = the full radius of the circle
    // cornerRadius is, of course, the corner radius. 
    // angleOfSpoke is the (maths convention) angle of the spoke
    // the function returns the radius of the spoke.

    let theta = atan((radius - cornerRadius) / radius) // This determines which case

    let modAngle = angleOfSpoke % M_PI_2 // By symmetry we need only consider the first quadrant

    if modAngle <= theta { // it's on the vertical flat
        return radius / cos(modAngle)
    } else if modAngle > M_PI_2 - theta { // it's on the horizontal flat
        return radius / cos(M_PI_2 - modAngle)
    } else { // it's on the corner arc
        // We are using the cosine rule to solve the triangle formed by
        // the clock centre, the curved corner's centre,
        // and the point of intersection of the spoke.
        // Then use quadratic solution to solve for the radius.
        let diagonal = hypot(radius - cornerRadius, radius - cornerRadius)
        let rcosa = diagonal * cos(M_PI_4 - modAngle)
        let sqrTerm = rcosa * rcosa - diagonal * diagonal + cornerRadius * cornerRadius
        if sqrTerm < 0.0 {
            println("Aaargh - Negative term") // Doesn't happen - use assert in production
            return 0.0
        } else {
            return rcosa + sqrt(sqrTerm) // larger of the two solutions
        }
    }
}

在图表中 OP = 对角线,OA = 半径,PS = PB = 角半径,OS = 函数 return,BÔX = theta,SÔX = angleOfSpoke