Path's Bezier Curve

1. Path common method table

For compatibility (lazy), the methods that were added after API 21 (ie, Android version 5.0) have been removed from this table. I can't help but complain, why it seems that some overloaded methods that can be written easily have to wait until API21 is added. The baby's heart is broken at the moment.

effect Related methods Remark
Move the starting point moveTo Move the starting position of the next operation
set end point setLastPoint Reset the position of the last point in the current path, if called before drawing, the effect is the same as moveTo
connecting straight lines lineTo Add a line from the previous point to the current point to Path
closed path close Connect the first point to the last point to form a closed area
add content addRect, addRoundRect, addOval, addCircle, addPath, addArc, arcTo Add (rectangle, rounded rectangle, ellipse, circle, path, arc) to the current Path (note the difference between addArc and arcTo)
is empty isEmpty Check if Path is empty
Is it a rectangle isRect Determine if path is a rectangle
replace path set Replace all contents of the current path with the new path
offset path offset Offset the operation before the current path (does not affect the operation after)
Bezier curve quadTo, cubicTo Methods for quadratic and cubic bezier curves respectively
rXxx methods rMoveTo, rLineTo, rQuadTo, rCubicTo The method without r is based on the origin coordinate system (offset), and the rXxx method is based on the current point coordinate system (offset)
fill pattern setFillType, getFillType, isInverseFillType, toggleInverseFillType Set, get, judge and switch fill mode
Tips incReserve Prompt how many points in Path are waiting to be added** (this method seems to allow Path to optimize the storage structure)**
Boolean operations (API19) op Perform Boolean operations on two Path s (ie, take intersection, union, etc.)
Calculate the boundaries computeBounds Calculate the bounds of Path
reset path reset, rewind Clear the contents of Path
reset does not preserve internal data structures, but does preserve FillType.
rewind will preserve the internal data structure, but not the FillType
matrix operations transform matrix transformation

2. Detailed Path

What can bezier curves do?

The use of Bezier curves is very extensive. It can be said that Bezier curves laid the foundation of computer graphics (because it can describe any complex graphics in precise mathematical language), and you have used it inadvertently. .

If you use Photoshop, you may notice that there is a pen tool in it. The core of this pen tool is a bezier curve.

You said you can't PS? It doesn't matter, if you have read the previous articles or used 2D drawing, you must have drawn circles, arcs, rounded rectangles and so on. The arc part here is all the use of Bezier curves.

Bezier curves have a very wide range of functions, just to name a few chestnuts:

  • QQ little red dot drag effect
  • Some cool pull-to-refresh controls
  • Flip book effect of reading software
  • Making some smooth line charts
  • Lots of cool animation effects

 

How to get started with Bezier curves easily?

Although Bezier curves are widely used, there seems to be no suitable Chinese tutorials at present. The Chinese articles about Bezier curves that can be searched for Android can be basically divided into the following categories:

  • Popular science type (just let people know about Bezier, no substantive content)
  • Pretentious (put out a lot of formulas, quote a bunch of English originals)
  • Basic type (just explain the usage of two functions of Bezier curve)
  • Practical type (according to examples to explain the use of Bezier curves)

The more useful of the above types are the basic type and the actual type, but both have their own shortcomings. This article will synthesize the content of the two and learn the Bezier curve from scratch.

 

Step 1. Understand the principles of Bezier curves

Understanding the Bezier curve here is not to learn the derivation process of the formula, but to understand how the Bezier curve is generated. Bezier curves use a series of points to control the state of the curve. I simply divide these points into two categories:

type effect
data point Determine where the curve starts and ends
control point Determine how much the curve bends

For the time being, it is only for understanding the concept, and the detailed meaning will be explained next.

First-order curve principle:

The first-order curve has no control points, only two data points (A and B), and the final effect is a line segment.

The above figure represents a certain stage in the generation process of the first-order curve. The dynamic process can refer to the figure below (the dynamic demonstration pictures related to the Bezier curve in this article are from Wikipedia)

PS: The first-order curve is actually the lineTo explained earlier.

The second-order curve principle:

The second-order curve is described by two data points (A and C) and a control point (B), which is roughly as follows:

The red curve part in the above figure is the legendary second-order Bezier curve, so how is this red curve generated? Next, let's analyze one of the states:

Connect AB BC, and take point D on AB and point E on BC, so that it satisfies the conditions:

Connect DE, take point F, such that:

The point F obtained in this way is a point on the Bezier curve, and the dynamic process is as follows:

PS: The method corresponding to the second-order curve is quadTo

 

Principle of third-order curve:

The third-order curve is described by two data points (A and D) and two control points (B and C), as follows:

The calculation process of the third-order curve is similar to that of the second-order curve. For details, see the dynamic effect in the following figure:

PS: The method corresponding to the third-order curve is cubicTo

Bezier Curve Cheat Sheet

highly recommended click here Practice Bezier curves to deepen your understanding of Bezier curves.

First-order curve:

A first-order curve is a line segment, which is very simple, see the previous article Basic operation of Path , will not be explained in detail here.

Second order curve:

Through the simple understanding of the second-order curve above, we know that the second-order curve is composed of two data points and one control point. Next, we will use an example to demonstrate how the second-order curve is used.

First of all, the two data points control the position where the Bezier curve starts and ends, which is easier to understand, while the control points control the bending state of the Bezier, which is relatively difficult to understand, so this example focuses on understanding Bezier The relationship between the bending state of the curve and the control point, not much nonsense, first on the renderings:

In order to make it easier to see the relationship between the control points and the degree of curvature of the curve, the auxiliary points and auxiliary lines are drawn in the above figure. It can be seen from the dynamic diagram above that the Bezier curve is similar to a rubber band in the dynamic change process. The elastic effect is very common when making some elastic effects.

The main code is as follows:

public class Bezier extends View {

    private Paint mPaint;
    private int centerX, centerY;

    private PointF start, end, control;

    public Bessel1(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(8);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setTextSize(60);

        start = new PointF(0,0);
        end = new PointF(0,0);
        control = new PointF(0,0);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w/2;
        centerY = h/2;

        // Initialize the location of data points and control points
        start.x = centerX-200;
        start.y = centerY;
        end.x = centerX+200;
        end.y = centerY;
        control.x = centerX;
        control.y = centerY-100;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Update control points based on touch position and prompt to redraw
        control.x = event.getX();
        control.y = event.getY();
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // Plot data points and control points
        mPaint.setColor(Color.GRAY);
        mPaint.setStrokeWidth(20);
        canvas.drawPoint(start.x,start.y,mPaint);
        canvas.drawPoint(end.x,end.y,mPaint);
        canvas.drawPoint(control.x,control.y,mPaint);

        // draw auxiliary lines
        mPaint.setStrokeWidth(4);
        canvas.drawLine(start.x,start.y,control.x,control.y,mPaint);
        canvas.drawLine(end.x,end.y,control.x,control.y,mPaint);

        // draw bezier curves
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(8);

        Path path = new Path();

        path.moveTo(start.x,start.y);
        path.quadTo(control.x,control.y,end.x,end.y);

        canvas.drawPath(path, mPaint);
    }
}

Third-order curve:

A third-order curve has two data points and two control points to control the curve state.

Code:

public class Bezier2 extends View {

    private Paint mPaint;
    private int centerX, centerY;

    private PointF start, end, control1, control2;
    private boolean mode = true;

    public Bezier2(Context context) {
        this(context, null);

    }

    public Bezier2(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(8);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setTextSize(60);

        start = new PointF(0, 0);
        end = new PointF(0, 0);
        control1 = new PointF(0, 0);
        control2 = new PointF(0, 0);
    }

    public void setMode(boolean mode) {
        this.mode = mode;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;

        // Initialize the location of data points and control points
        start.x = centerX - 200;
        start.y = centerY;
        end.x = centerX + 200;
        end.y = centerY;
        control1.x = centerX;
        control1.y = centerY - 100;
        control2.x = centerX;
        control2.y = centerY - 100;

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Update control points based on touch position and prompt to redraw
        if (mode) {
            control1.x = event.getX();
            control1.y = event.getY();
        } else {
            control2.x = event.getX();
            control2.y = event.getY();
        }
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //drawCoordinateSystem(canvas);

        // Plot data points and control points
        mPaint.setColor(Color.GRAY);
        mPaint.setStrokeWidth(20);
        canvas.drawPoint(start.x, start.y, mPaint);
        canvas.drawPoint(end.x, end.y, mPaint);
        canvas.drawPoint(control1.x, control1.y, mPaint);
        canvas.drawPoint(control2.x, control2.y, mPaint);

        // draw auxiliary lines
        mPaint.setStrokeWidth(4);
        canvas.drawLine(start.x, start.y, control1.x, control1.y, mPaint);
        canvas.drawLine(control1.x, control1.y,control2.x, control2.y, mPaint);
        canvas.drawLine(control2.x, control2.y,end.x, end.y, mPaint);

        // draw bezier curves
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(8);

        Path path = new Path();

        path.moveTo(start.x, start.y);
        path.cubicTo(control1.x, control1.y, control2.x,control2.y, end.x, end.y);

        canvas.drawPath(path, mPaint);
    }
}

Compared with the second-order curve, the third-order curve can make more complex shapes, but for the high-order curve, the combination of the low-order curve can also achieve the same effect, which is the legendary reduction. Therefore, our packaging method for Bezier curves is generally only up to third-order curves.

Descending and Ascension

type Paraphrase Variety
downgrade In the case of keeping the shape and direction of the curve unchanged, reduce the number of control points, that is, reduce the order of the curve Methods become simpler, more data points, possibly fewer control points, less flexibility
Ascending Order In the case of keeping the shape and direction of the curve unchanged, increase the number of control points, that is, increase the curve order The method is more complex, the data points are unchanged, the control points are increased, and the flexibility is increased

 

The third step. Bezier curve use example

Before making this example, we must first clarify a content, that is, under what circumstances do you need to use the Bezier curve?

Need to draw irregular shapes? of course not! At present, I think the use of the Bezier curve mainly has the following aspects (only my personal opinion, there may be errors, welcome to correct)

serial number content Example
1 When the curve state is not known in advance and real-time calculation is required Smooth line graph of weather forecast temperature changes
2 When the display state changes according to user operations QQ little red dot, simulates the effect of flipping a book
3 Some more complex motion states (used with PathMeasure) Animation of complex motion states

As for the case where only a static curve graph is needed, wouldn't it be better to use a picture, and a lot of calculations would be very uneconomical.

If it is to display SVG vector graphics, there are already relevant analysis tools (Bézier curves are still used internally), and no manual calculation is required.

The main advantage of the Bezier curve is that the state of the curve can be controlled in real time, and the state of the curve can be smoothly changed in real time by changing the state of the control point.

Next we use a simple example to make a circle gradient into a heart shape:

Effect picture:

Thought analysis:

The final effect we need is to transform a circle into a heart shape. Through analysis, we can see that a circle can be composed of four third-order Bezier curves, as follows:

The heart shape can also be composed of a four-segment third-order Bezier curve, as follows:

The only difference between the two is the position of the data points and control points, so you only need to adjust the positions of the data points and control points to turn the circle into a heart shape.

Core difficulty:

1. How to get the positions of data points and control points?

The use of data points and control points for drawing circles has already been calculated in detail, you can refer to an answer on stackoverflow How to create circle with Bézier curves? The data in it only needs to be used.

For the heart-shaped data points and control points, it can be obtained by translating some of the circular data points and control points, and the specific parameters can be slowly adjusted to a satisfactory effect.

2. How to achieve the gradient effect?

Gradient is actually moving the data points and control points a little bit each time, and then redrawing the interface, adjusting the data points and control points many times in a short period of time to gradually approach the target value, and achieving a gradient through continuous redrawing of the interface. Effect. The process can refer to the following dynamic effect:

public class Bezier3 extends View {
    private static final float C = 0.551915024494f;     // A constant used to calculate the position of the control points to draw the circular bezier curve

    private Paint mPaint;
    private int mCenterX, mCenterY;

    private PointF mCenter = new PointF(0,0);
    private float mCircleRadius = 200;                  // the radius of the circle
    private float mDifference = mCircleRadius*C;        // The difference between the circle's control points and the data points

    private float[] mData = new float[8];               // Clockwise record the four data points that draw the circle
    private float[] mCtrl = new float[16];              // Clockwise record the eight control points for drawing the circle

    private float mDuration = 1000;                     // total change time
    private float mCurrent = 0;                         // Current elapsed time
    private float mCount = 100;                         // How many times to divide the time in total
    private float mPiece = mDuration/mCount;            // length of each


    public Bezier3(Context context) {
        this(context, null);

    }

    public Bezier3(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(8);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setTextSize(60);


        // Initialize data points

        mData[0] = 0;
        mData[1] = mCircleRadius;

        mData[2] = mCircleRadius;
        mData[3] = 0;

        mData[4] = 0;
        mData[5] = -mCircleRadius;

        mData[6] = -mCircleRadius;
        mData[7] = 0;

        // Initialize control point

        mCtrl[0]  = mData[0]+mDifference;
        mCtrl[1]  = mData[1];

        mCtrl[2]  = mData[2];
        mCtrl[3]  = mData[3]+mDifference;

        mCtrl[4]  = mData[2];
        mCtrl[5]  = mData[3]-mDifference;

        mCtrl[6]  = mData[4]+mDifference;
        mCtrl[7]  = mData[5];

        mCtrl[8]  = mData[4]-mDifference;
        mCtrl[9]  = mData[5];

        mCtrl[10] = mData[6];
        mCtrl[11] = mData[7]-mDifference;

        mCtrl[12] = mData[6];
        mCtrl[13] = mData[7]+mDifference;

        mCtrl[14] = mData[0]-mDifference;
        mCtrl[15] = mData[1];
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCenterX = w / 2;
        mCenterY = h / 2;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
         drawCoordinateSystem(canvas);       // drawing coordinate system

        canvas.translate(mCenterX, mCenterY); // Move the coordinate system to the center of the canvas
        canvas.scale(1,-1);                 // Flip the Y axis

        drawAuxiliaryLine(canvas);


        // draw bezier curves
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(8);

        Path path = new Path();
        path.moveTo(mData[0],mData[1]);

        path.cubicTo(mCtrl[0],  mCtrl[1],  mCtrl[2],  mCtrl[3],     mData[2], mData[3]);
        path.cubicTo(mCtrl[4],  mCtrl[5],  mCtrl[6],  mCtrl[7],     mData[4], mData[5]);
        path.cubicTo(mCtrl[8],  mCtrl[9],  mCtrl[10], mCtrl[11],    mData[6], mData[7]);
        path.cubicTo(mCtrl[12], mCtrl[13], mCtrl[14], mCtrl[15],    mData[0], mData[1]);

        canvas.drawPath(path, mPaint);

        mCurrent += mPiece;
        if (mCurrent < mDuration){

            mData[1] -= 120/mCount;
            mCtrl[7] += 80/mCount;
            mCtrl[9] += 80/mCount;

            mCtrl[4] -= 20/mCount;
            mCtrl[10] += 20/mCount;

            postInvalidateDelayed((long) mPiece);
        }
    }

    // draw auxiliary lines
    private void drawAuxiliaryLine(Canvas canvas) {
        // Plot data points and control points
        mPaint.setColor(Color.GRAY);
        mPaint.setStrokeWidth(20);

        for (int i=0; i<8; i+=2){
            canvas.drawPoint(mData[i],mData[i+1], mPaint);
        }

        for (int i=0; i<16; i+=2){
            canvas.drawPoint(mCtrl[i], mCtrl[i+1], mPaint);
        }


        // draw auxiliary lines
        mPaint.setStrokeWidth(4);

        for (int i=2, j=2; i<8; i+=2, j+=4){
            canvas.drawLine(mData[i],mData[i+1],mCtrl[j],mCtrl[j+1],mPaint);
            canvas.drawLine(mData[i],mData[i+1],mCtrl[j+2],mCtrl[j+3],mPaint);
        }
        canvas.drawLine(mData[0],mData[1],mCtrl[0],mCtrl[1],mPaint);
        canvas.drawLine(mData[0],mData[1],mCtrl[14],mCtrl[15],mPaint);
    }

    // drawing coordinate system
    private void drawCoordinateSystem(Canvas canvas) {
        canvas.save();                      // draw coordinate system

        canvas.translate(mCenterX, mCenterY); // Move the coordinate system to the center of the canvas
        canvas.scale(1,-1);                 // Flip the Y axis

        Paint fuzhuPaint = new Paint();
        fuzhuPaint.setColor(Color.RED);
        fuzhuPaint.setStrokeWidth(5);
        fuzhuPaint.setStyle(Paint.Style.STROKE);

        canvas.drawLine(0, -2000, 0, 2000, fuzhuPaint);
        canvas.drawLine(-2000, 0, 2000, 0, fuzhuPaint);

        canvas.restore();
    }
}

3. Summary

In fact, the most important thing about Bezier curves is the core understanding of how Bezier curves are generated. Only by understanding how Bezier curves are generated can we make better use of Bezier curves. At the end of the last article, I said that this article will involve a bit of self-intersecting graphics rendering. Unfortunately, this article is gone. Please look forward to the next article (may appear in the next article o( ̄︶ ̄)o), the next article The article is still Path related content, teach you some more fun things.

Reference article

https://github.com/GcsSloop/AndroidNote/blob/master/CustomView/README.md

Tags: Android Path

Posted by mostwantedunm on Sun, 15 May 2022 06:59:43 +0300