IPhone smooth sketching algorithm

I am working on a thumbnail application on an iPhone. It works for me, but not as good as here enter image description here

And I'm looking for any suggestion to smooth out the picture. Basically, what I did is when the user places a finger on the screen, which I called

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

then I collect one touch in an array with

 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 

and when the user left a finger from the screen, I called

 - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 

then i draw all the points in the array using

 NSMutableArray *points = [collectedArray points]; CGPoint firstPoint; [[points objectAtIndex:0] getValue:&firstPoint]; CGContextMoveToPoint(context, firstPoint.x, firstPoint.y); CGContextSetLineCap(context, kCGLineCapRound); CGContextSetLineJoin(context, kCGLineJoinRound); for (int i=1; i < [points count]; i++) { NSValue *value = [points objectAtIndex:i]; CGPoint point; [value getValue:&point]; CGContextAddLineToPoint(context, point.x, point.y); } CGContextStrokePath(context); UIGraphicsPushContext(context); 

And now I want to improve the drawing to look like the Sketch Book application enter image description here

I think there is something to do with the signal processing algorithm to reorder all the points in the array, but I'm not sure. Any help is appreciated.

Thanks in advance:)

+49
ios iphone quartz-graphics drawing
Feb 22 2018-11-22T00:
source share
6 answers

The easiest way to smooth such a curve is to use a Bezier curve instead of straight lines. For the math behind this, see this article (pointed out in this answer ) which describes how to calculate the curves needed to smooth out a curve passing through multiple points.

I believe that the Core Plot framework now has the ability to smooth graph curves so you can look at the code used there to implement such anti-aliasing.

There is no magic in it, as these smoothing procedures are performed quickly and relatively easily.

+17
Feb 23 '11 at 18:30
source share
 CGPoint midPoint(CGPoint p1, CGPoint p2) { return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5); } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; previousPoint1 = [touch previousLocationInView:self]; previousPoint2 = [touch previousLocationInView:self]; currentPoint = [touch locationInView:self]; } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; previousPoint2 = previousPoint1; previousPoint1 = [touch previousLocationInView:self]; currentPoint = [touch locationInView:self]; // calculate mid point CGPoint mid1 = midPoint(previousPoint1, previousPoint2); CGPoint mid2 = midPoint(currentPoint, previousPoint1); UIGraphicsBeginImageContext(self.imageView.frame.size); CGContextRef context = UIGraphicsGetCurrentContext(); [self.imageView.image drawInRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height)]; CGContextMoveToPoint(context, mid1.x, mid1.y); // Use QuadCurve is the key CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); CGContextSetLineCap(context, kCGLineCapRound); CGContextSetLineWidth(context, 2.0); CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); CGContextStrokePath(context); self.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } 
+53
Mar 31 2018-11-11T00:
source share

I really like this topic. Thanks for all the implementations, such as Krzysztof Zablotsky and Yu-Sen Khan. I changed the version of Yu-Sen Han to change the line thickness depending on the pan speed (actually the distance between the last strokes). In addition, I implemented a bitmap (for touchBegan and touchEnded locations close to each other) Here is the result: enter image description here

To determine the thickness of the line, I chose this distance function:

(Do not ask me why ... I just, although it fits, but I'm sure you can find the best)

enter image description here

 CGFloat dist = distance(previousPoint1, currentPoint); CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2; 

Another hint. To be sure that the thickness changes smoothly, I limited it depending on the thickness of the previous segment and the user factor:

 self.lineWidth = MAX(MIN(newWidth,lastWidth*WIDTH_RANGE_COEF),lastWidth/WIDTH_RANGE_COEF); 
+13
Oct 24
source share

Thanks for entering. I am updating my quest here because I need a place for it.

I look at both the CorePlot and Bezier curve solutions that you proposed with little success.

For corePlot, I can get the graph of the graph from the int array, but I can not find anything related to smoothing the .BTW curve. Here I am using CPScatterPlot with some random number.

enter image description here

as for the Bezier curve, My quest led me to here. It is somehow connected with the implementation of Spline in iOS

  CatmullRomSpline *myC = [[CatmullRomSpline alloc] initAtPoint:CGPointMake(1.0, 1.0)]; [myC addPoint:CGPointMake(1.0, 1.5)]; [myC addPoint:CGPointMake(1.0, 1.15)]; [myC addPoint:CGPointMake(1.0, 1.25)]; [myC addPoint:CGPointMake(1.0, 1.23)]; [myC addPoint:CGPointMake(1.0, 1.24)]; [myC addPoint:CGPointMake(1.0, 1.26)]; NSLog(@"xxppxx %@",[myC asPointArray]); NSLog(@"xxppxx2 %@",myC.curves); 

and I get the result:

  2011-02-24 14:45:53.915 DVA[10041:40b] xxppxx ( "NSPoint: {1, 1}", "NSPoint: {1, 1.26}" ) 2011-02-24 14:45:53.942 DVA[10041:40b] xxppxx2 ( "QuadraticBezierCurve: 0x59eea70" ) 

I'm not sure how to go from there. Therefore, I am also stuck on this front :(

I looked at GLPaint as the last resource. It uses OpenGLES and uses a soft spot sprite to plot points in an array. I know that this is more like fixing the problem, rather than fixing it. But I think I will share my findings here.

Black is GLPaint and white is old. And the last one is a sketch from the Sketch Book app to compare

enter image description hereenter image description hereenter image description here

I am still trying to do it right, any further suggestion is welcome.

+2
Feb 24 2018-11-11T00:
source share

To get rid of the dumb point in GLPaint code.

Edit

 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 

this function

 //Ändrat av OLLE /* // Convert touch point from UIView referential to OpenGL one (upside-down flip) if (firstTouch) { firstTouch = NO; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; } else { location = [touch locationInView:self]; location.y = bounds.size.height - location.y; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; } */ location = [touch locationInView:self]; location.y = bounds.size.height - location.y; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; //Ändrat av OLLE// 

I know that this is not a solution to our problem, but it is something.

+1
Feb 24 '11 at 8:44
source share

I translated kyoji answer into Swift, as a reusable subclass of UIImageView . The TouchDrawImageView subclass allows the user to draw an image with a finger.

Once you have added this TouchDrawImageView class to your project, be sure to open the storyboard and

  • select TouchDrawImageView as the "Custom Class" of your image.
  • check the User Interaction property in your image view

Here's the code for TouchDrawImageView.swift :

 import UIKit class TouchDrawImageView: UIImageView { var previousPoint1 = CGPoint() override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } previousPoint1 = touch.previousLocation(in: self) } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let previousPoint2 = previousPoint1 previousPoint1 = touch.previousLocation(in: self) let currentPoint = touch.location(in: self) // calculate mid point let mid1 = midPoint(p1: previousPoint1, p2: previousPoint2) let mid2 = midPoint(p1: currentPoint, p2: previousPoint1) UIGraphicsBeginImageContext(self.frame.size) guard let context = UIGraphicsGetCurrentContext() else { return } if let image = self.image { image.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)) } context.move(to: mid1) context.addQuadCurve(to: mid2, control: previousPoint1) context.setLineCap(.round) context.setLineWidth(2.0) context.setStrokeColor(red: 1.0, green: 0, blue: 0, alpha: 1.0) context.strokePath() self.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } func midPoint(p1: CGPoint, p2: CGPoint) -> CGPoint { return CGPoint(x: (p1.x + p2.x) / 2.0, y: (p1.y + p2.y) / 2.0) } } 
+1
Sep 23 '17 at 20:40
source share



All Articles