CoreMotion Controlled Drawing in 3D Space

Overview

SwiftSpace

Gyroscope Driven Drawing in 3D Space

Companion project to: http://flexmonkey.blogspot.co.uk/2015/08/coremotion-controlled-3d-sketching-on.html

I was really impressed by a demo of InkScape that I read about in Creative Applications recently. InkScape is an Android app which allows users to sketch in a 3D space that's controlled by the device's accelerometer. It's inspired by Rhonda which pre-dates accelerometers and uses a trackball instead.

Of course, my first thought was, "how can I do this in Swift?". I've never done any work with CoreMotion before, so this was a good opportunity to learn some new stuff. My first port of call was this excellent article on iOS motion at NSHipster.

My plan for the application was to have a SceneKit scene with a motion controlled camera rotating around an offset pivot point at the centre of the SceneKit world. With each touchesBegan(), I'd create a new flat box in the centre of the screen that aligned with the camera and on touchesMoved(), I'd use the touch location to append to a path that I'd draw onto a CAShapeLayer that I'd use as the diffuse material for the newly created geometry.

Easy! Let's break it down:

Creating the Camera

I wanted the camera at to always point at and rotate around the centre of the world while being slightly offset from it. The two things to help this are the camera's pivot property and using a "look at constraint". First off, I create a node to represent the centre of the world and the camera itself:

    let centreNode = SCNNode()
    centreNode.position = SCNVector3(x: 0, y: 0, z: 0)
    scene.rootNode.addChildNode(centreNode)

    let camera = SCNCamera()
    camera.xFov = 20

    camera.yFov = 20

Next, an SCNLookAtConstraint means that however I translate the camera, it will always point at the centre:

    let constraint = SCNLookAtConstraint(target: centreNode)
    cameraNode.constraints = [constraint]

...and finally, setting the camera's pivot will reposition it but have it rotate around the centre of the world:

    cameraNode.pivot = SCNMatrix4MakeTranslation(0, 0, -cameraDistance)

Handling iPhone Motion

Next up is handling the iPhone's motion to rotate the camera. Remembering that the iPhone's roll is its rotation along the front-to-back axis and its pitch is its rotation along its side-to-side axis:

...I'll use those properties to control my camera's x and y Euler angles.

The first step is to create an instance of CMMotionManager and ensure it's available and working (so this code won't work on the simulator):

    let motionManager = CMMotionManager()
        
    guard motionManager.gyroAvailable else
    {
        fatalError("CMMotionManager not available.")

    }

Next up, I start the motion manager with a little block of code that's invoked with each update. I use a tuple to store the initial attitude of the iPhone and simple use the difference between that initial value and the current attitude to set the camera's Euler angles:

    let queue = NSOperationQueue.mainQueue
    
    motionManager.deviceMotionUpdateInterval = 1 / 30
    
    motionManager.startDeviceMotionUpdatesToQueue(queue())
    {
        (deviceMotionData: CMDeviceMotion?, error: NSError?) in
        
        if let deviceMotionData = deviceMotionData
        {
            if (self.initialAttitude == nil)
            {
                self.initialAttitude = (deviceMotionData.attitude.roll,
                    deviceMotionData.attitude.pitch)
            }
            
            self.cameraNode.eulerAngles.y = Float(self.initialAttitude!.roll - deviceMotionData.attitude.roll)
            self.cameraNode.eulerAngles.x = Float(self.initialAttitude!.pitch - deviceMotionData.attitude.pitch)
        }

    }

##Drawing in 3D

Since I know the angles of my camera, it's pretty simple to align the target geometry for drawing on the touchesBegan() method - it just shares the same attitude:

    currentDrawingNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 0, chamferRadius: 0))

    currentDrawingNode.eulerAngles.x = self.cameraNode.eulerAngles.x
    currentDrawingNode.eulerAngles.y = self.cameraNode.eulerAngles.y

At the same time, I create a new CAShapeLayer that will contain a stroked path that follows the user's finger:

    currentDrawingLayer = CAShapeLayer()

    let material = SCNMaterial()

    material.diffuse.contents = currentDrawingLayer
    material.lightingModelName = SCNLightingModelConstant
            

    currentDrawingNode.geometry?.materials = [material]

On touchesMoved(), I need to convert the location in the main view to the location on the geometry. Since this geometry has a size of 1 x 1 (from -0.5 through 0.5 in both directions), I'll need to convert that to coordinates in my CAShapeLayer (arbitrarily set to 512 x 512) to add points its path.

There are a few steps to do this, taking the locationInView() of the first item in the touches set, I pass it into hitTest() on my SceneKit scene. This returns an array of SCNHitTestResults for all the geometries underneath the touch which I filter for the current geometry and then simply rescale the result's localCoordinates to find the coordinates on the current CAShapeLayer:

    let locationInView = touches.first?.locationInView(view)

    if let hitTestResult:SCNHitTestResult = sceneKitView.hitTest(locationInView!, options: nil).filter( { $0.node == currentDrawingNode }).first,
        currentDrawingLayer = currentDrawingLayer

    {
        let drawPath = UIBezierPath(CGPath: currentDrawingLayer.path!)

        let newX = CGFloat((hitTestResult.localCoordinates.x + 0.5) * Float(currentDrawingLayerSize))
        let newY = CGFloat((hitTestResult.localCoordinates.y + 0.5) * Float(currentDrawingLayerSize))
        
        drawPath.addLineToPoint(CGPoint(x: newX, y: newY))
        
        currentDrawingLayer.path = drawPath.CGPath
    }

...and that's kind of it!

The source code to this project is available here at my GitHub repository. It was developed under Xcode 7 beta 5 and tested on my iPhone 6 running iOS 8.4.1.

You might also like...
A SwiftUI Framework for Drawing Chart
A SwiftUI Framework for Drawing Chart

PrettyAxis A SwiftUI Framework for drawing charts. Fearture Support Drawing Bar Chart RadarChart Line Chart and Scatter Charts Pie Chart and Donut Cha

A simple drawing app for iPad using SwiftUI
A simple drawing app for iPad using SwiftUI

TinyDraw A small little drawing app fro iPad using SwiftUIs Canvas view. Implemented along the HWS+ live stream by Paul Hudson. Features Drawing lines

Space! – an iOS 14 widget displaying NASA's Astronomy Picture of the Day
Space! – an iOS 14 widget displaying NASA's Astronomy Picture of the Day

Space! NASA's Astronomy Picture of the Day – now on your Home Screen with widgets! Space! displays the latest APOD photo curated by NASA every day. Se

App developed for Space Apps NASA Hackathon

AirCheck App developed for Space Apps NASA Hackathon April 22, 23, 24 Attached products Backend Web/Mobile Web Page and scalability plan Requirements

Space! – an iOS widget displaying NASA's Astronomy Picture of the Day
Space! – an iOS widget displaying NASA's Astronomy Picture of the Day

Space! NASA's Astronomy Picture of the Day – now on your Home Screen with widgets! Space! displays the latest APOD photo curated by NASA every day. Se

macOS command line tool to return the available disk space on APFS volumes

diskspace Returns available disk space With the various APFS features the value for free disk space returned from tools such as du or df will not be a

A free, multiplatform SDK for real-time facial motion capture using blendshapes, and rigid head pose in 3D space from any RGB camera, photo, or video.
A free, multiplatform SDK for real-time facial motion capture using blendshapes, and rigid head pose in 3D space from any RGB camera, photo, or video.

mocap4face by Facemoji mocap4face by Facemoji is a free, multiplatform SDK for real-time facial motion capture based on Facial Action Coding System or

This project is an application that lets users learn about the missions and astronauts that formed NASA’s Apollo space program.

This project is an application that lets users learn about the missions and astronauts that formed NASA’s Apollo space program.

Use 1600+ icons (and more!) from FontAwesome and Google Material Icons in your swift/iOS project in an easy and space-efficient way!
Use 1600+ icons (and more!) from FontAwesome and Google Material Icons in your swift/iOS project in an easy and space-efficient way!

Swicon Use 1600+ icons from FontAwesome and Google Material Icons in your iOS project in an easy and space-efficient way! The built-in icons are from

Challenge-swiftui-space - Project for SwiftUI Dev Sprints on Devpass
Challenge-swiftui-space - Project for SwiftUI Dev Sprints on Devpass

SwiftUI Challenge - Space App 🚀 Neste desafio, desenvolveremos a interface de u

ARSpaceStation - A minimal AR iOS App that shows the International Space Station (ISS) in real scale
ARSpaceStation - A minimal AR iOS App that shows the International Space Station (ISS) in real scale

AR Space Station This is a minimal AR iOS app that shows the International Space

An iOS AR app that allows you to walk in the International Space Station
An iOS AR app that allows you to walk in the International Space Station

AR Inside International Space Station (ISS) An iOS AR app that allows you to walk in the International Space Station. Xcode 13.2.1 Target: iOS / iPadO

ARVideoPortal - A Minimal iOS AR app to display 360 / video in sphere space
ARVideoPortal - A Minimal iOS AR app to display 360 / video in sphere space

AR Video Portal A minimal iOS AR app to display 360 / video in sphere space. Xco

This is a minimal AR Portal app that uses the RealityKit's occlusion materials to hide the inner space
This is a minimal AR Portal app that uses the RealityKit's occlusion materials to hide the inner space

AR Simple Portal This is a minimal AR Portal app that uses the RealityKit's occlusion materials to hide the inner space. Xcode 13.2.1 Target: iOS / iP

ARInRoomISS - A minimal iOS AR app that displays the International Space Station (ISS) in the room
ARInRoomISS - A minimal iOS AR app that displays the International Space Station (ISS) in the room

A minimal iOS AR app to display the International Space Station (ISS) in the room.

ARVideoInSpace - A Minimal iOS AR app to play a NASA video in space
ARVideoInSpace - A Minimal iOS AR app to play a NASA video in space

AR Video In Space A minimal iOS AR app for playing a NASA video in outer space.

A clicker-like game based on the concept of a Knight abandoned in space
A clicker-like game based on the concept of a Knight abandoned in space

A Knight in Space A clicker-like game based on the concept of a Knight abandoned in space. Team Members Albin Shrestha, Zac Galer, Connor Kite, and Ma

Comments
Owner
simon gladman
simon gladman
RoboSuitcase - A robotic suitcase controlled using an Apple Watch

Tartanhacks2022 — RoboSuitcase A robotic suitcase controlled using an Apple Watc

Ravi Dudhagra 3 Mar 22, 2022
A simple, declarative, functional drawing framework, in Swift!

DePict - A simple, declarative, functional drawing framework. To produce a drawing, call the Draw function (just type Draw and let autocomplete do the

David Cairns 35 Sep 16, 2021
PullToRefresh extension for all UIScrollView type classes with animated text drawing style

PullToRefreshCoreText PullToRefresh extension for all UIScrollView type classes with animated text drawing style Demo Install Manual Copy the files in

Cem Olcay 314 Dec 3, 2022
A Fast and Complete Swift Drawing Library

FastDraw A Fast and Complete Swift Drawing Library Description FastDraw is a high performance and highly extensible Drawing Library that supports Appl

Collin Zhang 18 Nov 14, 2022
A simple, declarative, functional drawing framework, in Swift!

DePict - A simple, declarative, functional drawing framework. To produce a drawing, call the Draw function (just type Draw and let autocomplete do the

David Cairns 35 Sep 16, 2021
Drawing and Geometry made easy on iOS - now in Swift 3.0

InkKit Swift Support Swift 4.0 InkKit is Swift 4.0 by default, so to use that just include InkKit in your podfile: pod 'InkKit' Swift 3.2 In order to

Shaps 373 Dec 27, 2022
NXDrawKit is a simple and easy but useful drawing kit for iPhone

⚠️ To use with Swift 5.0 please ensure you are using >= 0.8.0 ⚠️ ⚠️ To use with Swift 4.2 please ensure you are using >= 0.7.1 ⚠️ ⚠️ To use with Swift

Nicejinux 1.3k Dec 31, 2022
it's simple drawing app.

IOSObjC_KidsBoard The application is develop in Objective IOS. kids can draw whatever they want and also kids can save the drawing as well as undo era

Vnnovate Solutions Pvt Ltd 0 Oct 28, 2021
it's simple drawing app

IOSObjC_KidsBoard The application is develop in Objective IOS. kids can draw whatever they want and also kids can save the drawing as well as undo era

Jiten Engineer 0 Oct 28, 2021
A Swift library for parsing and drawing SVG images to CoreGraphics contexts.

SwiftDraw A Swift library for parsing and drawing SVG images to CoreGraphics contexts. SwiftDraw can also convert an SVG into Swift source code. Usage

Simon Whitty 119 Jan 3, 2023