不可靠的UIBezierPath曲线机制,控制点和曲线点 [英] Undestanding UIBezierPath curving mechanism, controlPoint and the curve point
问题描述
我试图用 UIBezierPath
绘制一个简单的抛物线形状。我有一个 maxPoint
和一个 boundingRect
,其中我基于抛物线的宽度和拉伸。
这是我绘制抛物线的函数(我在容器视图中绘制抛物线, rect
将会是 container.bounds <
$ b
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)
//绘图代码
...
}
我的问题是,我想要 那么这条道路究竟在做什么呢?或者换句话说,我如何从 对于may应用程序,adam.wulf的解决方案很好,但它实际上并不创建抛物线。要创建抛物线,我们需要计算给定二次曲线中点的控制点。贝塞尔路径只是数学;我们可以很容易地计算出来。我们只需要颠倒Bézier函数并在t = 0.5时求解。 其中 (计算Bézier在其他点不是很直观。 t = 0.25并不是沿路的四分之一。但幸运的是,对于我们的目的而言,t = 0.5与我们对二次方的中点的直觉相当吻合。) 考虑到我们的解决方案,我们可以编写我们的代码。原谅翻译Swift 3;我的Xcode 7.3版本对iOS游戏机并不满意,但应该很容易转换为2.2。 maxPoint $ c $我发现函数是抛物线本身的实际极端点。因此,例如,如果我发送
(CGRectGetMidX(container.bounds),0)
,最高点应该位于最顶端的中心。但是在使用这个特定点的函数时,结果如下所示:
controlPoint
获得我需要的实际最大点?我试着根据 boundingRect
的高度从 y
值中加入和减去不同的值,但是我无法完全找到正确的组合,因为在具有不同 y
值的不同点上,它的行为有所不同。似乎有某种倍增器被添加进来,我该如何解决它?
<0.5>Bézier解0.5(中点)很好地在。 p>
2 * Pc - P0 / 2 - P2 / 2
Pc
是我们想要经过的点, P0
和 P2
是终点。
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)
让p2 = CGPoint(x:boundingRect.maxX,y:boundingRect.maxY)
让p1 = CGPoint(x:halfPoint1D(p0:
y:halfPoint1D(p0:p0.y,p2:p2.y,control:maxPoint.y))
path.move(to:p0)
path.addQuadCurve(到:p2,controlPoint:p1)
返回路径
}
halfPoint1D
函数是我们解决方案的一维实现。对于我们的二维 CGPoint
,我们只需要调用它两次。
如果我可以推荐一个资源为了理解Bézier曲线,它可能是构造Bézier曲线部分来自维基百科。研究显示曲线如何产生的小动画我觉得非常有启发性。 具体案例部分也很有用。为了深入探讨这个话题(我建议所有开发者都熟悉这个话题),我喜欢 A Bézier曲线入门。可以浏览它,只需阅读目前您感兴趣的部分即可。但是对这组函数的基本了解将大大减少Core Graphics绘制中的魔法,并使UIBezierPath成为一个工具而不是黑盒子。
I'm trying to draw a simple Parabola shape using UIBezierPath
. I have a maxPoint
and a boundingRect
of which I'm basing the width and stretch of the parabola.
Here's the function I made to draw the parabola (I draw the parabola in a container view, rect
will be 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
...
}
My problem is, that I want the maxPoint
that I send in the function to be the actual extreme point in the parabola itself. So for example, if I send in (CGRectGetMidX(container.bounds), 0)
, The maximum point should be at the top-most center. But in using this function with this particular point, this is what the result looks like:
So what exactly the path does here? Or in other words, how can I get from the controlPoint
to the actual max point that I need? I've tried adding and subtracting different values from the y
value, based on the height of the boundingRect
, but I couldn't quite find the right combination, as in different points with different y
values it behaves differently. There seem to be some kind of multiplier being added in, how can I solve it?
For may applications adam.wulf's solution is fine, but it doesn't actually create a parabola. To create a parabola, we need to compute the control point given the midpoint of the quadratic curve. Bézier paths are just math; we can compute this quite easily. We just need to invert the Bézier function and solve it for t=0.5.
The Bézier solution at 0.5 (the midpoint) is derived nicely at Draw a quadratic Bézier curve through three given points.
2*Pc - P0/2 - P2/2
Where Pc
is the point we want to go through and P0
and P2
are the end points.
(Computing the Bézier at other points is not very intuitive. The value at t=0.25 is not "a quarter of the way along the path." But luckily for our purposes, t=0.5 matches quite nicely to our intuition of "the midpoint" on a quadratic.)
Given our solution, we can write our code. Forgive the translation to Swift 3; my copy of Xcode 7.3 isn't very happy with iOS playgrounds, but it should be easy to convert to 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
}
The halfPoint1D
function is the the one-dimensional implementation of our solution. For our two-dimentional CGPoint
, we just have to call it twice.
If I could recommend just one resource for understanding Bézier curves, it would probably be the "Constructing Bézier curves" section from Wikipedia. Studying the little animations that show how the curves come about I find very enlightening. The "Specific Cases" section is useful as well. For a deep exploration of the topic (and one that I recommend all developers have a passing familiarity with), I like A Primer on Bézier Curves. It's ok to skim it and just read the parts that interest you at the moment. But a basic understanding of this group of functions will go a long way to removing the magic from drawing in Core Graphics and make UIBezierPath a tool rather than a black box.
这篇关于不可靠的UIBezierPath曲线机制,控制点和曲线点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!