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

Custom boxes for directions #185

Merged
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
79 changes: 38 additions & 41 deletions ARCL/Source/Nodes/PolylineNode.swift
Expand Up @@ -10,51 +10,52 @@ import Foundation
import SceneKit
import MapKit


/// A block that will build an SCNBox with the provided distance.
/// Note: the distance should be aassigned to the length
public typealias BoxBuilder = (_ distance: CGFloat) -> SCNBox

/// A Node that is used to show directions in AR-CL.
public class PolylineNode {
public private(set) var locationNodes = [LocationNode]()

public let polyline: MKPolyline
public let altitude: CLLocationDistance

private let lightNode: SCNNode = {
let node = SCNNode()
node.light = SCNLight()
node.light!.type = .ambient
if #available(iOS 10.0, *) {
node.light!.intensity = 25
}
node.light!.attenuationStartDistance = 100
node.light!.attenuationEndDistance = 100
node.position = SCNVector3(x: 0, y: 10, z: 0)
node.castsShadow = false
node.light!.categoryBitMask = 3
return node
}()

private let lightNode3: SCNNode = {
let node = SCNNode()
node.light = SCNLight()
node.light!.type = .omni
if #available(iOS 10.0, *) {
node.light!.intensity = 100
}
node.light!.attenuationStartDistance = 100
node.light!.attenuationEndDistance = 100
node.light!.castsShadow = true
node.position = SCNVector3(x: -10, y: 10, z: -10)
node.castsShadow = false
node.light!.categoryBitMask = 3
return node
}()

public init(polyline: MKPolyline, altitude: CLLocationDistance) {
public let boxBuilder: BoxBuilder

/// Creates a `PolylineNode` from the provided polyline, altitude (which is assumed to be uniform
/// for all of the points) and an optional SCNBox to use as a prototype for the location boxes.
///
/// - Parameters:
/// - polyline: The polyline that we'll be creating location nodes for.
/// - altitude: The uniform altitude to use to show the location nodes.
/// - boxBuilder: A block that will customize how a box is built.
public init(polyline: MKPolyline, altitude: CLLocationDistance, boxBuilder: BoxBuilder? = nil) {
self.polyline = polyline
self.altitude = altitude
self.boxBuilder = boxBuilder ?? Constants.defaultBuilder

contructNodes()
}

fileprivate func contructNodes() {
}

// MARK: - Implementation

private extension PolylineNode {

struct Constants {
static let defaultBuilder: BoxBuilder = { (distance) -> SCNBox in
let box = SCNBox(width: 1, height: 0.2, length: distance, chamferRadius: 0)
box.firstMaterial?.diffuse.contents = UIColor(red: 47.0/255.0, green: 125.0/255.0, blue: 255.0/255.0, alpha: 1.0)
return box
}
}

/// This is what actually builds the SCNNodes and appends them to the
/// locationNodes collection so they can be added to the scene and shown
/// to the user. If the prototype box is nil, then the default box will be used
func contructNodes() {
let points = polyline.points()

for i in 0 ..< polyline.pointCount - 1 {
Expand All @@ -63,23 +64,19 @@ public class PolylineNode {

let distance = currentLocation.distance(from: nextLocation)

let box = SCNBox(width: 1, height: 0.2, length: CGFloat(distance), chamferRadius: 0)
box.firstMaterial?.diffuse.contents = UIColor(red: 47.0/255.0, green: 125.0/255.0, blue: 255.0/255.0, alpha: 1.0)
let box = boxBuilder(CGFloat(distance))
let boxNode = SCNNode(geometry: box)

let bearing = -currentLocation.bearing(between: nextLocation)

let boxNode = SCNNode(geometry: box)
boxNode.pivot = SCNMatrix4MakeTranslation(0, 0, 0.5 * Float(distance))
boxNode.eulerAngles.y = Float(bearing).degreesToRadians
boxNode.categoryBitMask = 3
boxNode.addChildNode(lightNode)
boxNode.addChildNode(lightNode3)

let locationNode = LocationNode(location: currentLocation)
locationNode.addChildNode(boxNode)

locationNodes.append(locationNode)
}

}

}
12 changes: 10 additions & 2 deletions ARCL/Source/SceneLocationView.swift
Expand Up @@ -276,11 +276,19 @@ public extension SceneLocationView {
@available(iOS 11.0, *)
public extension SceneLocationView {

func addRoutes(routes: [MKRoute]) {
/// Adds routes to the scene and lets you specify the geometry prototype for the box.
/// Note: You can provide your own SCNBox prototype to base the direction nodes from.
///
/// - Parameters:
/// - routes: The MKRoute of directions.
/// - boxBuilder: A block that will customize how a box is built.
func addRoutes(routes: [MKRoute], boxBuilder: BoxBuilder? = nil) {
guard let altitude = sceneLocationManager.currentLocation?.altitude else {
return assertionFailure("we don't have an elevation")
}
let polyNodes = routes.map { PolylineNode(polyline: $0.polyline, altitude: altitude - 2.0) }
let polyNodes = routes.map {
PolylineNode(polyline: $0.polyline, altitude: altitude - 2.0, boxBuilder: boxBuilder)
}

polylineNodes.append(contentsOf: polyNodes)
polyNodes.forEach {
Expand Down
2 changes: 1 addition & 1 deletion ARKit+CoreLocation.xcodeproj/project.pbxproj
Expand Up @@ -305,7 +305,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
};
F3C651027697D46CB173FF94 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
Expand Down
21 changes: 21 additions & 0 deletions ARKit+CoreLocation/Assets.xcassets/box0.imageset/Contents.json
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "box0.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions ARKit+CoreLocation/Assets.xcassets/box1.imageset/Contents.json
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "box1.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions ARKit+CoreLocation/Assets.xcassets/box2.imageset/Contents.json
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "box2.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions ARKit+CoreLocation/Assets.xcassets/box3.imageset/Contents.json
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "box3.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions ARKit+CoreLocation/Assets.xcassets/box4.imageset/Contents.json
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "box4.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions ARKit+CoreLocation/Assets.xcassets/box5.imageset/Contents.json
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "box5.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion ARKit+CoreLocation/POIViewController.swift
Expand Up @@ -183,9 +183,25 @@ extension POIViewController {
return
}

let box = SCNBox(width: 1, height: 0.2, length: 5, chamferRadius: 0.25)
box.firstMaterial?.diffuse.contents = UIColor.gray.withAlphaComponent(0.5)

// 2. If there is a route, show that
if let routes = routes {
sceneLocationView.addRoutes(routes: routes)
sceneLocationView.addRoutes(routes: routes) { distance -> SCNBox in
let box = SCNBox(width: 1.75, height: 0.5, length: distance, chamferRadius: 0.25)

// // Option 1: An absolutely terrible box material set (that demonstrates what you can do):
// box.materials = ["box0", "box1", "box2", "box3", "box4", "box5"].map {
// let material = SCNMaterial()
// material.diffuse.contents = UIImage(named: $0)
// return material
// }

// Option 2: Something more typical
box.firstMaterial?.diffuse.contents = UIColor.blue.withAlphaComponent(0.7)
return box
}
} else {
// 3. If not, then show the
buildDemoData().forEach {
Expand Down