Search code examples
iosdrawingcgrectbrush

How to get a tapering line brush effect in Objective C using GraphicContext


I am doing a calligraphy application and would like to modify my code below so when a user ends a brush stroke, the line tapers and thins out like it would with a real calligraphy pen (a flick effect). I understand that touchesEnded may be a better way to do this, however I was just wondering what would be the best way to programmatically make this flick at the end of a stroke using CGRect or GraphicsContext in UIKit in Xcode for Objective C.

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;

UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:self.view];



UIGraphicsBeginImageContext(CGSizeMake(320, 568));
[drawImage.image drawInRect:CGRectMake(0, 0, 320, 568)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0, 0, 0, 1);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());


[drawImage setFrame:CGRectMake(0, 0, 320, 568)];
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//what code do I put here to get the flick effect - what CGGetContext Parameter
//may be applicable or what programming technique may help with this.     

}

Solution

  • This may be over simplifying, but it should lead you in something of the right direction. I'm going to just make a triangle, but you could eventually add bezier curves to make the effect more realistic.

    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    //what code do I put here to get the flick effect - what CGGetContext Parameter
    //may be applicable or what programming technique may help with this. 
    
        UIGraphicsBeginImageContext(CGSizeMake(320, 568));
        [drawImage.image drawInRect:CGRectMake(0, 0, 320, 568)];
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetLineWidth(context, 5.0);
        CGContextSetRGBFillColor(context, 0, 0, 0, 1);
        CGContextBeginPath(context);
        CGContextBeginPath(context);
        CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
        CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
        CGContextClosePath(context);
        CGContextStrokePath(context);
    
        //Normalized directionality
        float lineLength = sqrt((currentPoint.x - lastPoint.x)*(currentPoint.x - lastPoint.x) + (currentPoint.y - lastPoint.y)*(currentPoint.y - lastPoint.y));
        float dx = (currentPoint.x - lastPoint.x)/lineLength;
        float dy = (currentPoint.y - lastPoint.y)/lineLength;
    
        //Now make a triangle
        CGContextBeginPath(context);
    
        //2.5 is from 1/2 of your line width (5)
        CGContextMoveToPoint(context, currentPoint.x + 2.5*dy, currentPoint.y - 2.5*dx);
    
        //This 10 is completely arbitrary, the length your taper is going to be.
        //Ideally this will be proportional to your last line segment length, longer if their finger is moving faster...
        CGContextAddLineToPoint(context, currentPoint.x + 10*dx, currentPoint.y + 10*dy);
    
        //Now the last tip of the triangle
        CGContextMoveToPoint(context, currentPoint.x - 2.5*dy, currentPoint.y + 2.5*dx);
        CGContextClosePath(context);
        CGContextFillPath(context);
    
        [drawImage setFrame:CGRectMake(0, 0, 320, 568)];
        drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    

    Now to make this cooler you could add in calculation of the curve the person was drawing and create your "triangle" taper with bezier curves in the direction that they were curving. That could actually be really fun to calculate.