“Pen” refers to the user through the fingers, tools or the mouse drag on the screen to simulate real scenarios pen to write words, the effect of the writing process is according to our writing speed of real time changing, by pen algorithm we can simulate the real situations at the time of writing at the end of the line change and take effect.
So how do we draw a stroke line?
This paper will introduce the basic concept of Bezier curve and the algorithm of stroke trajectory to explain how to use Bezier curve to achieve the function of stroke drawing.
1. Bessel curve
First of all, what is a Bezier curve?
According to Wikipedia, the Bezier curve was widely published in 1962 by French engineer Pierre Bezier, who used it to design the body of a car. Bessel curve was first developed by Paul de Castello in 1959 using de Castello algorithm to obtain Bessel curve with stable numerical method.
In the field of numerical analysis of mathematics, Bezier curve (English: Bezier curve) is an important parametric curve in computer graphics. The generalized Bezier curves of higher dimensions are called Bezier surfaces, of which bezier triangle is a special instance.
Basic use of UIBezierPath
Next, I will explain how to generate a Bezier curve path and the principle of the brush stroke.
Here we use the iOS UIBezierPath class to illustrate:
UIBezierPath is a wrapper around the CGPathRef data type. If the path is based on a vector shape, use lines and curves to create it.
As you can see from UIBezierPath’s API, the system has provided a rich interface for drawing a Bessel trajectory:
UIKIT_EXTERN API_AVAILABLE(ios(3.2)) @interface UIBezierPath: NSObject<NSCopying, NSSecureCoding> + (instancetype)bezierPath; + (instancetype)bezierPathWithRect:(CGRect)rect; + (instancetype)bezierPathWithOvalInRect:(CGRect)rect; + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii; + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise; + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath; - (instancetype)init NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; // Returns an immutable CGPathRef which is only valid until the UIBezierPath is further mutated. // Setting the path will create an immutable copy of the provided CGPathRef, so any further mutations on a provided CGMutablePathRef will be ignored. @property(nonatomic) CGPathRef CGPath; - (CGPathRef)CGPath NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED; // Path construction - (void)moveToPoint:(CGPoint)point; - (void)addLineToPoint:(CGPoint)point; - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2; - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint; - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle EndAngle: CGFloat endAngle clockwise (BOOL) clockwise API_AVAILABLE (ios (4.0)); - (void)closePath; - (void)removeAllPoints; // Appending paths - (void)appendPath:(UIBezierPath *)bezierPath;Copy the code
How to instantiate a UIBezierPath. The following illustration illustrates implementing a second-order BezierPath:
CGFloat speedFactor// CGFloat speed = distance ✖️ speedFactor; // Find the speedCopy the code
Three, the realization of the brush
Now that we’ve seen the general usage of UIBezierPath and how to generate a Bessel path, let’s take a look at how to generate a stroke path through UIBezierPath.
We know that the width of the path needs to be changed according to the drawing speed to achieve the brush edge. The speed can be obtained according to the distance/time we know, as shown in the following code:
CGFloat speedFactor// CGFloat speed = distance ✖️ speedFactor; // Find the speedCopy the code
Based on the speed value obtained above, we can use it to calculate the current LineWidth of our path (the function to calculate the LineWidth here is not described in detail, but can be changed according to the specific business requirements). After obtaining the LineWidth, we can calculate the two left and right points of the current point, leftPoint and rightPoint.
As shown below:
Through the above method, we can get the left and right 2 points of each point. Therefore, every 2 points collected on the screen can obtain 4 points of the rectangle, and a rectangle can be obtained by connecting the 4 points, and then a stroke path can be formed through the splicing of the rectangle.
UIBezierPath *bezierPath = [UIBezierPath bezierPath]; [bezierPath moveToPoint:leftPoint1]; [bezierPath addLineToPoint:leftPoint2]; [bezierPath addLineToPoint:rightPoint2]; [bezierPath addLineToPoint:rightPoint1]; [bezierPath addLineToPoint:leftPoint1]; [bezierPath closePath];Copy the code
Now suppose the points captured on the screen are startPoint, point1, point2, and endPoint. How do you draw a stroke path from these four points?
As shown below:
StartPoint ->leftPoint1->leftPoint2->endPoint->rightPoint2->rightPoint1->startPoint StartPoint ->point1->point2→endPoint
Trajectory optimization
Of course, we can also optimize the trajectory shown in the figure above to make it look smoother. We can use a second-order Bezier curve to connect the two points and make the trajectory look smoother.
The system also provides corresponding apis for us to use:
/// Bessel - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2; /// - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;Copy the code
The effect achieved by the above methods is shown in the figure below:
Four, conclusion
This paper gives a basic introduction to the usage and usage scenarios of UIBezierPath in iOS. We can see that UIBezierPath provides many convenient API methods. If we skillfully use these apis in actual scenarios, Combined with some of our own algorithms can easily draw a variety of graphics.