• Stars
    star
    241
  • Rank 167,643 (Top 4 %)
  • Language
    Swift
  • Created about 9 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Smooth Drawing for iOS in Swift with Hermite Spline Interpolation

SmoothScribble

Smooth Drawing for iOS in Swift with Hermite Spline Interpolation

Companion project to this blog post: http://flexmonkey.blogspot.co.uk/2015/11/smooth-drawing-for-ios-in-swift-with.html

If you’re creating a drawing or painting app for iOS, you may recall from my recent post discussing coalesced touches a way to increase touch resolution and access intermediate touch locations that may have occurred between touchesMoved invocations. However, if your app simply draws straight lines between each touch location, even the coalesced ones, and your user moves their finger or Pencil quickly, they’ll see their drawing rendered as line sections.

There’s a better solution: using a spline interpolation to draw Bezier curves between each touch location and render out a single, continuous curve. I’ve discussed spline interpolation in the past when drawing a curve to pass through all the control points of a Core Image Tone Curve filter and this project borrows that code but repurposes it for a drawing application.

My demo app presents the user with two boxes into either of which they can scribble a drawing, which is mirrored in the other box. The box on the left renders their drawing using spline interpolation and the box on the right with straight lines for touch location to touch location (albeit with coalesced touched). It’s immediately obvious how much nicer the drawing on the left is, appearing as a single curve.

SmoothScribble Basics

Both of the boxes on the screen are sub-classed ScribbleView instances which conform to Scribblable. The ScribbleView class is simply a UIView which contains two additional CAShapeLayer - backgroundLayer for displaying “historical” scribbles and a "working" drawingLayer for displaying the current, in progress scribble.

The Scribblable protocol contains three methods invoked at the beginning, during and at the end of a scribble gesture and a method to clear it.

func beginScribble(point: CGPoint)
func appendScribble(point: CGPoint)
func endScribble()
func clearScribble()

SimpleScribbleView Class

Let’s look at the simple implementation first. SimpleScribbleView will draw straight lines starting at the point set by beginScribble():

let simplePath = UIBezierPath()

func beginScribble(point: CGPoint)
{
    simplePath.moveToPoint(point)
}

Then adding lines to its simplePath and updating the drawing layer’s path with each move:

func appendScribble(point: CGPoint)
{
    simplePath.addLineToPoint(point)
    
    drawingLayer.path = simplePath.CGPath
}

Finally, when the user lifts their finger from the screen, the simplePath is appended to any existing background path (the “historical” scribbles) and then cleared:

func endScribble()
{
    if let backgroundPath = backgroundLayer.path
    {
        simplePath.appendPath(UIBezierPath(CGPath: backgroundPath))
    }
    
    backgroundLayer.path = simplePath.CGPath
    
    simplePath.removeAllPoints()
    
    drawingLayer.path = simplePath.CGPath
}

The result is, as you can see above, a series of straight lines with the line artefacts looking worse the faster the user moves their finger.

HermiteScribbleView Class

The spline interpolated version is a little different. Here, in addition to a UIBezierPath to hold the current scribble, there’s also an array of all the points that the path consists of and it will need to interpolate:

let hermitePath = UIBezierPath()
var interpolationPoints = [CGPoint]()

When beginScribble() is invoked on HermiteScribbleView, it creates a new array of those interpolation points and populates it with the initial position:

func beginScribble(point: CGPoint)
{
    interpolationPoints = [point]
}

Now with each move, it appends the new point to the interpolationPoints array and uses an extension I wrote to UIBezierPath named interpolatePointsWithHermite() to build a series of Bezier curves to give that smooth Hermite interpolated splines between all the points:

func appendScribble(point: CGPoint)
{
    interpolationPoints.append(point)
    
    hermitePath.removeAllPoints()
    hermitePath.interpolatePointsWithHermite(interpolationPoints)
    
    drawingLayer.path = hermitePath.CGPath
}

Finally, the endScribble() of HermiteScribbleView does pretty much the same thing as its simpler sibling, appending a copy of its “working” layer to its “historical” layer:

func endScribble()
{
    if let backgroundPath = backgroundLayer.path
    {
        hermitePath.appendPath(UIBezierPath(CGPath: backgroundPath))
    }
    
    backgroundLayer.path = hermitePath.CGPath
    
    hermitePath.removeAllPoints()
    
    drawingLayer.path = hermitePath.CGPath
}

Wiring up SimpleScribbleView and HermiteScribbleView

The main view controller uses a UIStackView to position the two scribble views either side-by-side in landscape of above and below each other in portrait.

In touches began, I figure out which of the two views is the source and set that to touchOrigin:

if(hermiteScribbleView.frame.contains(location))
{
   touchOrigin = hermiteScribbleView
}
else if (simpleScribbleView.frame.contains(location))
{
    touchOrigin = simpleScribbleView
}
else
{
    touchOrigin = nil
    return
}

With that set, I can use the touch’s locationInView for touchOrigin to apply the same touch information to both views.

You may have already guessed that it’s also the view controller’s touchesBegan() that invokes beginScribble():

if let adjustedLocationInView = touches.first?.locationInView(touchOrigin)
{
    hermiteScribbleView.beginScribble(adjustedLocationInView)
    simpleScribbleView.beginScribble(adjustedLocationInView)
}

The appendScribble() is invoked inside the view controller’s touchesMoved():

coalescedTouches.forEach
{
    hermiteScribbleView.appendScribble($0.locationInView(touchOrigin))
    simpleScribbleView.appendScribble($0.locationInView(touchOrigin))
}

Which just leaves touchesEnded() to invoke endScribble():

hermiteScribbleView.endScribble()
simpleScribbleView.endScribble()

In Conclusion

No matter how fast your code is, there’s a good chance your user’s fingers are faster. If you have a drawing app, smoothly interpolating a user’s gesture rather than just drawing straight lines between each touch location makes their drawings look far more natural and, maybe, closer to the image they had in mind.

As always, the source code to this little demo app is available at my GitHub repository here. Enjoy!

More Repositories

1

Filterpedia

Core Image Filter Explorer & Showcase
Swift
2,241
star
2

Blurable

Apply a Gaussian Blur to any UIView with Swift Protocol Extensions
Swift
932
star
3

Plum-O-Meter

3D Touch Application for Weighing Plums (and other small fruit!)
Swift
528
star
4

ParticleLab

Particle system that's both calculated and rendered on the GPU using the Metal framework
Swift
493
star
5

sweetcorn

Node based CIKernel creation
Swift
261
star
6

MarkingMenu

Swift MarkingMenu
Swift
217
star
7

CartoonEyes

Composite Cartoon Eyes over Face from Front Camera with CoreImage
Swift
163
star
8

LiveCameraFiltering

Demonstration of applying a Comic Book filter to a live video feed
Swift
151
star
9

SwiftSpace

CoreMotion Controlled Drawing in 3D Space
Swift
150
star
10

ShinpuruLayout

Simple Layout in Swift using HGroups & VGroups
Swift
128
star
11

ForceSketch

Demonstration of a Sketching App Using 3D Touch
Swift
109
star
12

ParticleCam

Metal based particle system influenced by iPad camera
Swift
102
star
13

ShinpuruImage

Syntactic Sugar for Accelerate/vImage and Core Image Filters
Swift
100
star
14

ShinpuruNodeUI

Node Based UI Component Written in Swift
Swift
98
star
15

ValentinesSwift

You love Swift & Swift loves you!
Swift
89
star
16

CoreImageForSwiftPlaygrounds

CoreImage For Swift Playgrounds
Swift
88
star
17

SnapSwift

SnapSeed Style Popup Menu for iOS
Swift
87
star
18

CoreImageHelpers

Syntactic sugar for displaying CIImage using OpenGL and grabbing CIImages from iOS cameras
Swift
73
star
19

VideoEffects

iPad app to open videos from file system, apply Core Image filters and save result back SavedPhotosAlbum
Swift
71
star
20

AudioKitNodality

AudioKitNodality
Swift
69
star
21

MetalReactionDiffusion

Reaction Diffusion using Swift & Metal
Swift
69
star
22

3D-Motion-Controller

Using MultipeerConnectivity and CoreMotion to allow an iPhone to act as a 3D mouse for an iPad app
Swift
67
star
23

Globular

Colourful SpriteKit Metaballs Controlled by 3D Touch
Swift
67
star
24

Interpolation-Playground-

Swift playground demonstrating lerp, smooth step, Catcall-Rom and others!
Swift
63
star
25

Nebula

Core Image Volumetric Rendering
Swift
60
star
26

MetalKit-Particles

An implementation of my Metal ParticleLab component for OS X 10.11 and iOS 9
Swift
57
star
27

AdvancedTouch

Swift Advanced Touch Handling in iOS9: Coalescing and Prediction
Swift
50
star
28

MetalVideoCapture

Demo of Creating a Metal Texture from AVCaptureSession and Applying MetalPerformanceShaders In-Place
Swift
50
star
29

AudioSynthesis

CoreAudio for Sound Synthesis Demonstration
Swift
48
star
30

UIScrollViewDemo

A Node Based User Interface implemented with the Presentation Model Pattern
Swift
41
star
31

SwiftCFD

CPU Based Navier Stokes Computational Fluid Dynamics in Swift for iOS
Swift
41
star
32

StrangeAttractor

Lorenz Attractor in Swift & Metal
Swift
39
star
33

Spritely

Using SpriteKit events to trigger AudioKit sounds
Swift
39
star
34

SwiftGoo

Kai's Power Tools Goo - written in Swift!
Swift
38
star
35

Scribe

Handwriting and Stroke Recognition in Swift
Swift
37
star
36

DeepPressGestureRecognizer

UIGestureRecognizer for recognising deep press 3D Touch on iPhone 6s
Swift
35
star
37

MercurialPaint

Mercurial Painting using Metal and Core Image
Swift
33
star
38

PencilController

Using Apple Pencil as a 3D Controller for Image Editing
Swift
30
star
39

CoreImagePerspectivePlayground

Core Image Perspective Playground
Swift
28
star
40

GameplayKitAgents

A Look at Agents, Goals & Behaviours in GameplayKit
Swift
27
star
41

CoreImageFluidDynamics

CIFilter / CIKernel based fluid dynamics
Swift
26
star
42

SceneKitMaterialEditor

A very simple app for editing SceneKit materials
Swift
26
star
43

DepthOfFieldExplorer

SceneKit Depth of Field Demonstration
Swift
26
star
44

ChromaTouch

Introduction to 3D Touch in Swift with Peek, Pop and Preview Actions
Swift
26
star
45

3D-ReTouch

Experimental Retouching App using 3D Touch
Swift
26
star
46

FurrySketch

Using Apple Pencil's Azimuth & Altitude Data for Creating Directional Furry Brush Strokes
Swift
26
star
47

Purikura

Bulging eyes Purikura effect
Swift
25
star
48

SkyCubeTextureDemo

A demonstration of using MDLSkyCubeTexture in a SceneKit project
Swift
25
star
49

CoreImageConvolutionExplorer

CoreImageConvolutionExplorer
Swift
24
star
50

CIImage-UIImage-Orientation-Fix

Demo of UIImageOrientation to TIFF Orientation conversion that fixes orientation issues when creating CIImage from UIImage
Swift
24
star
51

ImageToneCurveEditor

Project demonstrating the use of CIToneCurve
Swift
24
star
52

PencilSynth

An Audiokit Synthesiser Controller by Apple Pencil
Swift
23
star
53

CoreImageTransitionExplorer

Simple slide show demonstration using Core Image Transitions and PHImageManager
Swift
22
star
54

SceneKitProceduralNormalMapping

Demo of Core Image filter to create SceneKit normal maps from procedural bump maps
Swift
22
star
55

ForceZoom

Zoom Into Image Details using 3D Touch Peek
Swift
21
star
56

PendulaTone

Audio driven by pendulum waves
Swift
20
star
57

Rotatable

Swift Protocol Extension to Rotate any UIView
Swift
19
star
58

PhotoBrowserDemo

A Swift image browser/picker for use with PHImageManager
Swift
19
star
59

Protocol-Extension-Event-Dispatcher

Implementation of EventDispatcher pattern using Swift Protocol Extensions
Swift
18
star
60

ImageProcessingWithMetal

An introduction to image processing with Metal and Swift
17
star
61

WheelTone

Audio synthesis driven by a network of friction gears
Swift
15
star
62

FilterChainingDemo

Demonstration of chaining a series of CIFilters together
Swift
15
star
63

ConvolutionExplorer

vImage / Accelerate convolution filter in Swift
Swift
14
star
64

Bokeh

Demonstration of simulation of hexagonal bokeh using Metal Performance Shaders
Swift
14
star
65

SpriteKitMotionBlur

SpriteKitMotionBlur
Swift
14
star
66

FMNixieDisplay

Nixie Tube Display Component on Swift
Swift
14
star
67

MercurialText

Embossed Type using SceneKit and CIShadedMaterial
Swift
14
star
68

GPUImageDemo

GPUImageDemo
Swift
12
star
69

Christmas-Tree-Bowling

Apple Pencil Controlled Christmas Tree Bowling!
Swift
12
star
70

GestureRecognizer

Demo of custom UIGestureRecognizer
Swift
11
star
71

NumericDialDemo

NumericDialDemo
Swift
11
star
72

ProgSConCompanion

ProgSConCompanion
Swift
10
star
73

CoreImageReactionDiffusion

Gary-Scott reaction Diffusion Implemented as a Core Image CIKernel
Swift
10
star
74

MetalKit-ReactionDiffusion

Reaction diffusion simulation using Metal Kit with parameter gradients controlled by the camera
Swift
10
star
75

PencilScale

Using an Apple Pencil with an iPad Pro as an electronic scale
Swift
9
star
76

GrayScott

Non GPU GrayScott Reaction Diffusion Experiment Using NSOperation
Swift
9
star
77

FMHierarchicalSelector

Hierarchical Selector Component based on UIPickerView & UICollectionView
Swift
9
star
78

NemoCam

Virtual underwater video recording using Filterpedia's Caustic Refraction Filter
Swift
9
star
79

InnerPlanetCurves

Draws Cubic Bezier Curve Between Earth & Venus Using Mercury & Mars as Control Points
Swift
8
star
80

BristlePaint

Embossed Painting with Individual Bristles using SpriteKit Normal Mapping
Swift
8
star
81

StackView

A first play with Swift 2's UIStackView
Swift
8
star
82

MPS_Equalisation

Demonstration of Histogram Equalisation with Metal Performance Shaders
Swift
7
star
83

CoreImageReductionFilterExplorer

App demonstrating extracting color data from CIAreaAverage and displaying Core Image Histograms
Swift
7
star
84

Vertigo

Vertigo inducing colourful painting...
Swift
6
star
85

CoreImageCathodeRayTube

Simple cathode ray tube simulation CIKernel processing live feed from iOS camera.
Swift
5
star
86

Swift3_CoreImageDemo

Swift3_CoreImageDemo
Swift
4
star
87

LondonSwiftDemo

Demo Project for London Swift
Swift
4
star
88

Swarm-Chemistry-GCD

New Version of Swarm Chemistry, using GCD and Bitmap creation
Swift
4
star
89

PHImageManagerTwitterDemo

Demonstration of PHImageManager and Twitter Integration
Swift
3
star
90

CoreImageVoronoi

Full screen animated Moroni noise!
Swift
3
star
91

PhysicsExperiments

Swift
3
star
92

InlineMethodTest

Testing performance of inline code versus instance methods versus class methods
Swift
3
star
93

StPatricksDay

Happy St Patricks Day!
Swift
3
star
94

HisogramSpecificationBlendDemo

Demonstration of Histogram Specification for Image Composition
Swift
2
star
95

SelectorPlayground

Demonstration of `#selector` expression
Swift
2
star
96

FlexMonkeyExamples

Files to support posts in http://flexmonkey.blogspot.co.uk/
Swift
2
star
97

SwiftExperiments

Swift experiments
Swift
1
star
98

Swarm-Chemistry

DEPRECATED: Multithreaded CPU based Swarm Chemistry
Swift
1
star
99

ArraySpeedTest

Some simple tests looking at the performance of different techniques populating and interrogating arrays
Swift
1
star
100

GaussianPlayground

Demonstration of creating a Gaussian blur using separate vertical and horizontal convolution filters.
Swift
1
star