Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make it usable for macOS and iOS #2

Merged
merged 2 commits into from Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 22 additions & 2 deletions Sources/CameraControlARView/ARViewContainer.swift
Expand Up @@ -5,15 +5,21 @@
// Created by Joseph Heck on 2/9/22.
//

#if os(iOS)
import UIKit
typealias PlatformViewRepresentable = UIViewRepresentable
#elseif os(macOS)
import Cocoa
typealias PlatformViewRepresentable = NSViewRepresentable
#endif
import RealityKit
import SwiftUI

/// A SwiftUI representable view that wraps an underlying augmented reality view with camera controls instance.
///
/// Create an ``CameraControlARView`` externally and hand it into the container so that you can interact with the
/// view controls, or the underlying scene, from within SwiftUI.
public struct ARViewContainer: NSViewRepresentable {
public struct ARViewContainer: PlatformViewRepresentable {
/// The type of view this container wraps.
public typealias NSViewType = RealityKit.ARView

Expand All @@ -26,6 +32,12 @@ public struct ARViewContainer: NSViewRepresentable {
}

/// Creates a new SwiftUI view.
#if os(iOS)
public func makeUIView(context _: Context) -> ARView {
let arView = cameraARView
return arView
}
#elseif os(macOS)
public func makeNSView(context _: Context) -> ARView {
// Creates the view object and configures its initial state.
//
Expand All @@ -37,11 +49,19 @@ public struct ARViewContainer: NSViewRepresentable {
let arView = cameraARView
return arView
}

#endif

#if os(iOS)
/// Updates the wrapped AR view with state information from SwiftUI.
public func updateUIView(_: ARView, context _: Context) {
// Updates the state of the specified view with new information from SwiftUI.
}
#elseif os(macOS)
/// Updates the wrapped AR view with state information from SwiftUI.
public func updateNSView(_: ARView, context _: Context) {
// Updates the state of the specified view with new information from SwiftUI.
}
#endif

/// Creates a new SwiftUI view that wraps and displays an augmented reality view.
/// - Parameter cameraARView: An instance of the camera-controlled AR View.
Expand Down
77 changes: 62 additions & 15 deletions Sources/CameraControlARView/CameraControlARView.swift
Expand Up @@ -5,8 +5,16 @@
// Created by Joseph Heck on 2/7/22.
//

import Cocoa
#if os(iOS)
import UIKit
import ARKit
#endif
#if os(macOS)
import AppKit
#endif
import RealityKit
import Foundation
import CoreGraphics

/// An augmented reality view for macOS that provides keyboard, trackpad, and mouse movement controls for the camera within the view.
///
Expand Down Expand Up @@ -124,7 +132,7 @@ import RealityKit
/// This view doubles the speed valuewhen the key is held-down.
public var keyspeed: Float

private var dragstart: NSPoint
private var dragstart: CGPoint
private var dragstart_rotation: Float
private var dragstart_inclination: Float
private var magnify_start: Float
Expand Down Expand Up @@ -153,12 +161,21 @@ import RealityKit
/// A copy of the basic transform applied ot the camera, and updated in parallel to reflect "upward" to SwiftUI.
@Published var macOSCameraTransform: Transform

#if os(iOS)
var pinchGesture:UIPinchGestureRecognizer?
@IBAction func pinchRecognized(_ pinch: UIPinchGestureRecognizer) {
let multiplier = ((pinch.scale > 1.0) ? -1.0 : 1.0) * Float(pinch.scale)/100 // magnify_end
radius = radius * (multiplier + 1)
updateCamera()
}
#endif

/// Creates a new AR View with the camera controlled by mouse, keyboard, and/or the trackpad.
///
/// The default motion mode for the view is ``MotionMode-swift.enum/arcball``, which orbits the camera around a specific point in space.
///
/// - Parameter frameRect: The frame rectangle for the view, measured in points.
public required init(frame frameRect: NSRect) {
public required init(frame frameRect: CGRect) {
motionMode = .arcball

// ARCBALL mode
Expand All @@ -178,18 +195,24 @@ import RealityKit

// Not mode specific
cameraAnchor = AnchorEntity(world: .zero)
dragstart = NSPoint.zero
dragstart = CGPoint.zero
dragstart_transform = cameraAnchor.transform.matrix
// reflect the camera's transform as an observed object
macOSCameraTransform = cameraAnchor.transform
super.init(frame: frameRect)

#if os(macOS) || targetEnvironment(simulator)
let cameraEntity = PerspectiveCamera()
cameraEntity.camera.fieldOfViewInDegrees = 60
cameraAnchor.addChild(cameraEntity)
scene.addAnchor(cameraAnchor)

#endif
updateCamera()

#if os(iOS)
self.pinchGesture = UIPinchGestureRecognizer(target: self, action:#selector(pinchRecognized(_:)))
self.addGestureRecognizer(self.pinchGesture!)
#endif
}

// MARK: - rotational transforms
Expand Down Expand Up @@ -296,10 +319,7 @@ import RealityKit
fatalError("init(coder:) has not been implemented")
}

override open dynamic func mouseDown(with event: NSEvent) {
// print("mouseDown EVENT: \(event)")
// print(" at \(event.locationInWindow) of \(self.frame)")
dragstart = event.locationInWindow
func dragStart() {
switch motionMode {
case .arcball:
dragstart_rotation = rotationAngle
Expand All @@ -308,12 +328,8 @@ import RealityKit
dragstart_transform = cameraAnchor.transform.matrix
}
}

override open dynamic func mouseDragged(with event: NSEvent) {
// print("mouseDragged EVENT: \(event)")
// print(" at \(event.locationInWindow) of \(self.frame)")
let deltaX = Float(event.locationInWindow.x - dragstart.x)
let deltaY = Float(event.locationInWindow.y - dragstart.y)

func dragMove(_ deltaX: Float, _ deltaY: Float) {
switch motionMode {
case .arcball:
rotationAngle = dragstart_rotation - deltaX * dragspeed
Expand Down Expand Up @@ -342,6 +358,36 @@ import RealityKit
cameraAnchor.transform = Transform(matrix: combined_transform)
}
}

#if os(iOS)
override open dynamic func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
dragstart = touches.first!.location(in: self)
dragStart()
}

override open dynamic func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let drag = touches.first!.location(in: self)
let deltaX = Float(drag.x - dragstart.x)
let deltaY = Float(dragstart.y - drag.y)
dragMove(deltaX, deltaY)
}
#endif

#if os(macOS)
override open dynamic func mouseDown(with event: NSEvent) {
// print("mouseDown EVENT: \(event)")
// print(" at \(event.locationInWindow) of \(self.frame)")
dragstart = event.locationInWindow
dragStart()
}

override open dynamic func mouseDragged(with event: NSEvent) {
// print("mouseDragged EVENT: \(event)")
// print(" at \(event.locationInWindow) of \(self.frame)")
let deltaX = Float(event.locationInWindow.x - dragstart.x)
let deltaY = Float(event.locationInWindow.y - dragstart.y)
dragMove(deltaX, deltaY)
}

override open dynamic func keyDown(with event: NSEvent) {
// print("keyDown: \(event)")
Expand Down Expand Up @@ -482,4 +528,5 @@ import RealityKit
break
}
}
#endif
}
58 changes: 58 additions & 0 deletions Sources/CameraControlARView/RealityView.swift
@@ -0,0 +1,58 @@
//
// RealityView.swift
// CompleteAnatomy
//
// Created by Volodymyr Boichentsov on 11/10/2022.
// Copyright © 2022 3D4Medical, LLC. All rights reserved.
//

import SwiftUI
import Combine
import RealityKit

fileprivate let arContainer = ARViewContainer.init(cameraARView: CameraControlARView.init(frame: .zero))

public struct RealityKitView: View {

public struct Context {
public var base: RealityKit.Scene?


public func add(_ entity:Entity) {

let originAnchor = AnchorEntity(world: .zero)
originAnchor.addChild(entity)
arContainer.cameraARView.scene.anchors.append(originAnchor)
}
}
let context = Context.init(base: arContainer.cameraARView.scene)
var update: (() -> Void)?
var updateCancellable: Cancellable?

public init(_ content: @escaping (_ context:Context) -> Void, update: (() -> Void)? = nil) {
content(context)
self.update = update

if let update = self.update {
self.updateCancellable = arContainer.cameraARView.scene.subscribe(to: SceneEvents.Update.self) { event in
update()
}
}
}

public var body: some View {
arContainer
}
}

struct RealityView_Previews: PreviewProvider {
static var previews: some View {
RealityKitView( { context in
let entity = ModelEntity.init(mesh: .generateBox(size: SIMD3<Float>.init(repeating: 1)))
context.add(entity)
}, update: {
print("update")
}).frame(width: 300, height: 300)
}
}