问题
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?
回答1:
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.
回答2:
The trick is to split the curve into two pieces so that you can control which points the curve passes through. As mentioned in Eduardo's answer, control points handle tangent, and end points are on the curve. This lets you have a curve from the bottom left to top center, then from top center to bottom right:
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()
回答3:
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)
this is the reason: https://cdn.tutsplus.com/mobile/authors/legacy/Akiel%20Khan/2012/10/15/bezier.png you should have to consider the tangent concept
来源:https://stackoverflow.com/questions/38723031/undestanding-uibezierpath-curving-mechanism-controlpoint-and-the-curve-point