Let’s build a water molecule with ARKit

Written by sandeepjoshi1910 | Published 2018/07/23
Tech Story Tags: ios | swift-4 | augmented-reality | arkit | mobile-app-development

TLDRvia the TL;DR App

Water forms a simple molecular structure which we will build in AR with Apple’s ARKit. We’ll learn basics of ARKit like working with nodes and manipulating them.

  • Start a new AR project in XCode with SceneKit as content technology
  • Build and Run to make sure everything is fine — You should be able to see the default spaceship
  • Now, delete the following lines of spaceship which is not needed

// Create a new scenelet scene = SCNScene(named: "art.scnassets/ship.scn")!// Set the scene to the viewsceneView.scene = scene

For reference, the x, y and z axes are as below. The camera is at the origin.

Oxygen is bigger than Hydrogen. So, we’ll first place an Oxygen atom. We’ll work with the ViewController.swift from now on.

  • Add an anchor for the oxygen.

func getMainAnchor() -> ARAnchor {

// Creates an anchor at a distance 0.1m in front of you . 

var anchorPosition = matrix\_identity\_float4x4

anchorPosition.columns.3.z = -0.1

let mainAnchor = ARAnchor(transform: anchorPosition)

return mainAnchor

}

  • Add the following code to viewDidLoadfunction.

let sphere = SCNSphere(radius: 0.01)

let oxygenNode = SCNNode(geometry: sphere)

oxygenNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red

sceneView.session.add(anchor: self.getMainAnchor())

sceneView.scene.rootNode.addChildNode(oxygenNode)

oxygenNode.position = SCNVector3(0.0, 0.0, Float(-0.1))

The above code creates a sphere geometry and an SCNNode for the Oxygen atom. Then, add the AR Anchor to the sceneView’s session and then add the Oxygen atom node as a child to the sceneview’s root node. This puts a red sphere at 0.1m in front of you.

Fig 1. Oxygen Atom

Now we should think about how we are going to construct the molecule by analyzing the structure.

Fig 2. Top view of the molecule

From the structure we have here, we need three atoms(spheres) and two tubes/cylinders which are separated by the specified angle. Also, the axis of the tubes need to be in X-Z plane(axis of the tube here means an imaginary line passing through the center of the tube length wise). Now this can be done in multiple ways by various translations and rotations. I will pick the one which is relatively easy.

  1. Place the Oxygen atom first
  2. Add a tube/cylinder(bond) which at first is pointing in +Y direction

Fig 3. Tube(O-H bond) on top of Oxygen

3. Now rotate the tube 90º along Z axis to place the tube in X-Z axis

Fig 4. OH bond rotated by 90 degrees

4. Rotate again by specified angle to form the water molecule. Do this for other O-H bond too

Fig 5. OH bond rotated by 37.775 degrees along Y axis

5. Add the Hydrogen atoms and you have the water molecule.

Now lets put this into code…

The following code adds two tubes at the head of the Oxygen atom already in place as shown in Fig 3.

By default the pivot of the tube is at the center. We change that and place it at the lower end of the tube.

let tube = SCNTube(innerRadius: 0.001, outerRadius: 0.0015, height: 0.05)

let OHbondOne = SCNNode(geometry: tube)OHbondOne.geometry?.firstMaterial?.diffuse.contents = UIColor.blue

let OHbondTwo = SCNNode(geometry: tube)OHbondTwo.geometry?.firstMaterial?.diffuse.contents = UIColor.blue

OHbondOne.position = SCNVector3(0.0, 0.025, -0.1)OHbondOne.pivot = SCNMatrix4MakeTranslation(0, -0.025, 0)OHbondOne.position.y = OHbondOne.position.y - 0.025

OHbondTwo.position = SCNVector3(0.0, 0.025, -0.1)OHbondTwo.pivot = SCNMatrix4MakeTranslation(0, -0.025, 0)OHbondTwo.position.y = OHbondTwo.position.y - 0.025

sceneView.scene.rootNode.addChildNode(OHbondOne)sceneView.scene.rootNode.addChildNode(OHbondTwo)

Now we need to rotate the tubes 90º clockwise to bring them in X-Z axis as in Fig 4.

let rotateAlongZAxis = SCNAction.rotateBy(x:0 , y: 0, z: -CGFloat(Float.pi/2), duration: 1)

OHbondOne.runAction(rotateAlongZAxis)OHbondTwo.runAction(rotateAlongZAxis)

Now we need to rotate the tubes along Y axis to get the molecular structure (Fig 5). But we need to determine to what angle it needs to be rotated. Simple analysis of the angles gives us the following.

We need to rotate first tube by 37.755º and second tube by 142.205º(37.755º+104.45º).

let rotateOHBondOneAlongYAxis = SCNAction.rotateBy(x:CGFloat(self.degreesToRadians(degrees: 37.755)), y: 0, z: 0, duration: 0.1)

let rotateOHBondTwoAlongYAxis = SCNAction.rotateBy(x:CGFloat(self.degreesToRadians(degrees: 142.205)), y: 0, z: 0, duration: 0.1)

OHbondOne.runAction(rotateOHBondOneAlongYAxis)

OHbondTwo.runAction(rotateOHBondTwoAlongYAxis)

Function to convert degrees to radians

func degreesToRadians(degrees: Float) -> Float {return (degrees * .pi) / 180.0}

Fig 6. OH bonds after rotation along Y axis

Now we need to add the Hydrogen atoms. We know where to add — at the other end of the bonds. But the problem is how exactly do we do it?

Fig 7. Hydrogen atom displacement calculation

So for H1, Place the Hydrogen atom in the same place as Oxygen and then

  • move ‘b’ distance in -X direction ( To place the node on left side of Oxygen)
  • move ‘a’ distance in -Z direction

and for H2, Place the Hydrogen atom in the same place of Oxygen and then

  • move ‘b’ distance in +X direction ( To place the node on right side of Oxygen)
  • move ‘a’ distance in -Z direction

Here is the code to do that,

  • Create Hydrogen nodes

let hSphere = SCNSphere(radius: 0.007)

let hOneNode = SCNNode(geometry: hSphere)hOneNode.geometry?.firstMaterial?.diffuse.contents = UIColor.gray

let hTwoNode = SCNNode(geometry: hSphere)hTwoNode.geometry?.firstMaterial?.diffuse.contents = UIColor.gray

hOneNode.position = SCNVector3(0.0, 0.0, Float(-0.1))hTwoNode.position = SCNVector3(0.0, 0.0, Float(-0.1))

sceneView.scene.rootNode.addChildNode(hOneNode)sceneView.scene.rootNode.addChildNode(hTwoNode)

self.placeHOneAtom(hone: hOneNode)self.placeHTwoAtom(htwo: hTwoNode)

  • The two functions to perform the x and z displacements mentioned in Fig. 7

// 0.05m is the bond(tube) length here

func placeHOneAtom(hone: SCNNode) {

hone.position.x = hone.position.x -cos(self.degreesToRadians(degrees: 37.755)) \* 0.05

hone.position.z = hone.position.z + sin(self.degreesToRadians(degrees: 37.755)) \* 0.05

}

func placeHTwoAtom(htwo: SCNNode) {

htwo.position.x = htwo.position.x +    cos(self.degreesToRadians(degrees: 37.755)) \* 0.05

htwo.position.z = htwo.position.z + sin(self.degreesToRadians(degrees: 37.755)) \* 0.05

}

Finally, we have the molecule once all of the code is added…

The completed project is available here.

Note:

  • Instead of changing the pivot of the tubes and rotating them, one could just rotate them along Z & Y axes as we did and then do the displacements along X and Z axes similar to the Hydrogen atoms. That will also give the same effect.

Published by HackerNoon on 2018/07/23