Skip to content

Instantly share code, notes, and snippets.

@wizard1066
Created February 21, 2023 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wizard1066/dc561b1c7a8cccfb5925f068a66e26c9 to your computer and use it in GitHub Desktop.
Save wizard1066/dc561b1c7a8cccfb5925f068a66e26c9 to your computer and use it in GitHub Desktop.
import SwiftUI
import ARKit
struct Fonts {
static func avenirNextCondensedBold (size: CGFloat) -> Font {
return Font.custom("AvenirNextCondensed-Bold", size: size)
}
static func neutonRegular (size: CGFloat) -> Font {
return Font.custom("Neuton-Regular", size: size)
}
}
struct ContentView: View {
@State var arview = ARSCNView()
var body: some View {
VStack {
CustomARView(view: arview)
}
.padding()
}
}
struct CustomARView: UIViewRepresentable {
typealias UIViewType = ARSCNView
var view:ARSCNView
var options: [Any] = []
func makeUIView(context: Context) -> ARSCNView {
view.session.delegate = context.coordinator
view.scene.background.contents = UIColor.gray
return view
}
func updateUIView(_ view: ARSCNView, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self.view)
}
}
class Coordinator: NSObject, ARSCNViewDelegate, ARSessionDelegate {
private var trackingView:ARSCNView
private var faceGeometry: ARSCNFaceGeometry!
private var faceNode: SCNNode!
private var spawnTime:TimeInterval = 0
private var cylinderNode: SCNNode!
private var coreNode: SCNNode!
private var textNodes = [SCNNode](repeating: SCNNode(), count: 9)
private var shadowNodes = [SCNNode](repeating: SCNNode(), count: 9)
private var coreNodes = [SCNNode](repeating: SCNNode(), count: 9)
private var plainNodes = [SCNNode](repeating: SCNNode(), count: 9)
private var paperNodes = [SCNNode](repeating: SCNNode(), count: 9)
private var colours = [UIColor](repeating: UIColor(), count: 9)
private var fmotion = false
private var bmotion = false
private var doubleLock = true
private var prefish = 0
private var postfish = 0
private var previousIndex = 0
private var postIndex = 0
private var currentIndex = 8 {
didSet {
previousIndex = currentIndex - 1
if previousIndex < 0 {
previousIndex = 8
}
previousIndex = previousIndex % 9
prefish = previousIndex - 1
if prefish < 0 {
prefish = 8
}
prefish = prefish % 9
currentIndex = currentIndex % 9
postIndex = currentIndex + 1
postIndex = postIndex % 9
postfish = postIndex + 1
postfish = postfish % 9
}
}
private var angle = 360.0 / 9
private let directs = ["2Quick","3Brown","4Fox","5Jumps","6Over","7The","8Lazy","9Dog","1The"]
private let indirects = ["kciuQ2","nworB3","xoF4","spmuJ5","revO6","ehT7","yzaL8","goD9","ehT1"]
private var flip = false
init(_ view: ARSCNView) {
self.trackingView = view
super.init()
guard ARFaceTrackingConfiguration.isSupported else {
fatalError("Face tracking not available on this on this device model!")
}
let configuration = ARFaceTrackingConfiguration()
self.trackingView.session.run(configuration)
self.trackingView.delegate = self
let geo = SCNCylinder(radius: 0.12, height: 0.05)
geo.radialSegmentCount = 9
cylinderNode = SCNNode(geometry: geo)
cylinderNode.geometry?.firstMaterial?.fillMode = .fill
cylinderNode.geometry?.firstMaterial?.diffuse.contents = UIColor.gray.withAlphaComponent(1)
var geo2 = SCNPlane(width: 0.025, height: 0.08)
geo2.firstMaterial?.fillMode = .fill
//geo2.firstMaterial?.diffuse.contents = UIColor.blue.withAlphaComponent(0.9)
//let newColor = UIColor(red: 255/255, green: 105/255, blue: 255/255, alpha: 0.9)
//geo2.firstMaterial?.diffuse.contents = newColor
for no in 1...9 {
let character = UnicodeScalar(no + 64)
// textNodes[no - 1] = newTextNode(Text: "\(no)")
let doo = Double(no) * 8.4 + 10
let doo2 = Double(no) * 8.4 + 10
let doo3 = Double(no) * 8.4 + 10
let newColor = UIColor(red: doo/255, green:doo2/255, blue: doo3/255, alpha: 0.8)
let fixedColor = UIColor(red: 0.203922, green: 0.203922, blue: 0.203922, alpha: 0.8)
//textNodes[no - 1] = newTextNode(Text: String(character!), newColor: fixedColor)
colours[no - 1] = UIColor.gray
textNodes[no - 1] = newTextNode(Text: directs[no - 1], newColor: fixedColor)
shadowNodes[no - 1] = newTextNode(Text: indirects[no - 1], newColor: fixedColor)
//textNodes[no - 1].simdPosition.z += 0.1
coreNodes[no - 1] = SCNNode()
coreNodes[no - 1].simdEulerAngles.y = GLKMathDegreesToRadians(Float(angle * Double(no)))
cylinderNode.addChildNode(coreNodes[no - 1])
//coreNodes[no - 1].addChildNode(textNodes[no - 1])
var geo2 = SCNPlane(width: 0.025, height: 0.08)
geo2.firstMaterial?.fillMode = .fill
let foo = Double(no) * 4.4 + 80
let foo2 = Double(no) * 6.4 + 80
let foo3 = Double(no) * 8.4 + 80
let newColor2 = UIColor(red: foo/255, green:foo2/255, blue: foo3/255, alpha: 1)
geo2.firstMaterial?.diffuse.contents = newColor2
plainNodes[no - 1] = SCNNode(geometry: geo2)
paperNodes[no - 1] = SCNNode(geometry: geo2)
plainNodes[no - 1].addChildNode(textNodes[no - 1])
paperNodes[no - 1].addChildNode(shadowNodes[no - 1])
paperNodes[no - 1].isHidden = true
plainNodes[no - 1].isHidden = false
coreNodes[no - 1].addChildNode(plainNodes[no - 1])
coreNodes[no - 1].addChildNode(paperNodes[no - 1])
plainNodes[no - 1].simdPosition.z += 0.14
paperNodes[no - 1].simdPosition.z += 0.14
}
// let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
// self.trackingView.addGestureRecognizer(tapGesture)
let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
swipeLeftGesture.direction = .left
self.trackingView.addGestureRecognizer(swipeLeftGesture)
let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
swipeRightGesture.direction = .right
self.trackingView.addGestureRecognizer(swipeRightGesture)
}
// @objc func handleTap(_ sender: UIGestureRecognizer? = nil) {
// let tapLocation = sender?.location(in: self.trackingView)
// SCNTransaction.begin()
// SCNTransaction.animationDuration = 1
// cylinderNode.simdEulerAngles.y -= GLKMathDegreesToRadians(10)
// SCNTransaction.commit()
// }
@objc func handleSwipe(_ sender: UISwipeGestureRecognizer? = nil) {
switch sender?.direction {
case UISwipeGestureRecognizer.Direction.right:
SCNTransaction.begin()
SCNTransaction.animationDuration = 1
cylinderNode.simdEulerAngles.y += GLKMathDegreesToRadians(Float(angle))
currentIndex -= 1
if currentIndex < 0 {
currentIndex = 8
}
SCNTransaction.commit()
print("RIGHT prefish \(prefish) previousIndex \(previousIndex) currentIndex \(currentIndex) postIndex \(postIndex) postfish \(postfish)")
if postfish == 0 {
flip.toggle()
}
if flip {
paperNodes[postfish].isHidden = false
plainNodes[postfish].isHidden = true
}
if !flip {
paperNodes[postfish].isHidden = true
plainNodes[postfish].isHidden = false
}
case UISwipeGestureRecognizer.Direction.left:
print("LEFT prefish \(prefish) previousIndex \(previousIndex) currentIndex \(currentIndex) postIndex \(postIndex) postfish \(postfish)")
SCNTransaction.begin()
SCNTransaction.animationDuration = 1
cylinderNode.simdEulerAngles.y -= GLKMathDegreesToRadians(Float(angle))
currentIndex += 1
currentIndex = currentIndex % 9
SCNTransaction.commit()
if postfish == plainNodes.count - 1 {
flip.toggle()
}
if flip {
paperNodes[postfish].isHidden = false
plainNodes[postfish].isHidden = true
}
if !flip {
paperNodes[postfish].isHidden = true
plainNodes[postfish].isHidden = false
}
case UISwipeGestureRecognizer.Direction.up:
break
case UISwipeGestureRecognizer.Direction.down:
break
default:
assert(false,"NeverHappens")
}
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
self.trackingView.scene.rootNode.addChildNode(cylinderNode)
cylinderNode.simdPosition.z -= 0.4
return nil
let device = trackingView.device
faceGeometry = ARSCNFaceGeometry(device: device!)
faceNode = SCNNode(geometry: faceGeometry)
faceNode.geometry?.firstMaterial?.fillMode = .lines
faceNode.geometry?.firstMaterial?.diffuse.contents = UIColor.clear.withAlphaComponent(1)
faceNode.addChildNode(cylinderNode)
return faceNode
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else { return }
faceGeometry.update(from: faceAnchor.geometry)
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
if textNodes[currentIndex].scale.x != 0.4 {
for i in 0...7 {
textNodes[i].scale = SCNVector3Make(0.04, 0.08, 0.04)
let material = SCNMaterial()
material.lightingModel = .physicallyBased
material.metalness.contents = 0.2
material.roughness.contents = 0.5
//textNodes[i].geometry?.materials = [material]
textNodes[i].geometry?.firstMaterial?.diffuse.contents = colours[i]
}
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.4
textNodes[previousIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
textNodes[previousIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
textNodes[postIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
textNodes[postIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
textNodes[currentIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.white
textNodes[currentIndex].scale = SCNVector3Make(0.04, 0.12, 0.06)
shadowNodes[previousIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
shadowNodes[previousIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
shadowNodes[postIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black
shadowNodes[postIndex].scale = SCNVector3Make(0.04, 0.10, 0.04)
shadowNodes[currentIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.white
shadowNodes[currentIndex].scale = SCNVector3Make(0.04, 0.12, 0.06)
SCNTransaction.commit()
}
if time > spawnTime {
spawnTime = time + TimeInterval(1)
}
}
func newTextNode(Text:String, newColor:UIColor) -> SCNNode {
let textGeo = SCNText(string: Text, extrusionDepth: 0.01)
let font = UIFont(name: "Neuton-Regular", size: 0.7)
textGeo.font = font
textGeo.firstMaterial!.diffuse.contents = newColor
textGeo.firstMaterial?.fillMode = .fill
textGeo.alignmentMode = CATextLayerAlignmentMode.center.rawValue
textGeo.chamferRadius = 0.001
let (minBound, maxBound) = textGeo.boundingBox
let textNode = SCNNode(geometry: textGeo)
textNode.pivot = SCNMatrix4MakeTranslation( (maxBound.x - minBound.x)/2 , minBound.y / 0.80 , 0)
textNode.scale = SCNVector3Make(0.04, 0.08, 0.04)
return textNode
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment