SwiftUI之深入解析三角函数和三角公式的应用

SwiftUI之深入解析三角函数和三角公式的应用,第1张

一、基本术语 三角是关于直角三角形的边和角之间的关系,可以给它们取个随意的名字,以确保我们都能理解对方,这些名称并不相关,可以选择任意名称,本文名称定义如下:

直角三角形:或者简单的直角三角形,它是一个三角形,其中有一个角是 90 度; Hypotenuse(斜边):它是直角三角形中最大的边,也是对着直角的边; Leg:任意边,这不是斜边; Opposed Leg:相对于其中一个角度,它是没有“接触”它的那个,在上图中, leg(a) 与 𝝰(alpha) 相反,leg(b) 与 𝝱(beta) 相反; Adjacent Leg:相对于其中一个角,它是接触它的那个角,在上图中,leg(a) 毗邻 𝝱(beta),leg(g) 毗邻𝝰(alpha)。 二、度和弧度 我们都知道度,如果我告诉你,给我一个 90 度的角,你可能马上就知道该怎么做。但如果我说,给一个 1.5708 弧度的角,那么你还知道怎么做吗?其实,它们都指向同一个角,角度和弧度是测量它们的两种不同尺度,这两个单元之间的转换非常简单:

π 是 Pi 的符号,如果需要从角度转换到弧度,那么需要:

将弧度转换为角度:

如下所示,是一个可以创建的扩展,以便在两个单位之间转换:
extension Double {
    var asDegrees: Double { return self * 180 / .pi }
    var asRadians: Double { return self * .pi / 180 }
}

let radAngle: Double = 2.0
print("radAngle radians= \(radAngle.asDegrees) degrees") 

let degAngle: Double = 180
print("degAngle degrees = \(degAngle.asRadians) radians") 
SwiftUI 有一个名为 Angle 的类型,带有一些方便的初始化式和计算属性:
let a = Angle(degrees: 180) // Create an angle using degrees
let b = Angle(radians: 2.3456) // Create an angle using radians
        
print("\(a.radians) radians = \(a.degrees) degrees")
print("\(b.radians) radians = \(b.degrees) degrees")
为什么会复杂化呢?degrees 不是更容易使用吗?也许对我们来说是这样,但数学上,弧度是有意义的,如果你有兴趣了解原因,可以浏览参考:Why Radians?。我们需要知道弧度,因为在 Swift 中的三角函数需要用弧度来指定角度。 三、三角函数的作用 正如我们看到的,给定一个直角三角形,可以从其他三角形推导出一些值。例如,如果知道斜边和其中一个角,就可以得到腿和其他角的大小。如果知道两条边,你就能得到斜边和角等。我们为什么需要这个呢?如果开始考虑三角形的顶点(A, B 和 C),在你的视图中作为 CGPoints,那么一切都比较清晰了。给定两个 CGPoint,可以计算从一个到另一个的方向(角度)(例如,对一个视图旋转效果很有用)。给定两个点的 x、y 坐标,可以得到它们之间的距离(斜边),给定一个 CGPoint 的距离和方向,可以获得第二个 CGPoint 坐标等。三角函数的另一个应用,是当你需要一个函数来平滑一个效果,一个距离,一个颜色,或者任何可以用数字表示的东西。 四、正弦余弦和正切是什么? 除了三个基本的三角函数之外,还应该知道反函数(反正弦,反余弦和反正切)。例如,如果一个角度 𝝱 的正弦值为 x,那么 x 的反正弦值为 𝝱:

根据勾股定理,斜边的平方等于两条边的平方和:

还有 SOH-CAH-TOA:

由这些公式,我们可以推断出其余的一切,当需要某个没有的值时,看看其他知道的值,然后选择正确的公式就行了。对于任何值,需要知道两条边,或者一条边和一个角,所有的计算组合都在下表中,如下图所示:


这就是我们需要的全部数学,让我们看看如何获得两个 CGPoint 之间的距离和方向,然后给定一个点坐标,一个距离和一个方向的情况下,如何计算第二个点。我们要用它们来画一个多边形,来看看到如何应用相同的想法,以绘制一个形状,如花朵显示在文章的顶部,最后使用 sin() 函数来平滑值的输入或输出。 五、角度和方向 如果你有两个任意的点,我们称它们为 pt1 和 pt2,我们将得到这两个点之间的距离和方向:

使用 SwiftUI,这段代码将得到两点之间的方向和距离:
func getDistanceAndDirection(_ pt1: CGPoint, _ pt2: CGPoint) -> (distance: CGFloat, angle: Angle) {
    let a = pt2.y - pt1.y // calculate leg a
    let b = pt2.x - pt1.x // calculate leg b
    
    var alpha = atan2(a, b) // calculate angle
    let s = sin(alpha) // sine of the angle
    let h = (a == 0 ? abs(b) : (a / s)) // calculate hypotenuse, and prevent divide by zero

    alpha = alpha < 0 ? alpha + (.pi * 2) : alpha // make sure angles are returned as positive values
    
    return (h, Angle(radians: Double(alpha)))
}
看这两点构成的三角形,距离与三角形的斜边相匹配,用 arctan 计算角度。在给定的方向和角度下获得第二个点: 如果知道一个点的坐标 (pt1),给定一个方向和长度,那么如何获得第二个点 (pt2)?

在这个例子中,创建一个形状来绘制一条线,给定一个 CGPoint,一个角度和一个距离:
Line(pt1: CGPoint(x: 100, y: 300), direction: Angle(degrees: 25), length: 300)
                .stroke(Color.blue, lineWidth: 2)
                .frame(width: 400, height: 400)
struct Line: Shape {
    let pt1: CGPoint
    let direction: Angle
    let length: CGFloat
    
    func path(in rect: CGRect) -> Path {
        let x = pt1.x + length * CGFloat(cos(direction.radians))
        let y = pt1.y - length * CGFloat(sin(direction.radians))
        
        let pt2 = CGPoint(x: x, y: y)
        
        var p = Path()
        
        p.move(to: pt1)
        p.addLine(to: pt2)
                
        return p
    }
}
六、绘制一个多边形 接下来,来创建一个形状来绘制一个正多边形,这里将使用一个七边多边形,代码几乎类似,可以创建任意数量的边:

一个多边形有许多顶点,想要得到相应的坐标,这样就可以画出连接它们的线。如下图所示,所有顶点到圆周中心的距离都相同:

如前所述,在两点之间,您总是可以创建一个直角三角形。在某些情况下,会形成一个三角形,其中一条边的长度为 0,而另一条边的长度为斜边,想象一个三角形,其中一条边收缩,直到它的长度为零。在这种情况下,正弦值为 0,余弦值为 1,反之亦然。三角函数可以很好地处理这个问题,其中一种情况,就是顶部的顶点(90 度角),cos(90) = 0 sin(90) = 1:

如果我们定义了多边形的中心和周长的半径,就可以得到所有的顶点,每个顶点的角度由多边形的边数决定。三角函数的美妙之处在于,它们可以处理大于 90 度的角,(在某些情况下)返回负值,这很好地满足了我们的绘图要求。例如,在上面的第二个三角形中,余弦值是负的,这意味着顶点的 x 坐标将小于圆周中心的 x 坐标,正是需要的:
struct PolygonShape: Shape {
    var sides: Int
    
    func path(in rect: CGRect) -> Path {        
        // hypotenuse (we make it fit inside the available rect
        let h = Double(min(rect.size.width, rect.size.height)) / 2.0
        
        // center
        let c = CGPoint(x: rect.size.width / 2.0, y: rect.size.height / 2.0)
        
        var path = Path()
                
        for i in 0..<sides {
            let angle = (Double(i) * (360.0 / Double(sides))) * Double.pi / 180

            // Calculate vertex position
            let pt = CGPoint(x: c.x + CGFloat(cos(angle) * h), y: c.y + CGFloat(sin(angle) * h))
            
            if i == 0 {
                path.move(to: pt) // move to first vertex
            } else {
                path.addLine(to: pt) // draw line to next vertex
            }
        }
        
        path.closeSubpath()
        
        return path
    }
}
我们随时都可以看到三角形,例如,在下面的花中,每个花瓣都由两条曲线组成;要画一条曲线,需要一个起点、一个终点和一个控制点,我们能用这三个点做什么呢?可以用来制作半瓣的三个点,另一半是对称的:

七、平滑进,平滑出 正弦(或余弦)函数有一个特点,我们看它的图形,可以注意到图形的形状重复,它的最小值是 -1,最大值是 1,f(x) 开始缓慢增长,然后稳定,然后再次缓慢增长:

如果稍微改变函数,为了移动和压缩图,我们得到一对理想的波,平滑地增加和减少一个任意值:

当然,还有其他方法可以实现平滑值,但这是一个值得提及的简单方法。可以使用这个功能淡入淡出几乎任何东西:声音音量、定位、移动、颜色、缩放等。如下所示,创建具有渐进缩放值的文本:

struct ContentView: View {
    var body: some View {
        ProgressiveText(text: "AAAAAAAA")
    }
}

struct ProgressiveText: View {
    let text: String
    
    var body: some View {
        
        HStack(spacing: 10) {
            ForEach(Array(text.enumerated()), id: \.0) { (n, ch) in
                Text(String(ch)).font(.largeTitle).fontWeight(.bold).scaleEffect(self.scaleValue(n, self.text.count))
            }
        }
    }
    
    func scaleValue(_ idx: Int, _ totalCharacters: Int) -> CGFloat {
        
        // Normalized character position, to a value between 0 and 1
        let x = Double(idx) / Double(totalCharacters)
        
        // Get a number between 0 and 1, according to a sine wave
        let y = (sin(2 * .pi * x - (.pi / 2)) + 1) / 2.0
        
        // Return a scale value from 1 (normal) to 3 (3 times the size).
        return 1 + 2 * CGFloat(y)
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/994621.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存