SDDrawView profile


SDDrawView is a drawing board component based on Bezier curves. The current styles include lines, rectangles, circles, arrows and so on. It has the basic functions of adjusting palette color, line width, line color and so on. Later ready access picture alter, eraser function, add text and other functions. As for why we want to make SDDrawView as a tripartite drawing board component, in fact, SDDrawView is mostly similar to the current online drawing board component, which is a complete wheel building component. In fact, I found a lot of sketchpad components on the Internet. The arrows in the three sides are not very satisfactory. They are very stiff, and most of them are polygonal arrows composed of a rectangle and a triangle. However, the arrow style of SDDrawView is different. The arrow style of SDDrawView is more similar to the arrow in QQ screenshot, which is more smooth and closer to reality. Next, take a look at a demo of the SDDrawView.


SDDrawView fast integration


How to integrate SDDrawView quickly? Very simple, just download the SDDrawViewDemo, drag the SDDrawView folder from the Demo into your project, and import the header file as follows.

#import "SDDrawView.h"
Copy the code

SDDrawView initialization is also easy. We initialize a SDDrawView object and add it to the corresponding View.

- (SDDrawView *)drawView{SDDrawView *)drawView{if(_drawView == nil){ _drawView = [[SDDrawView alloc] initWithFrame:[UIScreen mainScreen].bounds]; _drawView.drawViewColor = [UIColor whiteColor]; Drawview. lineWidth = 2.0f; DrawStyle = styleline; // style _drawview. lineColor = [UIColor redColor]; // Line color}return _drawView;
    
}
Copy the code

The graph style selection is an enumerated value that allows you to draw different graphs simply by setting the corresponding style.

typedef enum : NSUInteger {
    DrawStyleLine,
    DrawStyleSquare,
    DrawStyleCircle,
    DrawStyleArrow
} DrawStyle;
Copy the code

Here’s a description of all the SDDrawView properties and methods.

Properties or methods instructions
drawViewColor Panel color
lineWidth The width of the brush
drawStyle Draw an enumerated value with a DrawStyle value
lineColor The brush color
– (void)cleanAction; Remove sketchpad
– (void)rollbackAction; Step back


SDDrawView principle and arrow drawing


The core of SDDrawView is drawing using Bezier curves. Detect the user’s touch points with touchesBegan, touchesMoved, and touchesEnded methods, and then call [self setNeedsDisplay]. Method for redrawing operation.

Each figure or line is a SDBezierPath object, an SDBezierPath object inherits from UIBezierPath, and each SDBezierPath object has the following member variables.

@property(nonatomic,strong) UIColor *lineColor; @property(nonatomic,assign) DrawStyle DrawStyle; // Draw style @property(nonatomic,assign) CGPoint startPoint; // For rectangles or circles, use the @property(nonatomic,assign) CGPoint endPoint; // This property is required for rectangles or circlesCopy the code

Lines, rectangles and circles are easy to implement, but what about arrows like the one below?

Here is to use the knowledge of polar coordinates, here you can look at the 3D graphics I wrote: matrix and linear transformation there is a part of the 2D transformation matrix, as shown in the figure below.

To give you a more intuitive understanding of the rotation matrix formula, we can assume that a vectorThe coordinates of phi are (x,y), so the rotated phiAnd then you get your new vector, thenWhat are the coordinates of phi (x prime,y prime),x prime, and y prime?

Assume that the vectorThe Angle with x is theta, then(Trigonometric function can be deduced from ~)

So we know thatThe Angle between it and the X-axis is zero, then there is the derivation process

The SDDrawView code is shown below.


- (CGPoint)rotateVecWithPx:(float)px py:(float)py ang:(double)ang newLen:(double)newLen{
    
    double vx = px * cos(ang) - py * sin(ang);
    double vy = px * sin(ang) + py * cos(ang);
    double d = sqrt(vx * vx + vy * vy);
    vx = vx / d * newLen;
    vy = vy / d * newLen;
    return CGPointMake((float) vx, (float) vy);
}

Copy the code

The above principle is really boring, so long to say how to deal with the actual process, we can see that all we need is the coordinates of 7 points can complete the drawing of an arrow polygon graph. We know the two coordinates on the axis of the arrow, the two points where the user starts and ends the touch. We can assume the length ratio and properties of the arrows. For example, the Angle of B and H to the central axis is 30 degrees, and the length ratio is 0.2; The angles of C and E to the central axis are 18 degrees, and the length ratio is 0.157; Both d and F are 90 degrees from the central axis (d and F are the relative coordinate origin O ‘). The length ratio is 0.023 as shown below.

// Initialize the arrow's relative fixed value KEY_POINT_LEN1 = 70; KEY_POINT_LEN2 = 55; KEY_POINT_LEN3 = 8; KEY_POINT_ANGLE1 = 30 * M_PI/ 180; KEY_POINT_ANGLE2 = 18 * M_PI/ 180; KEY_POINT_ANGLE3 = 90 * M_PI/ 180; KEY_POINT_RATIO1 = 0.2; KEY_POINT_RATIO2 = 0.157; KEY_POINT_RATIO3 = 0.023;Copy the code

The establishment of the origin of coordinate system explains: When bCED four points need to be known, the origin of coordinate system is on point A; When two points of fd are needed, the origin of the coordinates is at the point O ‘.

Through the above series of preparations, we can then calculate the coordinates of each point. The code is shown below.

// Double len1 = KEY_POINT_LEN1; // Double len1 = KEY_POINT_LEN1; double len2 = KEY_POINT_LEN2; double len3 = KEY_POINT_LEN3; double len = sqrt(pow((endX - startX), 2) + pow((endY - startY), 2));if (len * KEY_POINT_RATIO1 < KEY_POINT_LEN1) {
            len1 = len * KEY_POINT_RATIO1;
        }
        if (len * KEY_POINT_RATIO2 < KEY_POINT_LEN2) {
            len2 = len * KEY_POINT_RATIO2;
        }
        if(len * KEY_POINT_RATIO3 < KEY_POINT_LEN3) { len3 = len * KEY_POINT_RATIO3; } CGPoint arrXY_11 = [self rotateVecWithPx:endX - startX py:endY - startY ang:KEY_POINT_ANGLE1 newLen:len1]; CGPoint arrXY_12 = [self rotateVecWithPx:endX - startX py:endY - startY ang:-KEY_POINT_ANGLE1 newLen:len1]; CGPoint arrXY_21 = [self rotateVecWithPx:endX - startX py:endY - startY ang:KEY_POINT_ANGLE2 newLen:len2]; CGPoint arrXY_22 = [self rotateVecWithPx:endX - startX py:endY - startY ang:-KEY_POINT_ANGLE2 newLen:len2]; CGPoint arrXY_31 = [self rotateVecWithPx:startX - endX py:startY - endY ang:KEY_POINT_ANGLE3 newLen:len3]; CGPoint arrXY_32 = [self rotateVecWithPx:startX - endX py:startY - endY ang:-KEY_POINT_ANGLE3 newLen:len3];; // Convert to the original coordinate systemfloat x11 = endX - arrXY_11.x;
        float y11 = endY - arrXY_11.y;
        float x12 = endX - arrXY_12.x;
        float y12 = endY - arrXY_12.y;
        float x21 = endX - arrXY_21.x;
        float y21 = endY - arrXY_21.y;
        float x22 = endX - arrXY_22.x;
        float y22 = endY - arrXY_22.y;
        float x31 = startX - arrXY_31.x;
        float y31 = startY - arrXY_31.y;
        float x32 = startX - arrXY_32.x;
        float y32 = startY - arrXY_32.y;
Copy the code

And then we just need to add our 7 points to the UIBezierPath object.

        [self moveToPoint:endPoint];
        [self addLineToPoint:CGPointMake(x11, y11)];
        [self addLineToPoint:CGPointMake(x21, y21)];
        [self addLineToPoint:CGPointMake(x32, y32)];
        [self addLineToPoint:CGPointMake(x31, y31)];
        [self addLineToPoint:CGPointMake(x22, y22)];
        [self addLineToPoint:CGPointMake(x12, y12)];
Copy the code


– > SDDrawView portal