import React from "react" import { StyleSheet, View, TouchableHighlight, Image } from "react-native" import { ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroAmbientLight, ViroDirectionalLight, ViroMaterials, Viro3DObject, ViroSpotLight, ViroNode, ViroQuad, ViroConstants, } from "react-viro" import autobind from "autobind-decorator" import backImage from "./images/back.png" import { config } from "../config" import "url-search-params-polyfill" const shapes = { order: { source: require("./models/hardhat_obj.obj"), resources: [require("./models/hardhat.mtl")], scale: [0.5, 0.5, 0.5], }, complaint: { source: require("./models/question_obj.obj"), resources: [require("./models/question.mtl")], scale: [1, 1, 1], }, inspection: { source: require("./models/clipboard_obj.obj"), resources: [require("./models/clipboard.mtl")], scale: [1, 1, 1], }, } const distance = (vectorA, vectorB) => { return Math.sqrt( (vectorB[0] - vectorA[0]) * (vectorB[0] - vectorA[0]) + (vectorB[1] - vectorA[1]) * (vectorB[1] - vectorA[1]) + (vectorB[2] - vectorA[2]) * (vectorB[2] - vectorA[2]) ) } class WorkItemSceneAR extends React.Component { constructor(props) { super(props) this.state = { position: [0, 0, 0], rotation: [0, 0, 0], shouldBillboard: true, trackingInitialized: false, haveAnchor: false, } } @autobind handleLoadEnd() { this.arScene.getCameraOrientationAsync().then((orientation) => { return this.arScene .performARHitTestWithRay(orientation.forward) .then((results) => { const { forward, position } = orientation // Default position is just one meter in front of the user const defaultPosition = [forward[0] * 1.0, forward[1] * 1.0, forward[2]] * 1.0 let hitResultPosition = null // Filter the hit test results based on the position. for (var i = 0; i < results.length; i++) { let result = results[i] if (result.type == "ExistingPlaneUsingExtent") { let distance = Math.sqrt( (result.transform.position[0] - position[0]) * (result.transform.position[0] - position[0]) + (result.transform.position[1] - position[1]) * (result.transform.position[1] - position[1]) + (result.transform.position[2] - position[2]) * (result.transform.position[2] - position[2]) ) if (distance > 1 && distance < 15) { // If we found a plane a decent distance away, choose it! hitResultPosition = result.transform.position break } } else if (result.type == "FeaturePoint" && !hitResultPosition) { // If we haven't found a plane and this feature point is within range, // then we'll use it as the initial display point let d = distance(position, result.transform.position) if (d > 1 && d < 15) { hitResultPosition = result.transform.position } } } this.setState({ position: hitResultPosition || defaultPosition, }) setTimeout(() => { this.updateInitialRotation() }, 200) }) .catch((err) => {}) }) } @autobind updateInitialRotation() { this.arNode.getTransformAsync().then((retDict) => { let rotation = retDict.rotation let absX = Math.abs(rotation[0]) let absZ = Math.abs(rotation[2]) let yRotation = rotation[1] // If the X and Z aren't 0, then adjust the y rotation. if (absX > 1 && absZ > 1) { yRotation = 180 - yRotation } this.setState({ rotation: [0, yRotation, 0], shouldBillboard: false, }) }) } @autobind handleTrackingUpdated(state, reason) { if ( !this.state.trackingInitialized && state === ViroConstants.TRACKING_NORMAL ) { this.setState({ trackingInitialized: true }) } } @autobind handleAnchorFound() { this.setState({ haveAnchor: true }) } @autobind handleClick(position, source) { const { workItemId } = this.props this.props.history.replace( `/activity${workItemId ? "?workItemId=" + workItemId : ""}` ) } render() { const { position, scale, rotation, shouldBillboard, trackingInitialized, haveAnchor, } = this.state const shape = shapes[this.props.workItemType] return ( (this.arScene = ref)} onTrackingUpdated={this.handleTrackingUpdated} onAnchorFound={this.handleAnchorFound}> (this.arNode = ref)} transformBehaviors={shouldBillboard ? "billboardY" : []} position={position} rotation={rotation}> {shape && trackingInitialized && haveAnchor && ( )} ) } } export class ARViewer extends React.Component { constructor(props) { super(props) const { search } = this.props.location if (search) { const params = new URLSearchParams(search) this.workItemId = params.get("workItemId") this.workItemType = params.get("workItemType") } } @autobind handleBackPress() { this.props.history.replace("/") } render() { return ( ) } }