我试图使用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)
,则最高点应位于最顶部的中心。但是在使用这个特殊点的函数时,这就是结果:
那么这条道路究竟是什么呢?或者换句话说,我怎样才能从controlPoint
到达我需要的实际最大点?我已经尝试根据y
的高度在boundingRect
值中添加和减去不同的值,但我无法找到正确的组合,就像在不同的点上一样不同的y
值表现不同。似乎有某种乘数被添加,我该如何解决?
答案 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
是我们要通过的点,P0
和P2
是终点。
(在其他点计算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你应该考虑切线概念