The problem is that you're stroking a path between a series of points captured in a gesture, and these points aren't all on the same line because they include natural deviations that take place as the users finger drags across the screen.
This is completely unrelated to "anti-aliasing", which is a technique to make a straight line look smoother, without visually distracting "jaggies". But the problem here is not that you're trying to improve the rendering of a single straight line, but rather that you're not drawing a straight line in the first place, but rather instead drawing a bunch of line segments that don't happen to line up.
My goal is to achieve Adobe Illustrator like lines when my user swipes across the screen
In that case, you don't want "anti-aliasing". All you need is a createPath
that
moveToPoint
to the first point; and
addLineToPoint
to the last point.
Just don't worry about any of the points in between. This simple solution will yield a single straight line that will start wherever your touches started and stretch to where your touches ended, and will continue to do so until you lift your finger.
I think that will translate to something like:
- (UIBezierPath *)createPath {
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:stroke.startPoint];
[bezierPath addLineToPoint:[[stroke.points lastObject] CGPointValue]];
return bezierPath;
}
If you don't want an actual line, but rather just want to smooth this, I'd suggest (a) do rolling average to eliminate peaks and valleys in your bouncy data points; and (b) use Hermite or Catmull-Rom spline (as outlined in https://spin.atomicobject.com/2014/05/28/ios-interpolating-points/) so the resulting path is smooth rather than a series of line segments.
Thus
- (UIBezierPath * _Nullable)createActualPathFor:(Stroke *)stroke {
if (stroke.points.count < 2) return nil;
UIBezierPath * path = [UIBezierPath bezierPath];
[path moveToPoint:[stroke.points.firstObject CGPointValue]];
for (NSInteger i = 1; i < stroke.points.count; i++) {
[path addLineToPoint:[stroke.points[i] CGPointValue]];
}
return path;
}
- (UIBezierPath * _Nullable)createSmoothedPathFor:(Stroke *)stroke interval:(NSInteger)interval {
if (stroke.points.count < 2) return nil;
NSMutableArray <NSValue *> *averagedPoints = [NSMutableArray arrayWithObject:stroke.points[0]];
NSInteger current = 1;
NSInteger count = 0;
do {
CGFloat sumX = 0;
CGFloat sumY = 0;
do {
CGPoint point = [stroke.points[current] CGPointValue];
sumX += point.x;
sumY += point.y;
current++;
count++;
} while (count < interval && current < stroke.points.count);
if (count >= interval) {
CGPoint average = CGPointMake(sumX / count, sumY / count);
[averagedPoints addObject:[NSValue valueWithCGPoint:average]];
count = 0;
}
} while (current < stroke.points.count);
if (count != 0) {
[averagedPoints addObject:stroke.points.lastObject];
}
return [UIBezierPath interpolateCGPointsWithHermite:averagedPoints closed:false];
}
Will yield the following (where the actual data points are in blue and the smoothed rendition is in red):

Note, I'm assuming that your array of points
of Stroke
includes the starting point, so you might have to adjust that accordingly.