I'm trying to create randomly generated wavy lines using Swift UI. I tried copying the math from this question on SO.
https://stackoverflow.com/a/54051986/1895804
But my bezier curves are creating sharp points instead of curving smoothly. Does anyone know why this is happening?
It should look like this:
But it looks like this with jagged points.
struct SuperCurvyLine: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let canvasW = rect.width
let canvasH = rect.height
let canvasX = canvasW/2
let canvasY = canvasH/2
let angVal = 0.20
let tension = 1.0
let npts: CGFloat = 60
var dw = [Double]()
var xs = [Double]()
var ys = [Double]()
var vys = [Double]()
var vxs = [Double]()
xs.append(0)
ys.append(canvasY)
var angle = 0.0
for i in 0...Int(npts) {
dw.append(2 * Double.random(in: 0...1) - 1)
vxs.append(10 * cos(2 * Double.pi * angle))
vys.append(10 * sin(2 * Double.pi * angle))
angle = angle + dw[i] * angVal
}
for i in 1...Int(npts) {
xs.append(xs[i - 1] + 3 * (vxs[i - 1] + vxs[i]) / 2)
ys.append(ys[i - 1] + 3 * (vys[i - 1] + vys[i]) / 2)
}
path.move(to: CGPoint(x: xs[0], y: ys[0]))
for i in 1...Int(npts) {
let cp1x = xs[i - 1] + tension * vxs[i - 1]
let cp1y = ys[i - 1] + tension * vys[i - 1]
let cp2x = xs[i] - tension * vxs[i]
let cp2y = ys[i] - tension * vys[i]
let to = CGPoint(x: cp1x, y: cp1y)
let control1 = CGPoint(x: cp2x, y: cp2y)
let control2 = CGPoint(x: xs[i], y: ys[i])
path.addCurve(to: control1, control1: to, control2: control1)
}
return path
}
}
You mixed up the to
, control1
and control2
points. According to the original JS, they should be:
path.addCurve(to: control2, control1: to, control2: control1)
You have named these rather confusing names. It should really be:
let control1 = CGPoint(x: cp1x, y: cp1y)
let control2 = CGPoint(x: cp2x, y: cp2y)
let to = CGPoint(x: xs[i], y: ys[i])
path.addCurve(to: to, control1: control1, control2: control2)
Now it makes a lot of sense. Note that the order of parameters in JS is "control1, control2, to", different from the Swift API.
For even smoother lines, you can use a lineJoin: .round
stroke style:
.stroke(style: .init(lineJoin: .round))