Как я могу использовать UIBezierPath для создания изогнутой линейной диаграммы с точками данных? - PullRequest
1 голос
/ 18 марта 2020

Я пытаюсь использовать UIBezierPath для создания линейного графика. Моя цель состоит в том, чтобы оживить этот график и сделать его «волнистым». Но я не могу найти путь для отражения своих данных.

Редактировать: добавленные точки данных

let dataPoints: [Double]


func wave(at elapsed: Double) -> UIBezierPath {
    let amplitude = CGFloat(50) - abs(fmod(CGFloat(elapsed/2), 3) - 1.5) * 100
    func f(_ x: Int) -> CGFloat {
        return sin(((CGFloat(dataPoints[x]*100) / bounds.width) + CGFloat(elapsed/2)) * 4 * .pi) * amplitude + bounds.midY
    }
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: f(0)))
    for x in 1..<dataPoints.count {
        path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
    }
    return path
}

1 Ответ

1 голос
/ 18 марта 2020

Несколько вопросов:

  1. Вы определили свой f(_:), но не используете его, а скорее используете dataPoints (который, как я вижу, не заполняется в любом месте) , Вероятно, проще всего просто напрямую использовать вашу функцию f.

  2. Вы добавляете кривые куби c Безье к своему пути, но ваши контрольные точки - это те же точки данных. Это не даст сглаживания. Контрольные точки должны быть точками до и после вашего значения x вдоль линии, касательной к рассматриваемой кривой (например, вдоль линии, определяемой точкой данных и наклоном, или первой производной вашей кривой).

    Это было бы не так сложно, но простое решение - просто использовать addLine. Если вы используете достаточное количество точек данных, оно будет выглядеть гладким.

  3. Когда вы сделаете это, это не будет хорошей гладкой синусоидой, если использовать только 12 точек данных. Вместо этого используйте намного большее число (120 или 360 или что-то еще). Ради единственной синусоиды, не беспокойтесь о количестве точек данных, если только вы не начнете видеть проблемы с производительностью.

  4. Ваши x значения в ваших CGPoint значениях меньше, чем вы, вероятно, хотели. Если вы только go из 0 .. <12, результирующий путь будет иметь ширину всего 12 точек; лол. Используйте значения <code>x для go по всему рассматриваемому виду.

Итак, вы можете получить что-то вроде:

func wave(at elapsed: Double) -> UIBezierPath {
    let amplitude = CGFloat(50) - abs(fmod(CGFloat(elapsed/2), 3) - 1.5) * 40
    func f(_ x: Int) -> CGFloat {
        return sin(((CGFloat(x) / bounds.width) + CGFloat(elapsed/2)) * 4 * .pi) * amplitude + bounds.midY
    }
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: f(0)))
    for x in 1...Int(bounds.width) {
        path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
    }
    return path
}

Жениться что с CADisplayLink для обновления кривой, и это дает:

sine curve

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...