Можно ли построить график полярной функции с помощью UIBezierPath? Я говорю не только о кругах, я говорю о кардиоидах, лимаконах, лемнискатах и т. д. В основном у меня есть один UIView, и я хочу нарисовать форму в представлении.
Графики полярных функций с помощью UIBezierPath
Ответы (2)
Для таких фигур нет встроенных методов, но вы всегда можете аппроксимировать их серией очень коротких прямых линий. У меня была причина аппроксимировать круг таким образом, и круг с ~ 100 прямыми линиями выглядит идентично кругу, нарисованному с помощью ovalInRect. При этом было проще всего сначала создать точки в полярных координатах, а затем преобразовать их в цикле в прямоугольные координаты, прежде чем передать массив точек методу, в котором я добавляю линии к пути Безье.
Вот моя быстрая вспомогательная функция (полностью прокомментированная), которая генерирует координаты (x, y) в данном CGRect из функции полярных координат.
func cartesianCoordsForPolarFunc(frame: CGRect, thetaCoefficient:Double, thetaCoefficientDenominator:Double, cosScalar:Double, iPrecision:Double) -> Array<CGPoint> {
// Frame: The frame in which to fit this curve.
// thetaCoefficient: The number to scale theta by in the cos.
// thetaCoefficientDenominator: The denominator of the thetaCoefficient
// cosScalar: The number to multiply the cos by.
// iPrecision: The step for continuity. 0 < iPrecision <= 2.pi. Defaults to 0.1
// Clean inputs
var precision:Double = 0.1 // Default precision
if iPrecision != 0 {// Can't be 0.
precision = iPrecision
}
// This is ther polar function
// var theta: Double = 0 // 0 <= theta <= 2pi
// let r = cosScalar * cos(thetaCoefficient * theta)
var points:Array<CGPoint> = [] // We store the points here
for theta in stride(from: 0, to: 2*Double.pi * thetaCoefficientDenominator, by: precision) { // Try to recreate continuity
let x = cosScalar * cos(thetaCoefficient * theta) * cos(theta) // Convert to cartesian
let y = cosScalar * cos(thetaCoefficient * theta) * sin(theta) // Convert to cartesian
let scaled_x = (Double(frame.width) - 0)/(cosScalar*2)*(x-cosScalar)+Double(frame.width) // Scale to the frame
let scaled_y = (Double(frame.height) - 0)/(cosScalar*2)*(y-cosScalar)+Double(frame.height) // Scale to the frame
points.append(CGPoint(x: scaled_x, y:scaled_y)) // Add the result
}
return points
}
Учитывая эти точки, вот пример того, как вы могли бы нарисовать UIBezierPath
. В моем примере это пользовательская функция UIView, которую я бы назвал UIPolarCurveView.
let flowerPath = UIBezierPath() // Declare my path
// Custom Polar scalars
let k: Double = 9/4
let length = 50
// Draw path
let points = cartesianCoordsForPolarFunc(frame: frame, thetaCoefficient: k, thetaCoefficientDenominator:4 cosScalar: length, iPrecision: 0.01) flowerPath.move(to: points[0])
for i in 2...points.count {
flowerPath.addLine(to: points[i-1])
}
flowerPath.close()
Вот результат: PS: Если вы планируете иметь несколько графиков в одном кадре, обязательно измените дополнение масштабирования, сделав второй
cosScalar
самым большим из используется cosScalars
. Вы можете сделать это, добавив аргумент к функции в примере.