import ARToolKit from "./artoolkit/ARToolKit" ; import ARToolKitCameraParam from "./artoolkit/ARToolKitCameraParam" ; import { ARToolKitController } from "./artoolkit/ARToolKitController" ; import * as THREE from "three" ; import { Source } from "./THREEAR" ; import { PatternMarker } from "./PatternMarker" ; import { BarcodeMarker } from "./BarcodeMarker" ; import cameraParametersData from "./artoolkit/CameraParameters" ; export interface MarkerPositioningParameters { smooth : boolean ; smoothCount : number ; smoothTolerance : number ; smoothThreshold : number ; } export interface ControllerParameters { source : Source ; positioning : MarkerPositioningParameters ; lostTimeout : number ; debug : boolean ; changeMatrixMode : "modelViewMatrix" | "cameraTransformMatrix" ; detectionMode : "color" | "color_and_matrix" | "mono" | "mono_and_matrix" ; matrixCodeType : string ; cameraParametersUrl : string | Uint8Array ; maxDetectionRate : number ; width : number ; height : number ; patternRatio : number ; imageSmoothingEnabled : boolean ; } interface Markers { pattern : PatternMarker [ ] ; barcode : BarcodeMarker [ ] ; } param parameters export class Controller extends THREE . EventDispatcher { public postInit : Promise < any > ; public disposed : boolean ; public markers : Markers ; private parameters : ControllerParameters ; private arController : ARToolKitController | null ; private smoothMatrices : any [ ] ; private _artoolkitProjectionAxisTransformMatrix : any ; constructor ( parameters ) { if ( ! parameters . source ) { throw Error ( "Source must be provided" ) ; } super ( ) ; this . parameters = { source : parameters . source , changeMatrixMode : "modelViewMatrix" , lostTimeout : 1000 , positioning : { smooth : true , smoothCount : 5 , smoothTolerance : 0.01 , smoothThreshold : 2 } , debug : false , detectionMode : "mono" , matrixCodeType : "3x3" , cameraParametersUrl : cameraParametersData , maxDetectionRate : 60 , width : 480 , height : 853 , patternRatio : 0.5 , imageSmoothingEnabled : false } ; this . markers = { pattern : [ ] , barcode : [ ] } ; this . smoothMatrices = [ ] ; this . arController = null ; this . setParameters ( parameters ) ; this . disposed = false ; this . postInit = this . initialize ( ) ; } public setParameters ( parameters ) { if ( ! parameters ) { return ; } for ( const key in parameters ) { if ( key ) { const newValue = parameters [ key ] ; if ( newValue === undefined ) { console . warn ( key + "' parameter is undefined." ) ; continue ; } const currentValue = ( this . parameters as any ) [ key ] ; if ( currentValue === undefined ) { console . warn ( key + "' is not a property of this material." ) ; continue ; } ( this . parameters as any ) [ key ] = newValue ; } } } public update ( imageData ) { if ( this . arController === null ) { return false ; } this . markers . pattern . forEach ( m => ( m . markerObject . visible = false ) ) ; this . markers . barcode . forEach ( m => ( m . markerObject . visible = false ) ) ; this . arController . process ( imageData ) ; this . checkForLostMarkers ( ) ; return true ; } public trackMarker ( marker ) { if ( marker instanceof PatternMarker ) { this . trackPatternMarker ( marker ) ; } else if ( marker instanceof BarcodeMarker ) { this . trackBarcode ( marker ) ; } } public dispose ( ) { if ( this . arController ) { this . arController . dispose ( ) ; this . arController = null ; this . disposed = true ; this . markers = { pattern : [ ] , barcode : [ ] } ; } } private initialize ( ) { return new Promise ( ( resolve , reject ) => { this . parameters . source . initialize ( ) . then ( ( ) => { this . _initArtoolkit ( ( ) => { const { camera } = this . parameters . source ; if ( camera !== null ) { camera . projectionMatrix . copy ( this . getProjectionMatrix ( ) ) ; } else { throw Error ( "Camera is not defined" ) ; } this . dispatchEvent ( { type : "initialized" } ) ; resolve ( this ) ; } ) ; } ) . catch ( error => { reject ( error ) ; } ) ; } ) ; } private _initArtoolkit ( onCompleted ) { this . _artoolkitProjectionAxisTransformMatrix = new THREE . Matrix4 ( ) ; this . _artoolkitProjectionAxisTransformMatrix . multiply ( new THREE . Matrix4 ( ) . makeRotationY ( Math . PI ) ) ; this . _artoolkitProjectionAxisTransformMatrix . multiply ( new THREE . Matrix4 ( ) . makeRotationZ ( Math . PI ) ) ; const cameraParameters = new ARToolKitCameraParam ( this . parameters . cameraParametersUrl , ( ) => { this . arController = new ARToolKitController ( this . parameters . width , this . parameters . height , cameraParameters ) ; const detectionModes = { color : ARToolKit . AR_TEMPLATE_MATCHING_COLOR , color_and_matrix : ARToolKit . AR_TEMPLATE_MATCHING_COLOR_AND_MATRIX , mono : ARToolKit . AR_TEMPLATE_MATCHING_MONO , mono_and_matrix : ARToolKit . AR_TEMPLATE_MATCHING_MONO_AND_MATRIX } ; const detectionMode = detectionModes [ this . parameters . detectionMode ] ; this . arController . setPatternDetectionMode ( detectionMode ) ; const matrixCodeTypes = { "3x3" : ARToolKit . AR_MATRIX_CODE_3x3 , "3x3_HAMMING63" : ARToolKit . AR_MATRIX_CODE_3x3_HAMMING63 , "3x3_PARITY65" : ARToolKit . AR_MATRIX_CODE_3x3_PARITY65 , "4x4" : ARToolKit . AR_MATRIX_CODE_4x4 , "4x4_BCH_13_9_3" : ARToolKit . AR_MATRIX_CODE_4x4_BCH_13_9_3 , "4x4_BCH_13_5_5" : ARToolKit . AR_MATRIX_CODE_4x4_BCH_13_5_5 } ; const matrixCodeType = matrixCodeTypes [ this . parameters . matrixCodeType ] ; this . arController . setMatrixCodeType ( matrixCodeType ) ; this . arController . setPattRatio ( this . parameters . patternRatio ) ; this . arController . setLogLevel ( 1 ) ; this . arController . addEventListener ( "getMarker" , ( event ) => { this . handleMarkerDetection ( event ) ; } ) ; onCompleted ( ) ; } , ( err ) => { throw err ; } ) ; return this ; } private handleMarkerDetection ( event ) { if ( event . data . type === ARToolKit . BARCODE_MARKER ) { this . markers . barcode . forEach ( barcodeMarker => { if ( event . data . marker . idMatrix === barcodeMarker . barcodeValue ) { this . onMarkerFound ( event , barcodeMarker ) ; } } ) ; } else if ( event . data . type === ARToolKit . PATTERN_MARKER ) { this . markers . pattern . forEach ( patternMarker => { if ( event . data . marker . idPatt === patternMarker . id ) { this . onMarkerFound ( event , patternMarker ) ; } } ) ; } } private checkForLostMarkers ( ) { [ ... this . markers . pattern , ... this . markers . barcode ] . forEach ( marker => { if ( marker . lastDetected && marker . found && new Date ( ) . getTime ( ) - marker . lastDetected . getTime ( ) > this . parameters . lostTimeout ) { this . onMarkerLost ( marker ) ; } } ) ; } private getProjectionMatrix ( ) { const controller = this . arController as ARToolKitController ; const projectionMatrixArr = Array . from ( controller . getCameraMatrix ( ) ) ; const projectionMatrix = new THREE . Matrix4 ( ) . fromArray ( projectionMatrixArr ) ; projectionMatrix . multiply ( this . _artoolkitProjectionAxisTransformMatrix ) ; const near = 0.1 ; const far = 1000 ; projectionMatrix . elements [ 10 ] = - ( far + near ) / ( far - near ) ; projectionMatrix . elements [ 14 ] = - ( 2 * far * near ) / ( far - near ) ; return projectionMatrix ; } private trackPatternMarker ( marker ) { if ( this . arController === null ) { return ; } this . markers . pattern . push ( marker ) ; const onSuccess = ( markerId ) => { marker . id = markerId ; ( this . arController as any ) . trackPatternMarkerId ( markerId , marker . size ) ; } ; const onError = ( err ) => { throw Error ( err ) ; } ; if ( marker . patternUrl ) { this . arController . loadMarker ( marker . patternUrl , onSuccess , onError ) ; } else { throw Error ( "No patternUrl defined in parameters" ) ; } } private trackBarcode ( marker ) { if ( this . arController === null ) { return ; } this . markers . barcode . push ( marker ) ; let barcodeMarkerId = null ; if ( marker . barcodeValue !== undefined ) { barcodeMarkerId = marker . barcodeValue ; marker . id = barcodeMarkerId ; this . arController . trackBarcodeMarkerId ( barcodeMarkerId , marker . size ) ; } else { throw Error ( "No barcodeValue defined in parameters" ) ; } } private onMarkerFound ( event , marker ) { if ( event . data . type === ARToolKit . PATTERN_MARKER && event . data . marker . cfPatt < marker . minConfidence ) { return ; } if ( event . data . type === ARToolKit . BARCODE_MARKER && event . data . marker . cfMatt < marker . minConfidence ) { return ; } marker . found = true ; marker . lastDetected = new Date ( ) ; const modelViewMatrix = new THREE . Matrix4 ( ) . fromArray ( event . data . matrix ) ; this . updateWithModelViewMatrix ( modelViewMatrix , marker . markerObject ) ; this . dispatchEvent ( { type : "markerFound" , marker } ) ; } private onMarkerLost ( marker ) { marker . found = false ; this . dispatchEvent ( { type : "markerLost" , marker } ) ; } /**
     * When you actually got a new modelViewMatrix, you need to perfom a whole bunch
     * of things. it is done here.
     */ private updateWithModelViewMatrix ( modelViewMatrix , markerObject ) { markerObject . visible = true ; const transformMatrix = this . _artoolkitProjectionAxisTransformMatrix ; const tmpMatrix = new THREE . Matrix4 ( ) . copy ( transformMatrix ) ; tmpMatrix . multiply ( modelViewMatrix ) ; modelViewMatrix . copy ( tmpMatrix ) ; let renderRequired = false ; const markerAxisTransformMatrix = new THREE . Matrix4 ( ) . makeRotationX ( Math . PI / 2 ) ; modelViewMatrix . multiply ( markerAxisTransformMatrix ) ; if ( this . parameters . changeMatrixMode === "modelViewMatrix" ) { if ( this . parameters . positioning . smooth ) { let averages = [ ] ; let exceedsAverageTolerance = 0 ; this . smoothMatrices . push ( modelViewMatrix . elements . slice ( ) ) ; if ( this . smoothMatrices . length < this . parameters . positioning . smoothCount + 1 ) { markerObject . matrix . copy ( modelViewMatrix ) ; } else { this . smoothMatrices . shift ( ) ; averages = [ ] ; for ( let i = 0 ; i < modelViewMatrix . elements . length ; i ++ ) { let sum = 0 ; for ( let j = 0 ; j < this . smoothMatrices . length ; j ++ ) { sum += this . smoothMatrices [ j ] [ i ] ; } averages [ i ] = sum / this . parameters . positioning . smoothCount ; const vary = Math . abs ( averages [ i ] - modelViewMatrix . elements [ i ] ) ; if ( vary >= this . parameters . positioning . smoothTolerance ) { exceedsAverageTolerance ++ ; } } if ( exceedsAverageTolerance >= this . parameters . positioning . smoothThreshold ) { for ( let i = 0 ; i < modelViewMatrix . elements . length ; i ++ ) { modelViewMatrix . elements [ i ] = averages [ i ] ; } markerObject . matrix . copy ( modelViewMatrix ) ; renderRequired = true ; } } } else { markerObject . matrix . copy ( modelViewMatrix ) ; } } else if ( this . parameters . changeMatrixMode === "cameraTransformMatrix" ) { markerObject . matrix . getInverse ( modelViewMatrix ) ; } else { throw Error ( ) ; } markerObject . matrix . decompose ( markerObject . position , markerObject . quaternion , markerObject . scale ) ; return renderRequired ; } } export default Controller ;