;(function() { 'use strict' /** The ARController is the main object for doing AR marker detection with JSARToolKit. To use an ARController, you need to tell it the dimensions to use for the AR processing canvas and pass it an ARCameraParam to define the camera parameters to use when processing images. The ARCameraParam defines the lens distortion and aspect ratio of the camera used. See https://www.artoolworks.com/support/library/Calibrating_your_camera for more information about AR camera parameteters and how to make and use them. If you pass an image as the first argument, the ARController uses that as the image to process, using the dimensions of the image as AR processing canvas width and height. If the first argument to ARController is an image, the second argument is used as the camera param. The camera parameters argument can be either an ARCameraParam or an URL to a camera definition file. If the camera argument is an URL, it is loaded into a new ARCameraParam, and the ARController dispatches a 'load' event and calls the onload method if it is defined. @exports ARController @constructor @param {number} width The width of the images to process. @param {number} height The height of the images to process. @param {ARCameraParam | string} camera The ARCameraParam to use for image processing. If this is a string, the ARController treats it as an URL and tries to load it as a ARCameraParam definition file, calling ARController#onload on success. */ var ARController = function(width, height, camera) { var id; var w = width, h = height; this.orientation = 'landscape'; this.listeners = {}; if (typeof width !== 'number') { var image = width; camera = height; w = image.videoWidth || image.width; h = image.videoHeight || image.height; this.image = image; } this.defaultMarkerWidth = 1; this.patternMarkers = {}; this.barcodeMarkers = {}; this.transform_mat = new Float32Array(16); this.canvas = document.createElement('canvas'); this.canvas.width = w; this.canvas.height = h; this.ctx = this.canvas.getContext('2d'); this.videoWidth = w; this.videoHeight = h; if (typeof camera === 'string') { var self = this; this.cameraParam = new ARCameraParam(camera, function() { self._initialize(); }, function(err) { console.error("ARController: Failed to load ARCameraParam", err); }); } else { this.cameraParam = camera; this._initialize(); } }; /** Destroys the ARController instance and frees all associated resources. After calling dispose, the ARController can't be used any longer. Make a new one if you need one. Calling this avoids leaking Emscripten memory, which may be important if you're using multiple ARControllers. */ ARController.prototype.dispose = function() { artoolkit.teardown(this.id); for (var t in this) { this[t] = null; } }; /** Detects markers in the given image. The process method dispatches marker detection events during its run. The marker detection process proceeds by first dispatching a markerNum event that tells you how many markers were found in the image. Next, a getMarker event is dispatched for each found marker square. Finally, getMultiMarker is dispatched for every found multimarker, followed by getMultiMarkerSub events dispatched for each of the markers in the multimarker. arController.addEventListener('markerNum', function(ev) { console.log("Detected " + ev.data + " markers.") }); arController.addEventListener('getMarker', function(ev) { console.log("Detected marker with ids:", ev.data.marker.id, ev.data.marker.idPatt, ev.data.marker.idMatrix); console.log("Marker data", ev.data.marker); console.log("Marker transform matrix:", [].join.call(ev.data.matrix, ', ')); }); arController.addEventListener('getMultiMarker', function(ev) { console.log("Detected multimarker with id:", ev.data.multiMarkerId); }); arController.addEventListener('getMultiMarkerSub', function(ev) { console.log("Submarker for " + ev.data.multiMarkerId, ev.data.markerIndex, ev.data.marker); }); arController.process(image); If no image is given, defaults to this.image. If the debugSetup has been called, draws debug markers on the debug canvas. @param {ImageElement | VideoElement} image The image to process [optional]. */ ARController.prototype.process = function(image) { this.detectMarker(image); var markerNum = this.getMarkerNum(); var k,o; for (k in this.patternMarkers) { o = this.patternMarkers[k] o.inPrevious = o.inCurrent; o.inCurrent = false; } for (k in this.barcodeMarkers) { o = this.barcodeMarkers[k] o.inPrevious = o.inCurrent; o.inCurrent = false; } for (var i=0; i -1 && (markerInfo.id === markerInfo.idPatt || markerInfo.idMatrix === -1)) { visible = this.trackPatternMarkerId(markerInfo.idPatt); markerType = artoolkit.PATTERN_MARKER; if (markerInfo.dir !== markerInfo.dirPatt) { this.setMarkerInfoDir(i, markerInfo.dirPatt); } } else if (markerInfo.idMatrix > -1) { visible = this.trackBarcodeMarkerId(markerInfo.idMatrix); markerType = artoolkit.BARCODE_MARKER; if (markerInfo.dir !== markerInfo.dirMatrix) { this.setMarkerInfoDir(i, markerInfo.dirMatrix); } } if (markerType !== artoolkit.UNKNOWN_MARKER && visible.inPrevious) { this.getTransMatSquareCont(i, visible.markerWidth, visible.matrix, visible.matrix); } else { this.getTransMatSquare(i, visible.markerWidth, visible.matrix); } // this.getTransMatSquare(i, visible.markerWidth, visible.matrix); visible.inCurrent = true; this.transMatToGLMat(visible.matrix, this.transform_mat); this.dispatchEvent({ name: 'getMarker', target: this, data: { index: i, type: markerType, marker: markerInfo, matrix: this.transform_mat } }); } var multiMarkerCount = this.getMultiMarkerCount(); for (var i=0; i= 0) { visible = true; this.dispatchEvent({ name: 'getMultiMarker', target: this, data: { multiMarkerId: i, matrix: this.transform_mat } }); break; } } if (visible) { for (var j=0; j -1) { this.listeners[name].splice(index, 1); } } }; /** Dispatches the given event to all registered listeners on event.name. @param {Object} event Event to dispatch. */ ARController.prototype.dispatchEvent = function(event) { var listeners = this.listeners[event.name]; if (listeners) { for (var i=0; i= 0) if marker is valid, or -1 if invalid. @field idPatt If pattern detection mode includes a pattern mode, will be marker ID (>= 0) if marker is valid, or -1 if invalid. @field idMatrix If pattern detection mode includes a matrix mode, will be marker ID (>= 0) if marker is valid, or -1 if invalid. @field dir If pattern detection mode is either pattern mode OR matrix but not both, and id != -1, will be marker direction (range 0 to 3, inclusive). @field dirPatt If pattern detection mode includes a pattern mode, and id != -1, will be marker direction (range 0 to 3, inclusive). @field dirMatrix If pattern detection mode includes a matrix mode, and id != -1, will be marker direction (range 0 to 3, inclusive). @field cf If pattern detection mode is either pattern mode OR matrix but not both, will be marker matching confidence (range 0.0 to 1.0 inclusive) if marker is valid, or -1.0 if marker is invalid. @field cfPatt If pattern detection mode includes a pattern mode, will be marker matching confidence (range 0.0 to 1.0 inclusive) if marker is valid, or -1.0 if marker is invalid. @field cfMatrix If pattern detection mode includes a matrix mode, will be marker matching confidence (range 0.0 to 1.0 inclusive) if marker is valid, or -1.0 if marker is invalid. @field pos 2D position (in camera image coordinates, origin at top-left) of the centre of the marker. @field line Line equations for the 4 sides of the marker. @field vertex 2D positions (in camera image coordinates, origin at top-left) of the corners of the marker. vertex[(4 - dir)%4][] is the top-left corner of the marker. Other vertices proceed clockwise from this. These are idealised coordinates (i.e. the onscreen position aligns correctly with the undistorted camera image.) @param {number} markerIndex The index of the marker to query. @returns {Object} The markerInfo struct. */ ARController.prototype.getMarker = function(markerIndex) { if (0 === artoolkit.getMarker(this.id, markerIndex)) { return artoolkit.markerInfo; } }; /** Set marker vertices to the given vertexData[4][2] array. Sets the marker pos to the center of the vertices. Useful for building custom markers for getTransMatSquare. A markerIndex of -1 is used to access the global custom marker. @param {number} markerIndex The index of the marker to edit. */ ARController.prototype.setMarkerInfoVertex = function(markerIndex, vertexData) { for (var i=0; i -1) { // Or a string with the camera param writeStringToFS(filename, url, writeCallback); } else { ajax(url, filename, writeCallback); } } // transfer image function writeStringToFS(target, string, callback) { var byteArray = new Uint8Array(string.length); for (var i=0; i