如何使时钟滴答声粘在时钟边框上?
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
我尝试在 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