Undestanding UIBezierPath弯曲机制,controlPoint和曲线点

时间:2016-08-02 14:19:28

标签: ios swift graphics uibezierpath

我试图使用UIBezierPath绘制一个简单的抛物线形状。我有一个maxPoint和一个boundingRect,其中我基于抛物线的宽度和伸展。
这是我绘制抛物线的函数(我在容器视图中绘制抛物线,rect将是container.bounds):

func addParabolaWithMax(maxPoint: CGPoint, inRect boundingRect: CGRect) {
    let path = UIBezierPath()

    let p1 = CGPointMake(1, CGRectGetMaxY(boundingRect)-1)
    let p3 = CGPointMake(CGRectGetMaxX(boundingRect)-1, CGRectGetMaxY(boundingRect)-1)

    path.moveToPoint(p1)
    path.addQuadCurveToPoint(p3, controlPoint: maxPoint)

    // Drawing code
    ...
}

我的问题是,我希望函数中发送的maxPoint成为抛物线本身的实际极值点。例如,如果我发送(CGRectGetMidX(container.bounds), 0),则最高点应位于最顶部的中心。但是在使用这个特殊点的函数时,这就是结果:

enter image description here

那么这条道路究竟是什么呢?或者换句话说,我怎样才能从controlPoint到达我需要的实际最大点?我已经尝试根据y的高度在boundingRect值中添加和减去不同的值,但我无法找到正确的组合,就像在不同的点上一样不同的y值表现不同。似乎有某种乘数被添加,我该如何解决?

3 个答案:

答案 0 :(得分:7)

对于may应用程序adam.wulf的解决方案很好,但它实际上并没有创建一个抛物线。要创建抛物线,我们需要计算给定二次曲线中点的控制点。 Bézier路径只是数学;我们可以很容易地计算出来。我们只需要反转Bézier函数并在t = 0.5时解决它。

Draw a quadratic Bézier curve through three given points处很好地得出了0.5(中点)的Bézier解。

2*Pc - P0/2 - P2/2

Pc是我们要通过的点,P0P2是终点。

(在其他点计算Bézier不是很直观.t = 0.25时的值不是“沿路径的四分之一。”但幸运的是,对于我们的目的,t = 0.5与我们的直觉相匹配二次方的“中点”。)

鉴于我们的解决方案,我们可以编写代码。原谅Swift 3的翻译;我的Xcode 7.3副本对iOS游乐场不是很满意,但它应该很容易转换为2.2。

func addParabolaWithMax(maxPoint: CGPoint, inRect boundingRect: CGRect) -> UIBezierPath {

    func halfPoint1D(p0: CGFloat, p2: CGFloat, control: CGFloat) -> CGFloat {
        return 2 * control - p0 / 2 - p2 / 2
    }

    let path = UIBezierPath()

    let p0 = CGPoint(x: 0, y: boundingRect.maxY)
    let p2 = CGPoint(x: boundingRect.maxX, y: boundingRect.maxY)

    let p1 = CGPoint(x: halfPoint1D(p0: p0.x, p2: p2.x, control: maxPoint.x),
                     y: halfPoint1D(p0: p0.y, p2: p2.y, control: maxPoint.y))

    path.move(to: p0)
    path.addQuadCurve(to: p2, controlPoint: p1)
    return path
}

halfPoint1D函数是我们解决方案的一维实现。对于我们的二维CGPoint,我们只需要调用它两次。

如果我只推荐一种资源来理解Bézier曲线,它可能是维基百科的"Constructing Bézier curves"部分。研究显示曲线如何形成的小动画,我发现非常具有启发性。 “特定案例”部分也很有用。为了深入探讨这个主题(我建议所有开发人员都熟悉这个主题),我喜欢A Primer on Bézier Curves。可以浏览它,只需阅读您感兴趣的部分。但是对这组函数的基本理解将大大有助于消除绘制Core Graphics中的魔力并使UIBezierPath成为工具而不是黑盒子。

答案 1 :(得分:1)

诀窍是将曲线分成两部分,以便您可以控制曲线通过的点。正如Eduardo的回答中所提到的,控制点处理切线,终点在曲线上。这使您可以从左下角到顶部中心,然后从顶部中心到右下角:

let p1 = CGPointMake(0,self.view.frame.height/2)
let p3 = CGPointMake(self.view.frame.width,self.view.frame.height/2)
let ctrlRight = CGPointMake(self.view.frame.width,0)
let ctrlLeft = CGPointZero

let bezierPath = UIBezierPath()
bezierPath.moveToPoint(p1)
bezierPath.addCurveToPoint(maxPoint, controlPoint1: p1, controlPoint2: ctrlLeft)
bezierPath.addCurveToPoint(p3, controlPoint1: ctrlRight, controlPoint2: p3)

UIColor.blackColor().setStroke()
bezierPath.lineWidth = 1
bezierPath.stroke()

答案 2 :(得分:0)

let path = UIBezierPath()

        let p1 = CGPointMake(0,self.view.frame.height/2)
        let p3 = CGPointMake(self.view.frame.width,self.view.frame.height/2)

        path.moveToPoint(p1)
        path.addQuadCurveToPoint(p3, controlPoint: CGPoint(x: self.view.frame.width/2, y: -self.view.frame.height/2))

        let line = CAShapeLayer()
        line.path = path.CGPath;
        line.strokeColor = UIColor.blackColor().CGColor
        line.fillColor = UIColor.redColor().CGColor
        view.layer.addSublayer(line)

这就是原因:https://cdn.tutsplus.com/mobile/authors/legacy/Akiel%20Khan/2012/10/15/bezier.png你应该考虑切线概念