Curnelious Curnelious - 29 days ago 25
Swift Question

Draw sine wave with frequency?

I am drawing one period of a sine wave with:

let width = rect.width
let height = rect.height

let origin = CGPoint(x: width * (1 - graphWidth) / 2, y: height * 0.50)

let path = UIBezierPath()
path.move(to: origin)

for angle in stride(from: 5.0, through: 360.0, by: 5.0) {
let x = origin.x + CGFloat(angle/360.0) * width * graphWidth
let y = origin.y - CGFloat(sin(angle/180.0 * Double.pi)) * height * amplitude
path.addLine(to: CGPoint(x: x, y: y))
}

Globals.sharedInstance.palleteGlowGreen.setStroke()
path.stroke()


How would I:


  1. Draw multiple periods for the same width (now its 0-360)

  2. Change number 1 (num of periods) dynamically so you see the wave squeezing.


Rob Rob
Answer

Just multiply the angle by periods:

@IBDesignable
class SineWaveView: UIView {

    @IBInspectable
    var graphWidth: CGFloat = 0.90  { didSet { setNeedsDisplay() } }

    @IBInspectable
    var amplitude: CGFloat = 0.20   { didSet { setNeedsDisplay() } }

    @IBInspectable
    var periods: CGFloat = 1.0      { didSet { setNeedsDisplay() } }

    override func draw(_ rect: CGRect) {
        let width = bounds.width
        let height = bounds.height

        let origin = CGPoint(x: width * (1 - graphWidth) / 2, y: height * 0.50)

        let path = UIBezierPath()
        path.move(to: origin)

        for angle in stride(from: 5.0, through: 360.0 * periods, by: 5.0) {
            let x = origin.x + angle/(360.0 * periods) * width * graphWidth
            let y = origin.y - sin(angle/180.0 * .pi) * height * amplitude
            path.addLine(to: CGPoint(x: x, y: y))
        }

        Globals.sharedInstance.palleteGlowGreen.setStroke()
        path.stroke()
    }

}

By the way, by making a change in periods call setNeedsDisplay, that means that when you update periods, the graph will be automatically redrawn.


And, rather than iterating through degrees, but then converting everything back to radians, I might just stay in radians:

override func draw(_ rect: CGRect) {
    let width = bounds.width
    let height = bounds.height

    let origin = CGPoint(x: width * (1 - graphWidth) / 2, y: height * 0.50)
    let maxAngle: CGFloat = 2 * .pi * periods
    let iterations = Int(min(1000, 100 * periods))

    let point = { (angle: CGFloat) -> CGPoint in
        let x = origin.x + angle/maxAngle * width * self.graphWidth
        let y = origin.y - sin(angle) * height * self.amplitude
        return CGPoint(x: x, y: y)
    }

    let path = UIBezierPath()
    path.move(to: point(0))

    for i in 1 ... iterations {
        path.addLine(to: point(maxAngle * CGFloat(i) / CGFloat(iterations)))
    }

    Globals.sharedInstance.palleteGlowGreen.setStroke()
    path.stroke()
}
Comments