diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..d857d301 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,80 @@ +{ + "settings" : { + "ecmascript": 6, + "jsx": true + }, + "env": { + "browser": true, + "node": true + }, + "ecmaFeatures": { + "blockBindings": true, + "forOf": true, + "blockBindings": true, + "defaultParams": true, + "globalReturn": false, + "modules": true, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "templateStrings": true, + "spread": true, + "jsx": true, + "arrowFunctions": true, + "classes": true, + "destructuring": true, + "objectLiteralComputedProperties": true + }, + "globals": {}, + "rules": { + "no-unused-expressions": 1, + "no-extra-boolean-cast": 1, + "no-multi-spaces": 2, + "no-underscore-dangle": 0, + "comma-dangle": 2, + "camelcase": 0, + "curly": 2, + "eqeqeq": 2, + "guard-for-in": 2, + "wrap-iife": 0, + "no-use-before-define": [1, "nofunc"], + "new-cap": 2, + "quotes": 0, + "strict": 0, + "no-caller": 2, + "no-empty": 1, + "no-new": 2, + "no-plusplus": 0, + "no-unused-vars": 1, + "no-trailing-spaces": 2, + + // STYLE + "max-params": [2, 7], + "key-spacing": [1, { + beforeColon: false, + afterColon: true + }], + "indent": [2, 4], + "brace-style": [2, "1tbs"], + "comma-spacing": [2, {before: false, after: true}], + "comma-style": [2, "last"], + "consistent-this": [1, "self"], + "eol-last": 0, + "new-cap": 0, + "new-parens": 2, + "no-array-constructor": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multiple-empty-lines": 2, + "semi-spacing": 2, + "dot-notation": 2, + "no-spaced-func": 1, + "no-shadow": 2, + "no-undef": 2, + "padded-blocks": [2, "never"], + "semi": [2, "always"], + "space-after-keywords": [2, "always"], + "space-infix-ops": 2, + "max-len" : [1, 120], + "consistent-return": 2, + "yoda": 2 + } +} diff --git a/Gruntfile.js b/Gruntfile.js index f7b2e200..2a7f61b1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,8 +1,7 @@ module.exports = function(grunt) { - // Project configuration. grunt.initConfig({ - pkg : grunt.file.readJSON('package.json'), + pkg: grunt.file.readJSON('package.json'), karma: { unit: { configFile: 'karma.conf.js' @@ -10,67 +9,14 @@ module.exports = function(grunt) { integration: { configFile: 'karma-integration.conf.js' } - }, - uglify : { - options : { - banner : '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n', - preserveComments: 'some' - }, - build : { - src : 'dist/<%= pkg.name %>.js', - dest : 'dist/<%= pkg.name %>.min.js' - } - }, - jshint : { - all : ['Gruntfile.js', 'src/*.js'] - }, - requirejs : { - compile : { - options : { - almond : true, - wrap : { - startFile : 'build/start.frag', - endFile : 'build/end.frag' - }, - "baseUrl" : "src", - "name" : "quagga", - "useStrict": true, - "out" : "dist/quagga.js", - "include" : ['quagga'], - "optimize" : "none", - "findNestedDependencies" : true, - "skipSemiColonInsertion" : true, - - "shim" : { - "typedefs" : { - "deps" : [], - "exports" : "typedefs" - } - }, - - "paths" : { - "typedefs" : "typedefs", - "gl-matrix": "../node_modules/gl-matrix/dist/gl-matrix-min" - } - } - } } }); - // Load the plugin that provides the "uglify" task. - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-requirejs'); grunt.loadNpmTasks('grunt-karma'); grunt.loadTasks('tasks'); - grunt.registerTask('build', ['check', 'requirejs']); - grunt.registerTask('check', ['jshint']); - grunt.registerTask('dist', ['build', 'uglify', 'uglyasm']); grunt.registerTask('test', ['karma']); grunt.registerTask('integrationtest', ['karma:integration']); - - grunt.registerTask('default', ['build']); -}; +}; diff --git a/README.md b/README.md index 445d6cd7..62bd703d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ quaggaJS ======== -- [Changelog](#changelog) (2015-09-15) +- [Changelog](#changelog) (2015-10-13) ## What is QuaggaJS? @@ -34,9 +34,9 @@ be aligned with the viewport. In order to take full advantage of quaggaJS, the browser needs to support the `getUserMedia` API which is already implemented in recent versions of Firefox, -Chrome, IE (Edge) and Opera. The API is also available on their mobile +Chrome, IE (Edge) and Opera. The API is also available on their mobile counterparts installed on Android (except IE). Safari does not allow the access -to the camera yet, neither on desktop, nor on mobile. You can check +to the camera yet, neither on desktop, nor on mobile. You can check [caniuse][caniuse_getusermedia] for updates. In cases where real-time decoding is not needed, or the platform does not @@ -90,11 +90,15 @@ You can build the library yourself by simply cloning the repo and typing: ```console > npm install -> grunt dist +> npm run build ``` -This grunt task builds a non optimized version `quagga.js` and a minified +This npm script builds a non optimized version `quagga.js` and a minified version `quagga.min.js` and places both files in the `dist` folder. +Additionally, a `quagga.map` source-map is placed alongside these files. This +file is only valid for the non-uglified version `quagga.js` because the +minified version is altered after compression and does not align with the map +file any more. ## API @@ -360,7 +364,7 @@ automatically generated in the coverage/ folder. ```console > npm install -> grunt test +> npm run test ``` ## Image Debugging @@ -430,6 +434,13 @@ on the ``singleChannel`` flag in the configuration when using ``decodeSingle``. ## Changelog +### 2015-10-13 +Take a look at the release-notes ([0.8.0] +(https://github.com/serratus/quaggaJS/releases/tag/v0.8.0)) + +- Improvements + - Replaced RequireJS with webpack + ### 2015-09-15 Take a look at the release-notes ([0.7.0] (https://github.com/serratus/quaggaJS/releases/tag/v0.7.0)) diff --git a/dist/quagga.js b/dist/quagga.js index dce89a34..09d702ff 100644 --- a/dist/quagga.js +++ b/dist/quagga.js @@ -1,7030 +1,12949 @@ -(function (root, factory) { - var factorySource = factory.toString(); - - if (typeof module !== 'undefined') { - module.exports = factory(factorySource); - } else { - //Browser globals case. Just assign the - //result to a property on the global. - root.Quagga = factory(factorySource); - } -}(this, function (__factorySource__) {/** - * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/almond for details - */ -//Going sloppy to avoid 'use strict' string cost, but strict practices should -//be followed. -/*jslint sloppy: true */ -/*global setTimeout: false */ - -var requirejs, require, define; -(function (undef) { - var main, req, makeMap, handlers, - defined = {}, - waiting = {}, - config = {}, - defining = {}, - hasOwn = Object.prototype.hasOwnProperty, - aps = [].slice, - jsSuffixRegExp = /\.js$/; - - function hasProp(obj, prop) { - return hasOwn.call(obj, prop); - } - - /** - * Given a relative module name, like ./something, normalize it to - * a real name that can be mapped to a path. - * @param {String} name the relative name - * @param {String} baseName a real name that the name arg is relative - * to. - * @returns {String} normalized name - */ - function normalize(name, baseName) { - var nameParts, nameSegment, mapValue, foundMap, lastIndex, - foundI, foundStarMap, starI, i, j, part, - baseParts = baseName && baseName.split("/"), - map = config.map, - starMap = (map && map['*']) || {}; - - //Adjust any relative paths. - if (name && name.charAt(0) === ".") { - //If have a base name, try to normalize against it, - //otherwise, assume it is a top-level require that will - //be relative to baseUrl in the end. - if (baseName) { - //Convert baseName to array, and lop off the last part, - //so that . matches that "directory" and not name of the baseName's - //module. For instance, baseName of "one/two/three", maps to - //"one/two/three.js", but we want the directory, "one/two" for - //this normalization. - baseParts = baseParts.slice(0, baseParts.length - 1); - name = name.split('/'); - lastIndex = name.length - 1; - - // Node .js allowance: - if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { - name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); - } - - name = baseParts.concat(name); - - //start trimDots - for (i = 0; i < name.length; i += 1) { - part = name[i]; - if (part === ".") { - name.splice(i, 1); - i -= 1; - } else if (part === "..") { - if (i === 1 && (name[2] === '..' || name[0] === '..')) { - //End of the line. Keep at least one non-dot - //path segment at the front so it can be mapped - //correctly to disk. Otherwise, there is likely - //no path mapping for a path starting with '..'. - //This can still fail, but catches the most reasonable - //uses of .. - break; - } else if (i > 0) { - name.splice(i - 1, 2); - i -= 2; - } - } - } - //end trimDots - - name = name.join("/"); - } else if (name.indexOf('./') === 0) { - // No baseName, so this is ID is resolved relative - // to baseUrl, pull off the leading dot. - name = name.substring(2); - } - } - - //Apply map config if available. - if ((baseParts || starMap) && map) { - nameParts = name.split('/'); - - for (i = nameParts.length; i > 0; i -= 1) { - nameSegment = nameParts.slice(0, i).join("/"); - - if (baseParts) { - //Find the longest baseName segment match in the config. - //So, do joins on the biggest to smallest lengths of baseParts. - for (j = baseParts.length; j > 0; j -= 1) { - mapValue = map[baseParts.slice(0, j).join('/')]; - - //baseName segment has config, find if it has one for - //this name. - if (mapValue) { - mapValue = mapValue[nameSegment]; - if (mapValue) { - //Match, update name to the new value. - foundMap = mapValue; - foundI = i; - break; - } - } - } - } - - if (foundMap) { - break; - } - - //Check for a star map match, but just hold on to it, - //if there is a shorter segment match later in a matching - //config, then favor over this star map. - if (!foundStarMap && starMap && starMap[nameSegment]) { - foundStarMap = starMap[nameSegment]; - starI = i; - } - } - - if (!foundMap && foundStarMap) { - foundMap = foundStarMap; - foundI = starI; - } - - if (foundMap) { - nameParts.splice(0, foundI, foundMap); - name = nameParts.join('/'); - } - } - - return name; - } - - function makeRequire(relName, forceSync) { - return function () { - //A version of a require function that passes a moduleName - //value for items that may need to - //look up paths relative to the moduleName - return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync])); - }; - } - - function makeNormalize(relName) { - return function (name) { - return normalize(name, relName); - }; - } - - function makeLoad(depName) { - return function (value) { - defined[depName] = value; - }; - } - - function callDep(name) { - if (hasProp(waiting, name)) { - var args = waiting[name]; - delete waiting[name]; - defining[name] = true; - main.apply(undef, args); - } - - if (!hasProp(defined, name) && !hasProp(defining, name)) { - throw new Error('No ' + name); - } - return defined[name]; - } - - //Turns a plugin!resource to [plugin, resource] - //with the plugin being undefined if the name - //did not have a plugin prefix. - function splitPrefix(name) { - var prefix, - index = name ? name.indexOf('!') : -1; - if (index > -1) { - prefix = name.substring(0, index); - name = name.substring(index + 1, name.length); - } - return [prefix, name]; - } - - /** - * Makes a name map, normalizing the name, and using a plugin - * for normalization if necessary. Grabs a ref to plugin - * too, as an optimization. - */ - makeMap = function (name, relName) { - var plugin, - parts = splitPrefix(name), - prefix = parts[0]; - - name = parts[1]; - - if (prefix) { - prefix = normalize(prefix, relName); - plugin = callDep(prefix); - } - - //Normalize according - if (prefix) { - if (plugin && plugin.normalize) { - name = plugin.normalize(name, makeNormalize(relName)); - } else { - name = normalize(name, relName); - } - } else { - name = normalize(name, relName); - parts = splitPrefix(name); - prefix = parts[0]; - name = parts[1]; - if (prefix) { - plugin = callDep(prefix); - } - } - - //Using ridiculous property names for space reasons - return { - f: prefix ? prefix + '!' + name : name, //fullName - n: name, - pr: prefix, - p: plugin - }; - }; - - function makeConfig(name) { - return function () { - return (config && config.config && config.config[name]) || {}; - }; - } - - handlers = { - require: function (name) { - return makeRequire(name); - }, - exports: function (name) { - var e = defined[name]; - if (typeof e !== 'undefined') { - return e; - } else { - return (defined[name] = {}); - } - }, - module: function (name) { - return { - id: name, - uri: '', - exports: defined[name], - config: makeConfig(name) - }; - } - }; - - main = function (name, deps, callback, relName) { - var cjsModule, depName, ret, map, i, - args = [], - callbackType = typeof callback, - usingExports; - - //Use name if no relName - relName = relName || name; - - //Call the callback to define the module, if necessary. - if (callbackType === 'undefined' || callbackType === 'function') { - //Pull out the defined dependencies and pass the ordered - //values to the callback. - //Default to [require, exports, module] if no deps - deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; - for (i = 0; i < deps.length; i += 1) { - map = makeMap(deps[i], relName); - depName = map.f; - - //Fast path CommonJS standard dependencies. - if (depName === "require") { - args[i] = handlers.require(name); - } else if (depName === "exports") { - //CommonJS module spec 1.1 - args[i] = handlers.exports(name); - usingExports = true; - } else if (depName === "module") { - //CommonJS module spec 1.1 - cjsModule = args[i] = handlers.module(name); - } else if (hasProp(defined, depName) || - hasProp(waiting, depName) || - hasProp(defining, depName)) { - args[i] = callDep(depName); - } else if (map.p) { - map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); - args[i] = defined[depName]; - } else { - throw new Error(name + ' missing ' + depName); - } - } - - ret = callback ? callback.apply(defined[name], args) : undefined; - - if (name) { - //If setting exports via "module" is in play, - //favor that over return value and exports. After that, - //favor a non-undefined return value over exports use. - if (cjsModule && cjsModule.exports !== undef && - cjsModule.exports !== defined[name]) { - defined[name] = cjsModule.exports; - } else if (ret !== undef || !usingExports) { - //Use the return value from the function. - defined[name] = ret; - } - } - } else if (name) { - //May just be an object definition for the module. Only - //worry about defining if have a module name. - defined[name] = callback; - } - }; - - requirejs = require = req = function (deps, callback, relName, forceSync, alt) { - if (typeof deps === "string") { - if (handlers[deps]) { - //callback in this case is really relName - return handlers[deps](callback); - } - //Just return the module wanted. In this scenario, the - //deps arg is the module name, and second arg (if passed) - //is just the relName. - //Normalize module name, if it contains . or .. - return callDep(makeMap(deps, callback).f); - } else if (!deps.splice) { - //deps is a config object, not an array. - config = deps; - if (config.deps) { - req(config.deps, config.callback); - } - if (!callback) { - return; - } - - if (callback.splice) { - //callback is an array, which means it is a dependency list. - //Adjust args if there are dependencies - deps = callback; - callback = relName; - relName = null; - } else { - deps = undef; - } - } - - //Support require(['a']) - callback = callback || function () {}; - - //If relName is a function, it is an errback handler, - //so remove it. - if (typeof relName === 'function') { - relName = forceSync; - forceSync = alt; - } - - //Simulate async callback; - if (forceSync) { - main(undef, deps, callback, relName); - } else { - //Using a non-zero value because of concern for what old browsers - //do, and latest browsers "upgrade" to 4 if lower value is used: - //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: - //If want a value immediately, use require('id') instead -- something - //that works in almond on the global level, but not guaranteed and - //unlikely to work in other AMD implementations. - setTimeout(function () { - main(undef, deps, callback, relName); - }, 4); - } - - return req; - }; - - /** - * Just drops the config on the floor, but returns req in case - * the config return value is used. - */ - req.config = function (cfg) { - return req(cfg); - }; - - /** - * Expose module registry for debugging and tooling - */ - requirejs._defined = defined; - - define = function (name, deps, callback) { - - //This module may not have dependencies - if (!deps.splice) { - //deps is not an array, so probably means - //an object literal or factory function for - //the value. Adjust args. - callback = deps; - deps = []; - } - - if (!hasProp(defined, name) && !hasProp(waiting, name)) { - waiting[name] = [name, deps, callback]; - } - }; - - define.amd = { - jQuery: true - }; -}()); - -define("almond", function(){}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('image_loader',[],function() { - "use strict"; - - var ImageLoader = {}; - ImageLoader.load = function(directory, callback, offset, size, sequence) { - var htmlImagesSrcArray = new Array(size), - htmlImagesArray = new Array(htmlImagesSrcArray.length), - i, - img, - num; - - if (sequence === false) { - htmlImagesSrcArray[0] = directory; - } else { - for ( i = 0; i < htmlImagesSrcArray.length; i++) { - num = (offset + i); - htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg"; - } - } - htmlImagesArray.notLoaded = []; - htmlImagesArray.addImage = function(img) { - htmlImagesArray.notLoaded.push(img); - }; - htmlImagesArray.loaded = function(loadedImg) { - var notloadedImgs = htmlImagesArray.notLoaded; - for (var x = 0; x < notloadedImgs.length; x++) { - if (notloadedImgs[x] == loadedImg) { - notloadedImgs.splice(x, 1); - for (var y = 0; y < htmlImagesSrcArray.length; y++) { - var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/")); - if (loadedImg.src.lastIndexOf(imgName) != -1) { - htmlImagesArray[y] = loadedImg; - break; - } - } - break; - } - } - if (notloadedImgs.length === 0) { - console.log("Images loaded"); - callback.apply(null, [htmlImagesArray]); - } - }; - - for ( i = 0; i < htmlImagesSrcArray.length; i++) { - img = new Image(); - htmlImagesArray.addImage(img); - addOnloadHandler(img, htmlImagesArray); - img.src = htmlImagesSrcArray[i]; - } - }; - - function addOnloadHandler(img, htmlImagesArray) { - img.onload = function() { - htmlImagesArray.loaded(this); - }; - } - - return (ImageLoader); +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(factory.toString()); + else if(typeof exports === 'object') + exports["Quagga"] = factory(factory.toString()); + else + root["Quagga"] = factory(factory.toString()); +})(this, function(__factorySource__) { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; +/******/ +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/"; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(1); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _typedefs = __webpack_require__(2); + + var _typedefs2 = _interopRequireDefault(_typedefs); + + // eslint-disable-line no-unused-vars + + var _image_wrapper = __webpack_require__(3); + + var _image_wrapper2 = _interopRequireDefault(_image_wrapper); + + var _barcode_locator = __webpack_require__(18); + + var _barcode_locator2 = _interopRequireDefault(_barcode_locator); + + var _barcode_decoder = __webpack_require__(23); + + var _barcode_decoder2 = _interopRequireDefault(_barcode_decoder); + + var _config2 = __webpack_require__(68); + + var _config3 = _interopRequireDefault(_config2); + + var _events = __webpack_require__(69); + + var _events2 = _interopRequireDefault(_events); + + var _camera_access = __webpack_require__(70); + + var _camera_access2 = _interopRequireDefault(_camera_access); + + var _image_debug = __webpack_require__(22); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + var _glMatrix = __webpack_require__(7); + + var _result_collector = __webpack_require__(71); + + var _result_collector2 = _interopRequireDefault(_result_collector); + + var merge = __webpack_require__(35); + var InputStream = __webpack_require__(72); + var FrameGrabber = __webpack_require__(74); + + var _inputStream, + _framegrabber, + _stopped, + _canvasContainer = { + ctx: { + image: null, + overlay: null + }, + dom: { + image: null, + overlay: null + } + }, + _inputImageWrapper, + _boxSize, + _decoder, + _workerPool = [], + _onUIThread = true, + _resultCollector, + _config = {}; + + function initializeData(imageWrapper) { + initBuffers(imageWrapper); + _decoder = _barcode_decoder2['default'].create(_config.decoder, _inputImageWrapper); + } + + function initConfig() { + if (typeof document !== "undefined") { + var vis = [{ + node: document.querySelector("div[data-controls]"), + prop: _config.controls + }, { + node: _canvasContainer.dom.overlay, + prop: _config.visual.show + }]; + + for (var i = 0; i < vis.length; i++) { + if (vis[i].node) { + if (vis[i].prop === true) { + vis[i].node.style.display = "block"; + } else { + vis[i].node.style.display = "none"; + } + } + } + } + } + + function initInputStream(cb) { + var video; + if (_config.inputStream.type === "VideoStream") { + video = document.createElement("video"); + _inputStream = InputStream.createVideoStream(video); + } else if (_config.inputStream.type === "ImageStream") { + _inputStream = InputStream.createImageStream(); + } else if (_config.inputStream.type === "LiveStream") { + var $viewport = document.querySelector("#interactive.viewport"); + if ($viewport) { + video = $viewport.querySelector("video"); + if (!video) { + video = document.createElement("video"); + $viewport.appendChild(video); + } + } + _inputStream = InputStream.createLiveStream(video); + _camera_access2['default'].request(video, _config.inputStream.constraints, function (err) { + if (!err) { + _inputStream.trigger("canrecord"); + } else { + return cb(err); + } + }); + } + + _inputStream.setAttribute("preload", "auto"); + _inputStream.setAttribute("autoplay", true); + _inputStream.setInputStream(_config.inputStream); + _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); + } + + function canRecord(cb) { + _barcode_locator2['default'].checkImageConstraints(_inputStream, _config.locator); + initCanvas(); + _framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image); + initConfig(); + + if (_config.numOfWorkers > 0) { + initWorkers(function () { + console.log("Workers created"); + ready(cb); + }); + } else { + initializeData(); + ready(cb); + } + } + + function ready(cb) { + _inputStream.play(); + cb(); + } + + function initCanvas() { + if (typeof document !== "undefined") { + var $viewport = document.querySelector("#interactive.viewport"); + _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); + if (!_canvasContainer.dom.image) { + _canvasContainer.dom.image = document.createElement("canvas"); + _canvasContainer.dom.image.className = "imgBuffer"; + if ($viewport && _config.inputStream.type === "ImageStream") { + $viewport.appendChild(_canvasContainer.dom.image); + } + } + _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); + _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; + _canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; + + _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer"); + if (!_canvasContainer.dom.overlay) { + _canvasContainer.dom.overlay = document.createElement("canvas"); + _canvasContainer.dom.overlay.className = "drawingBuffer"; + if ($viewport) { + $viewport.appendChild(_canvasContainer.dom.overlay); + } + var clearFix = document.createElement("br"); + clearFix.setAttribute("clear", "all"); + if ($viewport) { + $viewport.appendChild(clearFix); + } + } + _canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); + _canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; + _canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; + } + } + + function initBuffers(imageWrapper) { + if (imageWrapper) { + _inputImageWrapper = imageWrapper; + } else { + _inputImageWrapper = new _image_wrapper2['default']({ + x: _inputStream.getWidth(), + y: _inputStream.getHeight() + }); + } + + console.log(_inputImageWrapper.size); + _boxSize = [_glMatrix.vec2.clone([0, 0]), _glMatrix.vec2.clone([0, _inputImageWrapper.size.y]), _glMatrix.vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]), _glMatrix.vec2.clone([_inputImageWrapper.size.x, 0])]; + _barcode_locator2['default'].init(_inputImageWrapper, _config.locator); + } + + function getBoundingBoxes() { + if (_config.locate) { + return _barcode_locator2['default'].locate(); + } else { + return [[_glMatrix.vec2.clone(_boxSize[0]), _glMatrix.vec2.clone(_boxSize[1]), _glMatrix.vec2.clone(_boxSize[2]), _glMatrix.vec2.clone(_boxSize[3])]]; + } + } + + function transformResult(result) { + var topRight = _inputStream.getTopRight(), + xOffset = topRight.x, + yOffset = topRight.y, + i; + + if (!result || xOffset === 0 && yOffset === 0) { + return; + } + + if (result.line && result.line.length === 2) { + moveLine(result.line); + } + if (result.boxes && result.boxes.length > 0) { + for (i = 0; i < result.boxes.length; i++) { + moveBox(result.boxes[i]); + } + } + + function moveBox(box) { + var corner = box.length; + + while (corner--) { + box[corner][0] += xOffset; + box[corner][1] += yOffset; + } + } + + function moveLine(line) { + line[0].x += xOffset; + line[0].y += yOffset; + line[1].x += xOffset; + line[1].y += yOffset; + } + } + + function publishResult(result, imageData) { + if (_onUIThread) { + transformResult(result); + if (imageData && result && result.codeResult) { + if (_resultCollector) { + _resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult); + } + } + } + + _events2['default'].publish("processed", result); + if (result && result.codeResult) { + _events2['default'].publish("detected", result); + } + } + + function locateAndDecode() { + var result, boxes; + + boxes = getBoundingBoxes(); + if (boxes) { + result = _decoder.decodeFromBoundingBoxes(boxes); + result = result || {}; + result.boxes = boxes; + publishResult(result, _inputImageWrapper.data); + } else { + publishResult(); + } + } + + function update() { + var availableWorker; + + if (_onUIThread) { + if (_workerPool.length > 0) { + availableWorker = _workerPool.filter(function (workerThread) { + return !workerThread.busy; + })[0]; + if (availableWorker) { + _framegrabber.attachData(availableWorker.imageData); + } else { + return; // all workers are busy + } + } else { + _framegrabber.attachData(_inputImageWrapper.data); + } + if (_framegrabber.grab()) { + if (availableWorker) { + availableWorker.busy = true; + availableWorker.worker.postMessage({ + cmd: 'process', + imageData: availableWorker.imageData + }, [availableWorker.imageData.buffer]); + } else { + locateAndDecode(); + } + } + } else { + locateAndDecode(); + } + } + + function _start() { + _stopped = false; + (function frame() { + if (!_stopped) { + update(); + if (_onUIThread && _config.inputStream.type === "LiveStream") { + window.requestAnimFrame(frame); + } + } + })(); + } + + function initWorkers(cb) { + var i; + _workerPool = []; + + for (i = 0; i < _config.numOfWorkers; i++) { + initWorker(workerInitialized); + } + + function workerInitialized(workerThread) { + _workerPool.push(workerThread); + if (_workerPool.length >= _config.numOfWorkers) { + cb(); + } + } + } + + function initWorker(cb) { + var blobURL, + workerThread = { + worker: undefined, + imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), + busy: true + }; + + blobURL = generateWorkerBlob(); + workerThread.worker = new Worker(blobURL); + + workerThread.worker.onmessage = function (e) { + if (e.data.event === 'initialized') { + URL.revokeObjectURL(blobURL); + workerThread.busy = false; + workerThread.imageData = new Uint8Array(e.data.imageData); + console.log("Worker initialized"); + return cb(workerThread); + } else if (e.data.event === 'processed') { + workerThread.imageData = new Uint8Array(e.data.imageData); + workerThread.busy = false; + publishResult(e.data.result, workerThread.imageData); + } else if (e.data.event === 'error') { + console.log("Worker error: " + e.data.message); + } + }; + + workerThread.worker.postMessage({ + cmd: 'init', + size: { x: _inputStream.getWidth(), y: _inputStream.getHeight() }, + imageData: workerThread.imageData, + config: _config + }, [workerThread.imageData.buffer]); + } + + function workerInterface(factory) { + /* eslint-disable no-undef*/ + if (factory) { + var Quagga = factory(); + if (!Quagga) { + self.postMessage({ 'event': 'error', message: 'Quagga could not be created' }); + return; + } + } + var imageWrapper; + + self.onmessage = function (e) { + if (e.data.cmd === 'init') { + var config = e.data.config; + config.numOfWorkers = 0; + imageWrapper = new Quagga.ImageWrapper({ + x: e.data.size.x, + y: e.data.size.y + }, new Uint8Array(e.data.imageData)); + Quagga.init(config, ready, imageWrapper); + Quagga.onProcessed(onProcessed); + } else if (e.data.cmd === 'process') { + imageWrapper.data = new Uint8Array(e.data.imageData); + Quagga.start(); + } else if (e.data.cmd === 'setReaders') { + Quagga.setReaders(e.data.readers); + } + }; + + function onProcessed(result) { + self.postMessage({ + 'event': 'processed', + imageData: imageWrapper.data, + result: result + }, [imageWrapper.data.buffer]); + } + + function ready() { + // eslint-disable-line + self.postMessage({ 'event': 'initialized', imageData: imageWrapper.data }, [imageWrapper.data.buffer]); + } + + /* eslint-enable */ + } + + function generateWorkerBlob() { + var blob, factorySource; + + /* jshint ignore:start */ + if (typeof __factorySource__ !== 'undefined') { + factorySource = __factorySource__; // eslint-disable-line no-undef + } + /* jshint ignore:end */ + + blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'], { type: 'text/javascript' }); + + return window.URL.createObjectURL(blob); + } + + function _setReaders(readers) { + if (_decoder) { + _decoder.setReaders(readers); + } else if (_onUIThread && _workerPool.length > 0) { + _workerPool.forEach(function (workerThread) { + workerThread.worker.postMessage({ cmd: 'setReaders', readers: readers }); + }); + } + } + + exports['default'] = { + init: function init(config, cb, imageWrapper) { + _config = merge({}, _config3['default'], config); + if (imageWrapper) { + _onUIThread = false; + initializeData(imageWrapper); + return cb(); + } else { + initInputStream(cb); + } + }, + start: function start() { + _start(); + }, + stop: function stop() { + _stopped = true; + _workerPool.forEach(function (workerThread) { + workerThread.worker.terminate(); + console.log("Worker terminated!"); + }); + _workerPool.length = 0; + if (_config.inputStream.type === "LiveStream") { + _camera_access2['default'].release(); + _inputStream.clearEventHandlers(); + } + }, + pause: function pause() { + _stopped = true; + }, + onDetected: function onDetected(callback) { + _events2['default'].subscribe("detected", callback); + }, + offDetected: function offDetected(callback) { + _events2['default'].unsubscribe("detected", callback); + }, + onProcessed: function onProcessed(callback) { + _events2['default'].subscribe("processed", callback); + }, + offProcessed: function offProcessed(callback) { + _events2['default'].unsubscribe("processed", callback); + }, + setReaders: function setReaders(readers) { + _setReaders(readers); + }, + registerResultCollector: function registerResultCollector(resultCollector) { + if (resultCollector && typeof resultCollector.addResult === 'function') { + _resultCollector = resultCollector; + } + }, + canvas: _canvasContainer, + decodeSingle: function decodeSingle(config, resultCallback) { + config = merge({ + inputStream: { + type: "ImageStream", + sequence: false, + size: 800, + src: config.src + }, + numOfWorkers: 1, + locator: { + halfSample: false + } + }, config); + this.init(config, function () { + _events2['default'].once("processed", function (result) { + _stopped = true; + resultCallback.call(null, result); + }, true); + _start(); + }); + }, + ImageWrapper: _image_wrapper2['default'], + ImageDebug: _image_debug2['default'], + ResultCollector: _result_collector2['default'] + }; + module.exports = exports['default']; + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + /* + * typedefs.js + * Normalizes browser-specific prefixes + */ + + 'use strict'; + + if (typeof window !== 'undefined') { + window.requestAnimFrame = (function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function ( /* function FrameRequestCallback */callback) { + window.setTimeout(callback, 1000 / 60); + }; + })(); + + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; + } + Math.imul = Math.imul || function (a, b) { + var ah = a >>> 16 & 0xffff, + al = a & 0xffff, + bh = b >>> 16 & 0xffff, + bl = b & 0xffff; + // the shift by 0 fixes the sign on the high part + // the final |0 converts the unsigned value into a signed value + return al * bl + (ah * bl + al * bh << 16 >>> 0) | 0; + }; + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _subImage = __webpack_require__(4); + + var _subImage2 = _interopRequireDefault(_subImage); + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var _array_helper = __webpack_require__(17); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + var _glMatrix = __webpack_require__(7); + + /** + * Represents a basic image combining the data and size. + * In addition, some methods for manipulation are contained. + * @param size {x,y} The size of the image in pixel + * @param data {Array} If given, a flat array containing the pixel data + * @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed) + * @param initialize {Boolean} Indicating if the array should be initialized on creation. + * @returns {ImageWrapper} + */ + function ImageWrapper(size, data, ArrayType, initialize) { + if (!data) { + if (ArrayType) { + this.data = new ArrayType(size.x * size.y); + if (ArrayType === Array && initialize) { + _array_helper2['default'].init(this.data, 0); + } + } else { + this.data = new Uint8Array(size.x * size.y); + if (Uint8Array === Array && initialize) { + _array_helper2['default'].init(this.data, 0); + } + } + } else { + this.data = data; + } + this.size = size; + } + + /** + * tests if a position is within the image with a given offset + * @param imgRef {x, y} The location to test + * @param border Number the padding value in pixel + * @returns {Boolean} true if location inside the image's border, false otherwise + * @see cvd/image.h + */ + ImageWrapper.prototype.inImageWithBorder = function (imgRef, border) { + return imgRef.x >= border && imgRef.y >= border && imgRef.x < this.size.x - border && imgRef.y < this.size.y - border; + }; + + /** + * Performs bilinear sampling + * @param inImg Image to extract sample from + * @param x the x-coordinate + * @param y the y-coordinate + * @returns the sampled value + * @see cvd/vision.h + */ + ImageWrapper.sample = function (inImg, x, y) { + var lx = Math.floor(x); + var ly = Math.floor(y); + var w = inImg.size.x; + var base = ly * inImg.size.x + lx; + var a = inImg.data[base + 0]; + var b = inImg.data[base + 1]; + var c = inImg.data[base + w]; + var d = inImg.data[base + w + 1]; + var e = a - b; + x -= lx; + y -= ly; + + var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a); + return result; + }; + + /** + * Initializes a given array. Sets each element to zero. + * @param array {Array} The array to initialize + */ + ImageWrapper.clearArray = function (array) { + var l = array.length; + while (l--) { + array[l] = 0; + } + }; + + /** + * Creates a {SubImage} from the current image ({this}). + * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) + * @param size {ImageRef} The size of the resulting image + * @returns {SubImage} A shared part of the original image + */ + ImageWrapper.prototype.subImage = function (from, size) { + return new _subImage2['default'](from, size, this); + }; + + /** + * Creates an {ImageWrapper) and copies the needed underlying image-data area + * @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied + * @param from {ImageRef} The location where to copy from (top-left location) + */ + ImageWrapper.prototype.subImageAsCopy = function (imageWrapper, from) { + var sizeY = imageWrapper.size.y, + sizeX = imageWrapper.size.x; + var x, y; + for (x = 0; x < sizeX; x++) { + for (y = 0; y < sizeY; y++) { + imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x]; + } + } + }; + + ImageWrapper.prototype.copyTo = function (imageWrapper) { + var length = this.data.length, + srcData = this.data, + dstData = imageWrapper.data; + + while (length--) { + dstData[length] = srcData[length]; + } + }; + + /** + * Retrieves a given pixel position from the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ + ImageWrapper.prototype.get = function (x, y) { + return this.data[y * this.size.x + x]; + }; + + /** + * Retrieves a given pixel position from the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ + ImageWrapper.prototype.getSafe = function (x, y) { + var i; + + if (!this.indexMapping) { + this.indexMapping = { + x: [], + y: [] + }; + for (i = 0; i < this.size.x; i++) { + this.indexMapping.x[i] = i; + this.indexMapping.x[i + this.size.x] = i; + } + for (i = 0; i < this.size.y; i++) { + this.indexMapping.y[i] = i; + this.indexMapping.y[i + this.size.y] = i; + } + } + return this.data[this.indexMapping.y[y + this.size.y] * this.size.x + this.indexMapping.x[x + this.size.x]]; + }; + + /** + * Sets a given pixel position in the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @param value {Number} The grayscale value to set + * @returns {ImageWrapper} The Image itself (for possible chaining) + */ + ImageWrapper.prototype.set = function (x, y, value) { + this.data[y * this.size.x + x] = value; + return this; + }; + + /** + * Sets the border of the image (1 pixel) to zero + */ + ImageWrapper.prototype.zeroBorder = function () { + var i, + width = this.size.x, + height = this.size.y, + data = this.data; + for (i = 0; i < width; i++) { + data[i] = data[(height - 1) * width + i] = 0; + } + for (i = 1; i < height - 1; i++) { + data[i * width] = data[i * width + (width - 1)] = 0; + } + }; + + /** + * Inverts a binary image in place + */ + ImageWrapper.prototype.invert = function () { + var data = this.data, + length = data.length; + + while (length--) { + data[length] = data[length] ? 0 : 1; + } + }; + + ImageWrapper.prototype.convolve = function (kernel) { + var x, + y, + kx, + ky, + kSize = kernel.length / 2 | 0, + accu = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + accu = 0; + for (ky = -kSize; ky <= kSize; ky++) { + for (kx = -kSize; kx <= kSize; kx++) { + accu += kernel[ky + kSize][kx + kSize] * this.getSafe(x + kx, y + ky); + } + } + this.data[y * this.size.x + x] = accu; + } + } + }; + + ImageWrapper.prototype.moments = function (labelcount) { + var data = this.data, + x, + y, + height = this.size.y, + width = this.size.x, + val, + ysq, + labelsum = [], + i, + label, + mu11, + mu02, + mu20, + x_, + y_, + tmp, + result = [], + PI = Math.PI, + PI_4 = PI / 4; + + if (labelcount <= 0) { + return result; + } + + for (i = 0; i < labelcount; i++) { + labelsum[i] = { + m00: 0, + m01: 0, + m10: 0, + m11: 0, + m02: 0, + m20: 0, + theta: 0, + rad: 0 + }; + } + + for (y = 0; y < height; y++) { + ysq = y * y; + for (x = 0; x < width; x++) { + val = data[y * width + x]; + if (val > 0) { + label = labelsum[val - 1]; + label.m00 += 1; + label.m01 += y; + label.m10 += x; + label.m11 += x * y; + label.m02 += ysq; + label.m20 += x * x; + } + } + } + + for (i = 0; i < labelcount; i++) { + label = labelsum[i]; + if (!isNaN(label.m00) && label.m00 !== 0) { + x_ = label.m10 / label.m00; + y_ = label.m01 / label.m00; + mu11 = label.m11 / label.m00 - x_ * y_; + mu02 = label.m02 / label.m00 - y_ * y_; + mu20 = label.m20 / label.m00 - x_ * x_; + tmp = (mu02 - mu20) / (2 * mu11); + tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4) + PI; + label.theta = (tmp * 180 / PI + 90) % 180 - 90; + if (label.theta < 0) { + label.theta += 180; + } + label.rad = tmp > PI ? tmp - PI : tmp; + label.vec = _glMatrix.vec2.clone([Math.cos(tmp), Math.sin(tmp)]); + result.push(label); + } + } + + return result; + }; + + /** + * Displays the {ImageWrapper} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ + ImageWrapper.prototype.show = function (canvas, scale) { + var ctx, frame, data, current, pixel, x, y; + + if (!scale) { + scale = 1.0; + } + ctx = canvas.getContext('2d'); + canvas.width = this.size.x; + canvas.height = this.size.y; + frame = ctx.getImageData(0, 0, canvas.width, canvas.height); + data = frame.data; + current = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + pixel = y * this.size.x + x; + current = this.get(x, y) * scale; + data[pixel * 4 + 0] = current; + data[pixel * 4 + 1] = current; + data[pixel * 4 + 2] = current; + data[pixel * 4 + 3] = 255; + } + } + //frame.data = data; + ctx.putImageData(frame, 0, 0); + }; + + /** + * Displays the {SubImage} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ + ImageWrapper.prototype.overlay = function (canvas, scale, from) { + if (!scale || scale < 0 || scale > 360) { + scale = 360; + } + var hsv = [0, 1, 1]; + var rgb = [0, 0, 0]; + var whiteRgb = [255, 255, 255]; + var blackRgb = [0, 0, 0]; + var result = []; + var ctx = canvas.getContext('2d'); + var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y); + var data = frame.data; + var length = this.data.length; + while (length--) { + hsv[0] = this.data[length] * scale; + result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : _cv_utils2['default'].hsv2rgb(hsv, rgb); + data[length * 4 + 0] = result[0]; + data[length * 4 + 1] = result[1]; + data[length * 4 + 2] = result[2]; + data[length * 4 + 3] = 255; + } + ctx.putImageData(frame, from.x, from.y); + }; + + exports['default'] = ImageWrapper; + module.exports = exports['default']; + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + /** + * Construct representing a part of another {ImageWrapper}. Shares data + * between the parent and the child. + * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) + * @param size {ImageRef} The size of the resulting image + * @param I {ImageWrapper} The {ImageWrapper} to share from + * @returns {SubImage} A shared part of the original image + */ + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + function SubImage(from, size, I) { + if (!I) { + I = { + data: null, + size: size + }; + } + this.data = I.data; + this.originalSize = I.size; + this.I = I; + + this.from = from; + this.size = size; + } + + /** + * Displays the {SubImage} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ + SubImage.prototype.show = function (canvas, scale) { + var ctx, frame, data, current, y, x, pixel; + + if (!scale) { + scale = 1.0; + } + ctx = canvas.getContext('2d'); + canvas.width = this.size.x; + canvas.height = this.size.y; + frame = ctx.getImageData(0, 0, canvas.width, canvas.height); + data = frame.data; + current = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + pixel = y * this.size.x + x; + current = this.get(x, y) * scale; + data[pixel * 4 + 0] = current; + data[pixel * 4 + 1] = current; + data[pixel * 4 + 2] = current; + data[pixel * 4 + 3] = 255; + } + } + frame.data = data; + ctx.putImageData(frame, 0, 0); + }; + + /** + * Retrieves a given pixel position from the {SubImage} + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ + SubImage.prototype.get = function (x, y) { + return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x]; + }; + + /** + * Updates the underlying data from a given {ImageWrapper} + * @param image {ImageWrapper} The updated image + */ + SubImage.prototype.updateData = function (image) { + this.originalSize = image.size; + this.data = image.data; + }; + + /** + * Updates the position of the shared area + * @param from {x,y} The new location + * @returns {SubImage} returns {this} for possible chaining + */ + SubImage.prototype.updateFrom = function (from) { + this.from = from; + return this; + }; + + exports['default'] = SubImage; + module.exports = exports['default']; + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _cluster = __webpack_require__(6); + + var _cluster2 = _interopRequireDefault(_cluster); + + var _array_helper = __webpack_require__(17); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + var _glMatrix = __webpack_require__(7); + + var CVUtils = {}; + + /** + * @param x x-coordinate + * @param y y-coordinate + * @return ImageReference {x,y} Coordinate + */ + CVUtils.imageRef = function (x, y) { + var that = { + x: x, + y: y, + toVec2: function toVec2() { + return _glMatrix.vec2.clone([this.x, this.y]); + }, + toVec3: function toVec3() { + return _glMatrix.vec3.clone([this.x, this.y, 1]); + }, + round: function round() { + this.x = this.x > 0.0 ? Math.floor(this.x + 0.5) : Math.floor(this.x - 0.5); + this.y = this.y > 0.0 ? Math.floor(this.y + 0.5) : Math.floor(this.y - 0.5); + return this; + } + }; + return that; + }; + + /** + * Computes an integral image of a given grayscale image. + * @param imageDataContainer {ImageDataContainer} the image to be integrated + */ + CVUtils.computeIntegralImage2 = function (imageWrapper, integralWrapper) { + var imageData = imageWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0, + posA = 0, + posB = 0, + posC = 0, + posD = 0, + x, + y; + + // sum up first column + posB = width; + sum = 0; + for (y = 1; y < height; y++) { + sum += imageData[posA]; + integralImageData[posB] += sum; + posA += width; + posB += width; + } + + posA = 0; + posB = 1; + sum = 0; + for (x = 1; x < width; x++) { + sum += imageData[posA]; + integralImageData[posB] += sum; + posA++; + posB++; + } + + for (y = 1; y < height; y++) { + posA = y * width + 1; + posB = (y - 1) * width + 1; + posC = y * width; + posD = (y - 1) * width; + for (x = 1; x < width; x++) { + integralImageData[posA] += imageData[posA] + integralImageData[posB] + integralImageData[posC] - integralImageData[posD]; + posA++; + posB++; + posC++; + posD++; + } + } + }; + + CVUtils.computeIntegralImage = function (imageWrapper, integralWrapper) { + var imageData = imageWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0; + + // sum up first row + for (var i = 0; i < width; i++) { + sum += imageData[i]; + integralImageData[i] = sum; + } + + for (var v = 1; v < height; v++) { + sum = 0; + for (var u = 0; u < width; u++) { + sum += imageData[v * width + u]; + integralImageData[v * width + u] = sum + integralImageData[(v - 1) * width + u]; + } + } + }; + + CVUtils.thresholdImage = function (imageWrapper, threshold, targetWrapper) { + if (!targetWrapper) { + targetWrapper = imageWrapper; + } + var imageData = imageWrapper.data, + length = imageData.length, + targetData = targetWrapper.data; + + while (length--) { + targetData[length] = imageData[length] < threshold ? 1 : 0; + } + }; + + CVUtils.computeHistogram = function (imageWrapper, bitsPerPixel) { + if (!bitsPerPixel) { + bitsPerPixel = 8; + } + var imageData = imageWrapper.data, + length = imageData.length, + bitShift = 8 - bitsPerPixel, + bucketCnt = 1 << bitsPerPixel, + hist = new Int32Array(bucketCnt); + + while (length--) { + hist[imageData[length] >> bitShift]++; + } + return hist; + }; + + CVUtils.sharpenLine = function (line) { + var i, + length = line.length, + left = line[0], + center = line[1], + right; + + for (i = 1; i < length - 1; i++) { + right = line[i + 1]; + // -1 4 -1 kernel + line[i - 1] = center * 2 - left - right & 255; + left = center; + center = right; + } + return line; + }; + + CVUtils.determineOtsuThreshold = function (imageWrapper, bitsPerPixel) { + if (!bitsPerPixel) { + bitsPerPixel = 8; + } + var hist, + threshold, + bitShift = 8 - bitsPerPixel; + + function px(init, end) { + var sum = 0, + i; + for (i = init; i <= end; i++) { + sum += hist[i]; + } + return sum; + } + + function mx(init, end) { + var i, + sum = 0; + + for (i = init; i <= end; i++) { + sum += i * hist[i]; + } + + return sum; + } + + function determineThreshold() { + var vet = [0], + p1, + p2, + p12, + k, + m1, + m2, + m12, + max = (1 << bitsPerPixel) - 1; + + hist = CVUtils.computeHistogram(imageWrapper, bitsPerPixel); + for (k = 1; k < max; k++) { + p1 = px(0, k); + p2 = px(k + 1, max); + p12 = p1 * p2; + if (p12 === 0) { + p12 = 1; + } + m1 = mx(0, k) * p2; + m2 = mx(k + 1, max) * p1; + m12 = m1 - m2; + vet[k] = m12 * m12 / p12; + } + return _array_helper2['default'].maxIndex(vet); + } + + threshold = determineThreshold(); + return threshold << bitShift; + }; + + CVUtils.otsuThreshold = function (imageWrapper, targetWrapper) { + var threshold = CVUtils.determineOtsuThreshold(imageWrapper); + + CVUtils.thresholdImage(imageWrapper, threshold, targetWrapper); + return threshold; + }; + + // local thresholding + CVUtils.computeBinaryImage = function (imageWrapper, integralWrapper, targetWrapper) { + CVUtils.computeIntegralImage(imageWrapper, integralWrapper); + + if (!targetWrapper) { + targetWrapper = imageWrapper; + } + var imageData = imageWrapper.data; + var targetData = targetWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0, + v, + u, + kernel = 3, + A, + B, + C, + D, + avg, + size = (kernel * 2 + 1) * (kernel * 2 + 1); + + // clear out top & bottom-border + for (v = 0; v <= kernel; v++) { + for (u = 0; u < width; u++) { + targetData[v * width + u] = 0; + targetData[(height - 1 - v) * width + u] = 0; + } + } + + // clear out left & right border + for (v = kernel; v < height - kernel; v++) { + for (u = 0; u <= kernel; u++) { + targetData[v * width + u] = 0; + targetData[v * width + (width - 1 - u)] = 0; + } + } + + for (v = kernel + 1; v < height - kernel - 1; v++) { + for (u = kernel + 1; u < width - kernel; u++) { + A = integralImageData[(v - kernel - 1) * width + (u - kernel - 1)]; + B = integralImageData[(v - kernel - 1) * width + (u + kernel)]; + C = integralImageData[(v + kernel) * width + (u - kernel - 1)]; + D = integralImageData[(v + kernel) * width + (u + kernel)]; + sum = D - C - B + A; + avg = sum / size; + targetData[v * width + u] = imageData[v * width + u] > avg + 5 ? 0 : 1; + } + } + }; + + CVUtils.cluster = function (points, threshold, property) { + var i, + k, + cluster, + point, + clusters = []; + + if (!property) { + property = "rad"; + } + + function addToCluster(newPoint) { + var found = false; + for (k = 0; k < clusters.length; k++) { + cluster = clusters[k]; + if (cluster.fits(newPoint)) { + cluster.add(newPoint); + found = true; + } + } + return found; + } + + // iterate over each cloud + for (i = 0; i < points.length; i++) { + point = _cluster2['default'].createPoint(points[i], i, property); + if (!addToCluster(point)) { + clusters.push(_cluster2['default'].create(point, threshold)); + } + } + return clusters; + }; + + CVUtils.Tracer = { + trace: function trace(points, vec) { + var iteration, + maxIterations = 10, + top = [], + result = [], + centerPos = 0, + currentPos = 0; + + function trace(idx, forward) { + var from, + to, + toIdx, + predictedPos, + thresholdX = 1, + thresholdY = Math.abs(vec[1] / 10), + found = false; + + function match(pos, predicted) { + if (pos.x > predicted.x - thresholdX && pos.x < predicted.x + thresholdX && pos.y > predicted.y - thresholdY && pos.y < predicted.y + thresholdY) { + return true; + } else { + return false; + } + } + + // check if the next index is within the vec specifications + // if not, check as long as the threshold is met + + from = points[idx]; + if (forward) { + predictedPos = { + x: from.x + vec[0], + y: from.y + vec[1] + }; + } else { + predictedPos = { + x: from.x - vec[0], + y: from.y - vec[1] + }; + } + + toIdx = forward ? idx + 1 : idx - 1; + to = points[toIdx]; + while (to && (found = match(to, predictedPos)) !== true && Math.abs(to.y - from.y) < vec[1]) { + toIdx = forward ? toIdx + 1 : toIdx - 1; + to = points[toIdx]; + } + + return found ? toIdx : null; + } + + for (iteration = 0; iteration < maxIterations; iteration++) { + // randomly select point to start with + centerPos = Math.floor(Math.random() * points.length); + + // trace forward + top = []; + currentPos = centerPos; + top.push(points[currentPos]); + while ((currentPos = trace(currentPos, true)) !== null) { + top.push(points[currentPos]); + } + if (centerPos > 0) { + currentPos = centerPos; + while ((currentPos = trace(currentPos, false)) !== null) { + top.push(points[currentPos]); + } + } + + if (top.length > result.length) { + result = top; + } + } + return result; + } + }; + + CVUtils.DILATE = 1; + CVUtils.ERODE = 2; + + CVUtils.dilate = function (inImageWrapper, outImageWrapper) { + var v, + u, + inImageData = inImageWrapper.data, + outImageData = outImageWrapper.data, + height = inImageWrapper.size.y, + width = inImageWrapper.size.x, + sum, + yStart1, + yStart2, + xStart1, + xStart2; + + for (v = 1; v < height - 1; v++) { + for (u = 1; u < width - 1; u++) { + yStart1 = v - 1; + yStart2 = v + 1; + xStart1 = u - 1; + xStart2 = u + 1; + sum = inImageData[yStart1 * width + xStart1] + inImageData[yStart1 * width + xStart2] + inImageData[v * width + u] + inImageData[yStart2 * width + xStart1] + inImageData[yStart2 * width + xStart2]; + outImageData[v * width + u] = sum > 0 ? 1 : 0; + } + } + }; + + CVUtils.erode = function (inImageWrapper, outImageWrapper) { + var v, + u, + inImageData = inImageWrapper.data, + outImageData = outImageWrapper.data, + height = inImageWrapper.size.y, + width = inImageWrapper.size.x, + sum, + yStart1, + yStart2, + xStart1, + xStart2; + + for (v = 1; v < height - 1; v++) { + for (u = 1; u < width - 1; u++) { + yStart1 = v - 1; + yStart2 = v + 1; + xStart1 = u - 1; + xStart2 = u + 1; + sum = inImageData[yStart1 * width + xStart1] + inImageData[yStart1 * width + xStart2] + inImageData[v * width + u] + inImageData[yStart2 * width + xStart1] + inImageData[yStart2 * width + xStart2]; + outImageData[v * width + u] = sum === 5 ? 1 : 0; + } + } + }; + + CVUtils.subtract = function (aImageWrapper, bImageWrapper, resultImageWrapper) { + if (!resultImageWrapper) { + resultImageWrapper = aImageWrapper; + } + var length = aImageWrapper.data.length, + aImageData = aImageWrapper.data, + bImageData = bImageWrapper.data, + cImageData = resultImageWrapper.data; + + while (length--) { + cImageData[length] = aImageData[length] - bImageData[length]; + } + }; + + CVUtils.bitwiseOr = function (aImageWrapper, bImageWrapper, resultImageWrapper) { + if (!resultImageWrapper) { + resultImageWrapper = aImageWrapper; + } + var length = aImageWrapper.data.length, + aImageData = aImageWrapper.data, + bImageData = bImageWrapper.data, + cImageData = resultImageWrapper.data; + + while (length--) { + cImageData[length] = aImageData[length] || bImageData[length]; + } + }; + + CVUtils.countNonZero = function (imageWrapper) { + var length = imageWrapper.data.length, + data = imageWrapper.data, + sum = 0; + + while (length--) { + sum += data[length]; + } + return sum; + }; + + CVUtils.topGeneric = function (list, top, scoreFunc) { + var i, + minIdx = 0, + min = 0, + queue = [], + score, + hit, + pos; + + for (i = 0; i < top; i++) { + queue[i] = { + score: 0, + item: null + }; + } + + for (i = 0; i < list.length; i++) { + score = scoreFunc.apply(this, [list[i]]); + if (score > min) { + hit = queue[minIdx]; + hit.score = score; + hit.item = list[i]; + min = Number.MAX_VALUE; + for (pos = 0; pos < top; pos++) { + if (queue[pos].score < min) { + min = queue[pos].score; + minIdx = pos; + } + } + } + } + + return queue; + }; + + CVUtils.grayArrayFromImage = function (htmlImage, offsetX, ctx, array) { + ctx.drawImage(htmlImage, offsetX, 0, htmlImage.width, htmlImage.height); + var ctxData = ctx.getImageData(offsetX, 0, htmlImage.width, htmlImage.height).data; + CVUtils.computeGray(ctxData, array); + }; + + CVUtils.grayArrayFromContext = function (ctx, size, offset, array) { + var ctxData = ctx.getImageData(offset.x, offset.y, size.x, size.y).data; + CVUtils.computeGray(ctxData, array); + }; + + CVUtils.grayAndHalfSampleFromCanvasData = function (canvasData, size, outArray) { + var topRowIdx = 0; + var bottomRowIdx = size.x; + var endIdx = Math.floor(canvasData.length / 4); + var outWidth = size.x / 2; + var outImgIdx = 0; + var inWidth = size.x; + var i; + + while (bottomRowIdx < endIdx) { + for (i = 0; i < outWidth; i++) { + outArray[outImgIdx] = Math.floor((0.299 * canvasData[topRowIdx * 4 + 0] + 0.587 * canvasData[topRowIdx * 4 + 1] + 0.114 * canvasData[topRowIdx * 4 + 2] + (0.299 * canvasData[(topRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(topRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(topRowIdx + 1) * 4 + 2]) + (0.299 * canvasData[bottomRowIdx * 4 + 0] + 0.587 * canvasData[bottomRowIdx * 4 + 1] + 0.114 * canvasData[bottomRowIdx * 4 + 2]) + (0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4); + outImgIdx++; + topRowIdx = topRowIdx + 2; + bottomRowIdx = bottomRowIdx + 2; + } + topRowIdx = topRowIdx + inWidth; + bottomRowIdx = bottomRowIdx + inWidth; + } + }; + + CVUtils.computeGray = function (imageData, outArray, config) { + var l = imageData.length / 4 | 0, + i, + singleChannel = config && config.singleChannel === true; + + if (singleChannel) { + for (i = 0; i < l; i++) { + outArray[i] = imageData[i * 4 + 0]; + } + } else { + for (i = 0; i < l; i++) { + outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); + } + } + }; + + CVUtils.loadImageArray = function (src, callback, canvas) { + if (!canvas) { + canvas = document.createElement('canvas'); + } + var img = new Image(); + img.callback = callback; + img.onload = function () { + canvas.width = this.width; + canvas.height = this.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0); + var array = new Uint8Array(this.width * this.height); + ctx.drawImage(this, 0, 0); + var data = ctx.getImageData(0, 0, this.width, this.height).data; + CVUtils.computeGray(data, array); + this.callback(array, { + x: this.width, + y: this.height + }, this); + }; + img.src = src; + }; + + /** + * @param inImg {ImageWrapper} input image to be sampled + * @param outImg {ImageWrapper} to be stored in + */ + CVUtils.halfSample = function (inImgWrapper, outImgWrapper) { + var inImg = inImgWrapper.data; + var inWidth = inImgWrapper.size.x; + var outImg = outImgWrapper.data; + var topRowIdx = 0; + var bottomRowIdx = inWidth; + var endIdx = inImg.length; + var outWidth = inWidth / 2; + var outImgIdx = 0; + while (bottomRowIdx < endIdx) { + for (var i = 0; i < outWidth; i++) { + outImg[outImgIdx] = Math.floor((inImg[topRowIdx] + inImg[topRowIdx + 1] + inImg[bottomRowIdx] + inImg[bottomRowIdx + 1]) / 4); + outImgIdx++; + topRowIdx = topRowIdx + 2; + bottomRowIdx = bottomRowIdx + 2; + } + topRowIdx = topRowIdx + inWidth; + bottomRowIdx = bottomRowIdx + inWidth; + } + }; + + CVUtils.hsv2rgb = function (hsv, rgb) { + var h = hsv[0], + s = hsv[1], + v = hsv[2], + c = v * s, + x = c * (1 - Math.abs(h / 60 % 2 - 1)), + m = v - c, + r = 0, + g = 0, + b = 0; + + rgb = rgb || [0, 0, 0]; + + if (h < 60) { + r = c; + g = x; + } else if (h < 120) { + r = x; + g = c; + } else if (h < 180) { + g = c; + b = x; + } else if (h < 240) { + g = x; + b = c; + } else if (h < 300) { + r = x; + b = c; + } else if (h < 360) { + r = c; + b = x; + } + rgb[0] = (r + m) * 255 | 0; + rgb[1] = (g + m) * 255 | 0; + rgb[2] = (b + m) * 255 | 0; + return rgb; + }; + + CVUtils._computeDivisors = function (n) { + var largeDivisors = [], + divisors = [], + i; + + for (i = 1; i < Math.sqrt(n) + 1; i++) { + if (n % i === 0) { + divisors.push(i); + if (i !== n / i) { + largeDivisors.unshift(Math.floor(n / i)); + } + } + } + return divisors.concat(largeDivisors); + }; + + CVUtils._computeIntersection = function (arr1, arr2) { + var i = 0, + j = 0, + result = []; + + while (i < arr1.length && j < arr2.length) { + if (arr1[i] === arr2[j]) { + result.push(arr1[i]); + i++; + j++; + } else if (arr1[i] > arr2[j]) { + j++; + } else { + i++; + } + } + return result; + }; + + CVUtils.calculatePatchSize = function (patchSize, imgSize) { + var divisorsX = this._computeDivisors(imgSize.x), + divisorsY = this._computeDivisors(imgSize.y), + wideSide = Math.max(imgSize.x, imgSize.y), + common = this._computeIntersection(divisorsX, divisorsY), + nrOfPatchesList = [8, 10, 15, 20, 32, 60, 80], + nrOfPatchesMap = { + "x-small": 5, + "small": 4, + "medium": 3, + "large": 2, + "x-large": 1 + }, + nrOfPatchesIdx = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium, + nrOfPatches = nrOfPatchesList[nrOfPatchesIdx], + desiredPatchSize = Math.floor(wideSide / nrOfPatches), + optimalPatchSize; + + function findPatchSizeForDivisors(divisors) { + var i = 0, + found = divisors[Math.floor(divisors.length / 2)]; + + while (i < divisors.length - 1 && divisors[i] < desiredPatchSize) { + i++; + } + if (i > 0) { + if (Math.abs(divisors[i] - desiredPatchSize) > Math.abs(divisors[i - 1] - desiredPatchSize)) { + found = divisors[i - 1]; + } else { + found = divisors[i]; + } + } + if (desiredPatchSize / found < nrOfPatchesList[nrOfPatchesIdx + 1] / nrOfPatchesList[nrOfPatchesIdx] && desiredPatchSize / found > nrOfPatchesList[nrOfPatchesIdx - 1] / nrOfPatchesList[nrOfPatchesIdx]) { + return { x: found, y: found }; + } + return null; + } + + optimalPatchSize = findPatchSizeForDivisors(common); + if (!optimalPatchSize) { + optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(wideSide)); + if (!optimalPatchSize) { + optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(desiredPatchSize * nrOfPatches)); + } + } + return optimalPatchSize; + }; + + CVUtils._parseCSSDimensionValues = function (value) { + var dimension = { + value: parseFloat(value), + unit: value.indexOf("%") === value.length - 1 ? "%" : "%" + }; + + return dimension; + }; + + CVUtils._dimensionsConverters = { + top: function top(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.height * (dimension.value / 100)); + } + }, + right: function right(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.width - context.width * (dimension.value / 100)); + } + }, + bottom: function bottom(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.height - context.height * (dimension.value / 100)); + } + }, + left: function left(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.width * (dimension.value / 100)); + } + } + }; + + CVUtils.computeImageArea = function (inputWidth, inputHeight, area) { + var context = { width: inputWidth, height: inputHeight }; + + var parsedArea = Object.keys(area).reduce(function (result, key) { + var value = area[key], + parsed = CVUtils._parseCSSDimensionValues(value), + calculated = CVUtils._dimensionsConverters[key](parsed, context); + + result[key] = calculated; + return result; + }, {}); + + return { + sx: parsedArea.left, + sy: parsedArea.top, + sw: parsedArea.right - parsedArea.left, + sh: parsedArea.bottom - parsedArea.top + }; + }; + + exports['default'] = CVUtils; + module.exports = exports['default']; + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _glMatrix = __webpack_require__(7); + + /** + * Creates a cluster for grouping similar orientations of datapoints + */ + exports['default'] = { + create: function create(point, threshold) { + var points = [], + center = { + rad: 0, + vec: _glMatrix.vec2.clone([0, 0]) + }, + pointMap = {}; + + function init() { + _add(point); + updateCenter(); + } + + function _add(pointToAdd) { + pointMap[pointToAdd.id] = pointToAdd; + points.push(pointToAdd); + } + + function updateCenter() { + var i, + sum = 0; + for (i = 0; i < points.length; i++) { + sum += points[i].rad; + } + center.rad = sum / points.length; + center.vec = _glMatrix.vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]); + } + + init(); + + return { + add: function add(pointToAdd) { + if (!pointMap[pointToAdd.id]) { + _add(pointToAdd); + updateCenter(); + } + }, + fits: function fits(otherPoint) { + // check cosine similarity to center-angle + var similarity = Math.abs(_glMatrix.vec2.dot(otherPoint.point.vec, center.vec)); + if (similarity > threshold) { + return true; + } + return false; + }, + getPoints: function getPoints() { + return points; + }, + getCenter: function getCenter() { + return center; + } + }; + }, + createPoint: function createPoint(newPoint, id, property) { + return { + rad: newPoint[property], + point: newPoint, + id: id + }; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @fileoverview gl-matrix - High performance matrix and vector operations + * @author Brandon Jones + * @author Colin MacKenzie IV + * @version 2.3.0 + */ + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + // END HEADER + + exports.glMatrix = __webpack_require__(8); + exports.mat2 = __webpack_require__(9); + exports.mat2d = __webpack_require__(10); + exports.mat3 = __webpack_require__(11); + exports.mat4 = __webpack_require__(12); + exports.quat = __webpack_require__(13); + exports.vec2 = __webpack_require__(16); + exports.vec3 = __webpack_require__(14); + exports.vec4 = __webpack_require__(15); + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + /** + * @class Common utilities + * @name glMatrix + */ + var glMatrix = {}; + + // Constants + glMatrix.EPSILON = 0.000001; + glMatrix.ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + glMatrix.RANDOM = Math.random; + + /** + * Sets the type of array used when creating new vectors and matrices + * + * @param {Type} type Array type, such as Float32Array or Array + */ + glMatrix.setMatrixArrayType = function(type) { + GLMAT_ARRAY_TYPE = type; + } + + var degree = Math.PI / 180; + + /** + * Convert Degree To Radian + * + * @param {Number} Angle in Degrees + */ + glMatrix.toRadian = function(a){ + return a * degree; + } + + module.exports = glMatrix; + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 2x2 Matrix + * @name mat2 + */ + var mat2 = {}; + + /** + * Creates a new identity mat2 + * + * @returns {mat2} a new 2x2 matrix + */ + mat2.create = function() { + var out = new glMatrix.ARRAY_TYPE(4); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Creates a new mat2 initialized with values from an existing matrix + * + * @param {mat2} a matrix to clone + * @returns {mat2} a new 2x2 matrix + */ + mat2.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Copy the values from one mat2 to another + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Set a mat2 to the identity matrix + * + * @param {mat2} out the receiving matrix + * @returns {mat2} out + */ + mat2.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Transpose the values of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a1 = a[1]; + out[1] = a[2]; + out[2] = a1; + } else { + out[0] = a[0]; + out[1] = a[2]; + out[2] = a[1]; + out[3] = a[3]; + } + + return out; + }; + + /** + * Inverts a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + + // Calculate the determinant + det = a0 * a3 - a2 * a1; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = a3 * det; + out[1] = -a1 * det; + out[2] = -a2 * det; + out[3] = a0 * det; + + return out; + }; + + /** + * Calculates the adjugate of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.adjoint = function(out, a) { + // Caching this value is nessecary if out == a + var a0 = a[0]; + out[0] = a[3]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a0; + + return out; + }; + + /** + * Calculates the determinant of a mat2 + * + * @param {mat2} a the source matrix + * @returns {Number} determinant of a + */ + mat2.determinant = function (a) { + return a[0] * a[3] - a[2] * a[1]; + }; + + /** + * Multiplies two mat2's + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the first operand + * @param {mat2} b the second operand + * @returns {mat2} out + */ + mat2.multiply = function (out, a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = a0 * b0 + a2 * b1; + out[1] = a1 * b0 + a3 * b1; + out[2] = a0 * b2 + a2 * b3; + out[3] = a1 * b2 + a3 * b3; + return out; + }; + + /** + * Alias for {@link mat2.multiply} + * @function + */ + mat2.mul = mat2.multiply; + + /** + * Rotates a mat2 by the given angle + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2} out + */ + mat2.rotate = function (out, a, rad) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; + return out; + }; + + /** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat2} out + **/ + mat2.scale = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + v0 = v[0], v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; + return out; + }; + + /** + * Creates a matrix from a given angle + * This is equivalent to (but much faster than): + * + * mat2.identity(dest); + * mat2.rotate(dest, dest, rad); + * + * @param {mat2} out mat2 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2} out + */ + mat2.fromRotation = function(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad); + out[0] = c; + out[1] = s; + out[2] = -s; + out[3] = c; + return out; + } + + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat2.identity(dest); + * mat2.scale(dest, dest, vec); + * + * @param {mat2} out mat2 receiving operation result + * @param {vec2} v Scaling vector + * @returns {mat2} out + */ + mat2.fromScaling = function(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = v[1]; + return out; + } + + /** + * Returns a string representation of a mat2 + * + * @param {mat2} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat2.str = function (a) { + return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; + }; + + /** + * Returns Frobenius norm of a mat2 + * + * @param {mat2} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat2.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2))) + }; + + /** + * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix + * @param {mat2} L the lower triangular matrix + * @param {mat2} D the diagonal matrix + * @param {mat2} U the upper triangular matrix + * @param {mat2} a the input matrix to factorize + */ + + mat2.LDU = function (L, D, U, a) { + L[2] = a[2]/a[0]; + U[0] = a[0]; + U[1] = a[1]; + U[3] = a[3] - L[2] * U[1]; + return [L, D, U]; + }; + + + module.exports = mat2; + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 2x3 Matrix + * @name mat2d + * + * @description + * A mat2d contains six elements defined as: + *
+	 * [a, c, tx,
+	 *  b, d, ty]
+	 * 
+ * This is a short form for the 3x3 matrix: + *
+	 * [a, c, tx,
+	 *  b, d, ty,
+	 *  0, 0, 1]
+	 * 
+ * The last row is ignored so the array is shorter and operations are faster. + */ + var mat2d = {}; + + /** + * Creates a new identity mat2d + * + * @returns {mat2d} a new 2x3 matrix + */ + mat2d.create = function() { + var out = new glMatrix.ARRAY_TYPE(6); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + }; + + /** + * Creates a new mat2d initialized with values from an existing matrix + * + * @param {mat2d} a matrix to clone + * @returns {mat2d} a new 2x3 matrix + */ + mat2d.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(6); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + return out; + }; + + /** + * Copy the values from one mat2d to another + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the source matrix + * @returns {mat2d} out + */ + mat2d.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + return out; + }; + + /** + * Set a mat2d to the identity matrix + * + * @param {mat2d} out the receiving matrix + * @returns {mat2d} out + */ + mat2d.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + }; + + /** + * Inverts a mat2d + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the source matrix + * @returns {mat2d} out + */ + mat2d.invert = function(out, a) { + var aa = a[0], ab = a[1], ac = a[2], ad = a[3], + atx = a[4], aty = a[5]; + + var det = aa * ad - ab * ac; + if(!det){ + return null; + } + det = 1.0 / det; + + out[0] = ad * det; + out[1] = -ab * det; + out[2] = -ac * det; + out[3] = aa * det; + out[4] = (ac * aty - ad * atx) * det; + out[5] = (ab * atx - aa * aty) * det; + return out; + }; + + /** + * Calculates the determinant of a mat2d + * + * @param {mat2d} a the source matrix + * @returns {Number} determinant of a + */ + mat2d.determinant = function (a) { + return a[0] * a[3] - a[1] * a[2]; + }; + + /** + * Multiplies two mat2d's + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the first operand + * @param {mat2d} b the second operand + * @returns {mat2d} out + */ + mat2d.multiply = function (out, a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5]; + out[0] = a0 * b0 + a2 * b1; + out[1] = a1 * b0 + a3 * b1; + out[2] = a0 * b2 + a2 * b3; + out[3] = a1 * b2 + a3 * b3; + out[4] = a0 * b4 + a2 * b5 + a4; + out[5] = a1 * b4 + a3 * b5 + a5; + return out; + }; + + /** + * Alias for {@link mat2d.multiply} + * @function + */ + mat2d.mul = mat2d.multiply; + + /** + * Rotates a mat2d by the given angle + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2d} out + */ + mat2d.rotate = function (out, a, rad) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; + out[4] = a4; + out[5] = a5; + return out; + }; + + /** + * Scales the mat2d by the dimensions in the given vec2 + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the matrix to translate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat2d} out + **/ + mat2d.scale = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + v0 = v[0], v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; + out[4] = a4; + out[5] = a5; + return out; + }; + + /** + * Translates the mat2d by the dimensions in the given vec2 + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the matrix to translate + * @param {vec2} v the vec2 to translate the matrix by + * @returns {mat2d} out + **/ + mat2d.translate = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + v0 = v[0], v1 = v[1]; + out[0] = a0; + out[1] = a1; + out[2] = a2; + out[3] = a3; + out[4] = a0 * v0 + a2 * v1 + a4; + out[5] = a1 * v0 + a3 * v1 + a5; + return out; + }; + + /** + * Creates a matrix from a given angle + * This is equivalent to (but much faster than): + * + * mat2d.identity(dest); + * mat2d.rotate(dest, dest, rad); + * + * @param {mat2d} out mat2d receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2d} out + */ + mat2d.fromRotation = function(out, rad) { + var s = Math.sin(rad), c = Math.cos(rad); + out[0] = c; + out[1] = s; + out[2] = -s; + out[3] = c; + out[4] = 0; + out[5] = 0; + return out; + } + + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat2d.identity(dest); + * mat2d.scale(dest, dest, vec); + * + * @param {mat2d} out mat2d receiving operation result + * @param {vec2} v Scaling vector + * @returns {mat2d} out + */ + mat2d.fromScaling = function(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = v[1]; + out[4] = 0; + out[5] = 0; + return out; + } + + /** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat2d.identity(dest); + * mat2d.translate(dest, dest, vec); + * + * @param {mat2d} out mat2d receiving operation result + * @param {vec2} v Translation vector + * @returns {mat2d} out + */ + mat2d.fromTranslation = function(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = v[0]; + out[5] = v[1]; + return out; + } + + /** + * Returns a string representation of a mat2d + * + * @param {mat2d} a matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat2d.str = function (a) { + return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ')'; + }; + + /** + * Returns Frobenius norm of a mat2d + * + * @param {mat2d} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat2d.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + 1)) + }; + + module.exports = mat2d; + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 3x3 Matrix + * @name mat3 + */ + var mat3 = {}; + + /** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ + mat3.create = function() { + var out = new glMatrix.ARRAY_TYPE(9); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + }; + + /** + * Copies the upper-left 3x3 values into the given mat3. + * + * @param {mat3} out the receiving 3x3 matrix + * @param {mat4} a the source 4x4 matrix + * @returns {mat3} out + */ + mat3.fromMat4 = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[4]; + out[4] = a[5]; + out[5] = a[6]; + out[6] = a[8]; + out[7] = a[9]; + out[8] = a[10]; + return out; + }; + + /** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {mat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ + mat3.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + }; + + /** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + }; + + /** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ + mat3.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + }; + + /** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; + }; + + /** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; + }; + + /** + * Calculates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + out[0] = (a11 * a22 - a12 * a21); + out[1] = (a02 * a21 - a01 * a22); + out[2] = (a01 * a12 - a02 * a11); + out[3] = (a12 * a20 - a10 * a22); + out[4] = (a00 * a22 - a02 * a20); + out[5] = (a02 * a10 - a00 * a12); + out[6] = (a10 * a21 - a11 * a20); + out[7] = (a01 * a20 - a00 * a21); + out[8] = (a00 * a11 - a01 * a10); + return out; + }; + + /** + * Calculates the determinant of a mat3 + * + * @param {mat3} a the source matrix + * @returns {Number} determinant of a + */ + mat3.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + }; + + /** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the first operand + * @param {mat3} b the second operand + * @returns {mat3} out + */ + mat3.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b00 = b[0], b01 = b[1], b02 = b[2], + b10 = b[3], b11 = b[4], b12 = b[5], + b20 = b[6], b21 = b[7], b22 = b[8]; + + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; + }; + + /** + * Alias for {@link mat3.multiply} + * @function + */ + mat3.mul = mat3.multiply; + + /** + * Translate a mat3 by the given vector + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to translate + * @param {vec2} v vector to translate by + * @returns {mat3} out + */ + mat3.translate = function(out, a, v) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + x = v[0], y = v[1]; + + out[0] = a00; + out[1] = a01; + out[2] = a02; + + out[3] = a10; + out[4] = a11; + out[5] = a12; + + out[6] = x * a00 + y * a10 + a20; + out[7] = x * a01 + y * a11 + a21; + out[8] = x * a02 + y * a12 + a22; + return out; + }; + + /** + * Rotates a mat3 by the given angle + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ + mat3.rotate = function (out, a, rad) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + s = Math.sin(rad), + c = Math.cos(rad); + + out[0] = c * a00 + s * a10; + out[1] = c * a01 + s * a11; + out[2] = c * a02 + s * a12; + + out[3] = c * a10 - s * a00; + out[4] = c * a11 - s * a01; + out[5] = c * a12 - s * a02; + + out[6] = a20; + out[7] = a21; + out[8] = a22; + return out; + }; + + /** + * Scales the mat3 by the dimensions in the given vec2 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat3} out + **/ + mat3.scale = function(out, a, v) { + var x = v[0], y = v[1]; + + out[0] = x * a[0]; + out[1] = x * a[1]; + out[2] = x * a[2]; + + out[3] = y * a[3]; + out[4] = y * a[4]; + out[5] = y * a[5]; + + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + }; + + /** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat3.identity(dest); + * mat3.translate(dest, dest, vec); + * + * @param {mat3} out mat3 receiving operation result + * @param {vec2} v Translation vector + * @returns {mat3} out + */ + mat3.fromTranslation = function(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = v[0]; + out[7] = v[1]; + out[8] = 1; + return out; + } + + /** + * Creates a matrix from a given angle + * This is equivalent to (but much faster than): + * + * mat3.identity(dest); + * mat3.rotate(dest, dest, rad); + * + * @param {mat3} out mat3 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ + mat3.fromRotation = function(out, rad) { + var s = Math.sin(rad), c = Math.cos(rad); + + out[0] = c; + out[1] = s; + out[2] = 0; + + out[3] = -s; + out[4] = c; + out[5] = 0; + + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + } + + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat3.identity(dest); + * mat3.scale(dest, dest, vec); + * + * @param {mat3} out mat3 receiving operation result + * @param {vec2} v Scaling vector + * @returns {mat3} out + */ + mat3.fromScaling = function(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + + out[3] = 0; + out[4] = v[1]; + out[5] = 0; + + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + } + + /** + * Copies the values from a mat2d into a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat2d} a the matrix to copy + * @returns {mat3} out + **/ + mat3.fromMat2d = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = 0; + + out[3] = a[2]; + out[4] = a[3]; + out[5] = 0; + + out[6] = a[4]; + out[7] = a[5]; + out[8] = 1; + return out; + }; + + /** + * Calculates a 3x3 matrix from the given quaternion + * + * @param {mat3} out mat3 receiving operation result + * @param {quat} q Quaternion to create matrix from + * + * @returns {mat3} out + */ + mat3.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + yx = y * x2, + yy = y * y2, + zx = z * x2, + zy = z * y2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - yy - zz; + out[3] = yx - wz; + out[6] = zx + wy; + + out[1] = yx + wz; + out[4] = 1 - xx - zz; + out[7] = zy - wx; + + out[2] = zx - wy; + out[5] = zy + wx; + out[8] = 1 - xx - yy; + + return out; + }; + + /** + * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix + * + * @param {mat3} out mat3 receiving operation result + * @param {mat4} a Mat4 to derive the normal matrix from + * + * @returns {mat3} out + */ + mat3.normalFromMat4 = function (out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + + out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + + out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + + return out; + }; + + /** + * Returns a string representation of a mat3 + * + * @param {mat3} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat3.str = function (a) { + return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + + a[6] + ', ' + a[7] + ', ' + a[8] + ')'; + }; + + /** + * Returns Frobenius norm of a mat3 + * + * @param {mat3} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat3.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2))) + }; + + + module.exports = mat3; + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 4x4 Matrix + * @name mat4 + */ + var mat4 = {}; + + /** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ + mat4.create = function() { + var out = new glMatrix.ARRAY_TYPE(16); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + }; + + /** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ + mat4.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + }; + + /** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + }; + + /** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ + mat4.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + }; + + /** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; + }; + + /** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; + }; + + /** + * Calculates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; + }; + + /** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ + mat4.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + }; + + /** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ + mat4.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; + }; + + /** + * Alias for {@link mat4.multiply} + * @function + */ + mat4.mul = mat4.multiply; + + /** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ + mat4.translate = function (out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; + }; + + /** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ + mat4.scale = function(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + }; + + /** + * Rotates a mat4 by the given angle around the given axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ + mat4.rotate = function (out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < glMatrix.EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; + }; + + /** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.rotateX = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; + }; + + /** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.rotateY = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; + }; + + /** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.rotateZ = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; + }; + + /** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, dest, vec); + * + * @param {mat4} out mat4 receiving operation result + * @param {vec3} v Translation vector + * @returns {mat4} out + */ + mat4.fromTranslation = function(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; + } + + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.scale(dest, dest, vec); + * + * @param {mat4} out mat4 receiving operation result + * @param {vec3} v Scaling vector + * @returns {mat4} out + */ + mat4.fromScaling = function(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = v[1]; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = v[2]; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + /** + * Creates a matrix from a given angle around a given axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotate(dest, dest, rad, axis); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ + mat4.fromRotation = function(out, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t; + + if (Math.abs(len) < glMatrix.EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + // Perform rotation-specific matrix multiplication + out[0] = x * x * t + c; + out[1] = y * x * t + z * s; + out[2] = z * x * t - y * s; + out[3] = 0; + out[4] = x * y * t - z * s; + out[5] = y * y * t + c; + out[6] = z * y * t + x * s; + out[7] = 0; + out[8] = x * z * t + y * s; + out[9] = y * z * t - x * s; + out[10] = z * z * t + c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + /** + * Creates a matrix from the given angle around the X axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotateX(dest, dest, rad); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.fromXRotation = function(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad); + + // Perform axis-specific matrix multiplication + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = c; + out[6] = s; + out[7] = 0; + out[8] = 0; + out[9] = -s; + out[10] = c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + /** + * Creates a matrix from the given angle around the Y axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotateY(dest, dest, rad); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.fromYRotation = function(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad); + + // Perform axis-specific matrix multiplication + out[0] = c; + out[1] = 0; + out[2] = -s; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = s; + out[9] = 0; + out[10] = c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + /** + * Creates a matrix from the given angle around the Z axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotateZ(dest, dest, rad); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.fromZRotation = function(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad); + + // Perform axis-specific matrix multiplication + out[0] = c; + out[1] = s; + out[2] = 0; + out[3] = 0; + out[4] = -s; + out[5] = c; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + + /** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ + mat4.fromRotationTranslation = function (out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; + }; + + /** + * Creates a matrix from a quaternion rotation, vector translation and vector scale + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * mat4.scale(dest, scale) + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @param {vec3} s Scaling vector + * @returns {mat4} out + */ + mat4.fromRotationTranslationScale = function (out, q, v, s) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2, + sx = s[0], + sy = s[1], + sz = s[2]; + + out[0] = (1 - (yy + zz)) * sx; + out[1] = (xy + wz) * sx; + out[2] = (xz - wy) * sx; + out[3] = 0; + out[4] = (xy - wz) * sy; + out[5] = (1 - (xx + zz)) * sy; + out[6] = (yz + wx) * sy; + out[7] = 0; + out[8] = (xz + wy) * sz; + out[9] = (yz - wx) * sz; + out[10] = (1 - (xx + yy)) * sz; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; + }; + + /** + * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * mat4.translate(dest, origin); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * mat4.scale(dest, scale) + * mat4.translate(dest, negativeOrigin); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @param {vec3} s Scaling vector + * @param {vec3} o The origin vector around which to scale and rotate + * @returns {mat4} out + */ + mat4.fromRotationTranslationScaleOrigin = function (out, q, v, s, o) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2, + + sx = s[0], + sy = s[1], + sz = s[2], + + ox = o[0], + oy = o[1], + oz = o[2]; + + out[0] = (1 - (yy + zz)) * sx; + out[1] = (xy + wz) * sx; + out[2] = (xz - wy) * sx; + out[3] = 0; + out[4] = (xy - wz) * sy; + out[5] = (1 - (xx + zz)) * sy; + out[6] = (yz + wx) * sy; + out[7] = 0; + out[8] = (xz + wy) * sz; + out[9] = (yz - wx) * sz; + out[10] = (1 - (xx + yy)) * sz; + out[11] = 0; + out[12] = v[0] + ox - (out[0] * ox + out[4] * oy + out[8] * oz); + out[13] = v[1] + oy - (out[1] * ox + out[5] * oy + out[9] * oz); + out[14] = v[2] + oz - (out[2] * ox + out[6] * oy + out[10] * oz); + out[15] = 1; + + return out; + }; + + mat4.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + yx = y * x2, + yy = y * y2, + zx = z * x2, + zy = z * y2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - yy - zz; + out[1] = yx + wz; + out[2] = zx - wy; + out[3] = 0; + + out[4] = yx - wz; + out[5] = 1 - xx - zz; + out[6] = zy + wx; + out[7] = 0; + + out[8] = zx + wy; + out[9] = zy - wx; + out[10] = 1 - xx - yy; + out[11] = 0; + + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + + return out; + }; + + /** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.frustum = function (out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; + }; + + /** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.perspective = function (out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; + }; + + /** + * Generates a perspective projection matrix with the given field of view. + * This is primarily useful for generating projection matrices to be used + * with the still experiemental WebVR API. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.perspectiveFromFieldOfView = function (out, fov, near, far) { + var upTan = Math.tan(fov.upDegrees * Math.PI/180.0), + downTan = Math.tan(fov.downDegrees * Math.PI/180.0), + leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0), + rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0), + xScale = 2.0 / (leftTan + rightTan), + yScale = 2.0 / (upTan + downTan); + + out[0] = xScale; + out[1] = 0.0; + out[2] = 0.0; + out[3] = 0.0; + out[4] = 0.0; + out[5] = yScale; + out[6] = 0.0; + out[7] = 0.0; + out[8] = -((leftTan - rightTan) * xScale * 0.5); + out[9] = ((upTan - downTan) * yScale * 0.5); + out[10] = far / (near - far); + out[11] = -1.0; + out[12] = 0.0; + out[13] = 0.0; + out[14] = (far * near) / (near - far); + out[15] = 0.0; + return out; + } + + /** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.ortho = function (out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; + }; + + /** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ + mat4.lookAt = function (out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < glMatrix.EPSILON && + Math.abs(eyey - centery) < glMatrix.EPSILON && + Math.abs(eyez - centerz) < glMatrix.EPSILON) { + return mat4.identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; + }; + + /** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat4.str = function (a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; + }; + + /** + * Returns Frobenius norm of a mat4 + * + * @param {mat4} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat4.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2) + Math.pow(a[9], 2) + Math.pow(a[10], 2) + Math.pow(a[11], 2) + Math.pow(a[12], 2) + Math.pow(a[13], 2) + Math.pow(a[14], 2) + Math.pow(a[15], 2) )) + }; + + + module.exports = mat4; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + var mat3 = __webpack_require__(11); + var vec3 = __webpack_require__(14); + var vec4 = __webpack_require__(15); + + /** + * @class Quaternion + * @name quat + */ + var quat = {}; + + /** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ + quat.create = function() { + var out = new glMatrix.ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Sets a quaternion to represent the shortest rotation from one + * vector to another. + * + * Both vectors are assumed to be unit length. + * + * @param {quat} out the receiving quaternion. + * @param {vec3} a the initial vector + * @param {vec3} b the destination vector + * @returns {quat} out + */ + quat.rotationTo = (function() { + var tmpvec3 = vec3.create(); + var xUnitVec3 = vec3.fromValues(1,0,0); + var yUnitVec3 = vec3.fromValues(0,1,0); + + return function(out, a, b) { + var dot = vec3.dot(a, b); + if (dot < -0.999999) { + vec3.cross(tmpvec3, xUnitVec3, a); + if (vec3.length(tmpvec3) < 0.000001) + vec3.cross(tmpvec3, yUnitVec3, a); + vec3.normalize(tmpvec3, tmpvec3); + quat.setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + vec3.cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return quat.normalize(out, out); + } + }; + })(); + + /** + * Sets the specified quaternion with values corresponding to the given + * axes. Each axis is a vec3 and is expected to be unit length and + * perpendicular to all other specified axes. + * + * @param {vec3} view the vector representing the viewing direction + * @param {vec3} right the vector representing the local "right" direction + * @param {vec3} up the vector representing the local "up" direction + * @returns {quat} out + */ + quat.setAxes = (function() { + var matr = mat3.create(); + + return function(out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + + matr[2] = -view[0]; + matr[5] = -view[1]; + matr[8] = -view[2]; + + return quat.normalize(out, quat.fromMat3(out, matr)); + }; + })(); + + /** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {quat} a quaternion to clone + * @returns {quat} a new quaternion + * @function + */ + quat.clone = vec4.clone; + + /** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + * @function + */ + quat.fromValues = vec4.fromValues; + + /** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {quat} a the source quaternion + * @returns {quat} out + * @function + */ + quat.copy = vec4.copy; + + /** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + * @function + */ + quat.set = vec4.set; + + /** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ + quat.identity = function(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {vec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ + quat.setAxisAngle = function(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; + }; + + /** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + * @function + */ + quat.add = vec4.add; + + /** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ + quat.multiply = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; + }; + + /** + * Alias for {@link quat.multiply} + * @function + */ + quat.mul = quat.multiply; + + /** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {quat} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {quat} out + * @function + */ + quat.scale = vec4.scale; + + /** + * Rotates a quaternion by the given angle about the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + quat.rotateX = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; + }; + + /** + * Rotates a quaternion by the given angle about the Y axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + quat.rotateY = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + by = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; + }; + + /** + * Rotates a quaternion by the given angle about the Z axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + quat.rotateZ = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bz = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; + }; + + /** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate W component of + * @returns {quat} out + */ + quat.calculateW = function (out, a) { + var x = a[0], y = a[1], z = a[2]; + + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; + }; + + /** + * Calculates the dot product of two quat's + * + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {Number} dot product of a and b + * @function + */ + quat.dot = vec4.dot; + + /** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + * @function + */ + quat.lerp = vec4.lerp; + + /** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ + quat.slerp = function (out, a, b, t) { + // benchmarks: + // http://jsperf.com/quaternion-slerp-implementations + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + var omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = ax * bx + ay * by + az * bz + aw * bw; + // adjust signs (if necessary) + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + // calculate coefficients + if ( (1.0 - cosom) > 0.000001 ) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + + return out; + }; + + /** + * Performs a spherical linear interpolation with two control points + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {quat} c the third operand + * @param {quat} d the fourth operand + * @param {Number} t interpolation amount + * @returns {quat} out + */ + quat.sqlerp = (function () { + var temp1 = quat.create(); + var temp2 = quat.create(); + + return function (out, a, b, c, d, t) { + quat.slerp(temp1, a, d, t); + quat.slerp(temp2, b, c, t); + quat.slerp(out, temp1, temp2, 2 * t * (1 - t)); + + return out; + }; + }()); + + /** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate inverse of + * @returns {quat} out + */ + quat.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; + }; + + /** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate conjugate of + * @returns {quat} out + */ + quat.conjugate = function (out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Calculates the length of a quat + * + * @param {quat} a vector to calculate length of + * @returns {Number} length of a + * @function + */ + quat.length = vec4.length; + + /** + * Alias for {@link quat.length} + * @function + */ + quat.len = quat.length; + + /** + * Calculates the squared length of a quat + * + * @param {quat} a vector to calculate squared length of + * @returns {Number} squared length of a + * @function + */ + quat.squaredLength = vec4.squaredLength; + + /** + * Alias for {@link quat.squaredLength} + * @function + */ + quat.sqrLen = quat.squaredLength; + + /** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quaternion to normalize + * @returns {quat} out + * @function + */ + quat.normalize = vec4.normalize; + + /** + * Creates a quaternion from the given 3x3 rotation matrix. + * + * NOTE: The resultant quaternion is not normalized, so you should be sure + * to renormalize the quaternion yourself where necessary. + * + * @param {quat} out the receiving quaternion + * @param {mat3} m rotation matrix + * @returns {quat} out + * @function + */ + quat.fromMat3 = function(out, m) { + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + var fTrace = m[0] + m[4] + m[8]; + var fRoot; + + if ( fTrace > 0.0 ) { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = Math.sqrt(fTrace + 1.0); // 2w + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; // 1/(4w) + out[0] = (m[5]-m[7])*fRoot; + out[1] = (m[6]-m[2])*fRoot; + out[2] = (m[1]-m[3])*fRoot; + } else { + // |w| <= 1/2 + var i = 0; + if ( m[4] > m[0] ) + i = 1; + if ( m[8] > m[i*3+i] ) + i = 2; + var j = (i+1)%3; + var k = (i+2)%3; + + fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[j*3+k] - m[k*3+j]) * fRoot; + out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; + out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; + } + + return out; + }; + + /** + * Returns a string representation of a quatenion + * + * @param {quat} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + quat.str = function (a) { + return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; + }; + + module.exports = quat; + + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 3 Dimensional Vector + * @name vec3 + */ + var vec3 = {}; + + /** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ + vec3.create = function() { + var out = new glMatrix.ARRAY_TYPE(3); + out[0] = 0; + out[1] = 0; + out[2] = 0; + return out; + }; + + /** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {vec3} a vector to clone + * @returns {vec3} a new 3D vector + */ + vec3.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + }; + + /** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ + vec3.fromValues = function(x, y, z) { + var out = new glMatrix.ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; + }; + + /** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {vec3} a the source vector + * @returns {vec3} out + */ + vec3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + }; + + /** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ + vec3.set = function(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; + }; + + /** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; + }; + + /** + * Subtracts vector b from vector a + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; + }; + + /** + * Alias for {@link vec3.subtract} + * @function + */ + vec3.sub = vec3.subtract; + + /** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; + }; + + /** + * Alias for {@link vec3.multiply} + * @function + */ + vec3.mul = vec3.multiply; + + /** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; + }; + + /** + * Alias for {@link vec3.divide} + * @function + */ + vec3.div = vec3.divide; + + /** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; + }; + + /** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; + }; + + /** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec3} out + */ + vec3.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; + }; + + /** + * Adds two vec3's after scaling the second operand by a scalar value + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec3} out + */ + vec3.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + return out; + }; + + /** + * Calculates the euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} distance between a and b + */ + vec3.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x*x + y*y + z*z); + }; + + /** + * Alias for {@link vec3.distance} + * @function + */ + vec3.dist = vec3.distance; + + /** + * Calculates the squared euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} squared distance between a and b + */ + vec3.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x*x + y*y + z*z; + }; + + /** + * Alias for {@link vec3.squaredDistance} + * @function + */ + vec3.sqrDist = vec3.squaredDistance; + + /** + * Calculates the length of a vec3 + * + * @param {vec3} a vector to calculate length of + * @returns {Number} length of a + */ + vec3.length = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x*x + y*y + z*z); + }; + + /** + * Alias for {@link vec3.length} + * @function + */ + vec3.len = vec3.length; + + /** + * Calculates the squared length of a vec3 + * + * @param {vec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + vec3.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return x*x + y*y + z*z; + }; + + /** + * Alias for {@link vec3.squaredLength} + * @function + */ + vec3.sqrLen = vec3.squaredLength; + + /** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to negate + * @returns {vec3} out + */ + vec3.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; + }; + + /** + * Returns the inverse of the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to invert + * @returns {vec3} out + */ + vec3.inverse = function(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + out[2] = 1.0 / a[2]; + return out; + }; + + /** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to normalize + * @returns {vec3} out + */ + vec3.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + var len = x*x + y*y + z*z; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; + }; + + /** + * Calculates the dot product of two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} dot product of a and b + */ + vec3.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + }; + + /** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.cross = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; + }; + + /** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ + vec3.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; + }; + + /** + * Performs a hermite interpolation with two control points + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {vec3} c the third operand + * @param {vec3} d the fourth operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ + vec3.hermite = function (out, a, b, c, d, t) { + var factorTimes2 = t * t, + factor1 = factorTimes2 * (2 * t - 3) + 1, + factor2 = factorTimes2 * (t - 2) + t, + factor3 = factorTimes2 * (t - 1), + factor4 = factorTimes2 * (3 - 2 * t); + + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + + return out; + }; + + /** + * Performs a bezier interpolation with two control points + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {vec3} c the third operand + * @param {vec3} d the fourth operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ + vec3.bezier = function (out, a, b, c, d, t) { + var inverseFactor = 1 - t, + inverseFactorTimesTwo = inverseFactor * inverseFactor, + factorTimes2 = t * t, + factor1 = inverseFactorTimesTwo * inverseFactor, + factor2 = 3 * t * inverseFactorTimesTwo, + factor3 = 3 * factorTimes2 * inverseFactor, + factor4 = factorTimes2 * t; + + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + + return out; + }; + + /** + * Generates a random vector with the given scale + * + * @param {vec3} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec3} out + */ + vec3.random = function (out, scale) { + scale = scale || 1.0; + + var r = glMatrix.RANDOM() * 2.0 * Math.PI; + var z = (glMatrix.RANDOM() * 2.0) - 1.0; + var zScale = Math.sqrt(1.0-z*z) * scale; + + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale; + return out; + }; + + /** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ + vec3.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15]; + w = w || 1.0; + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + return out; + }; + + /** + * Transforms the vec3 with a mat3. + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m the 3x3 matrix to transform with + * @returns {vec3} out + */ + vec3.transformMat3 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = x * m[0] + y * m[3] + z * m[6]; + out[1] = x * m[1] + y * m[4] + z * m[7]; + out[2] = x * m[2] + y * m[5] + z * m[8]; + return out; + }; + + /** + * Transforms the vec3 with a quat + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec3} out + */ + vec3.transformQuat = function(out, a, q) { + // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations + + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; + }; + + /** + * Rotate a 3D vector around the x-axis + * @param {vec3} out The receiving vec3 + * @param {vec3} a The vec3 point to rotate + * @param {vec3} b The origin of the rotation + * @param {Number} c The angle of rotation + * @returns {vec3} out + */ + vec3.rotateX = function(out, a, b, c){ + var p = [], r=[]; + //Translate point to the origin + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; + + //perform rotation + r[0] = p[0]; + r[1] = p[1]*Math.cos(c) - p[2]*Math.sin(c); + r[2] = p[1]*Math.sin(c) + p[2]*Math.cos(c); + + //translate to correct position + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + + return out; + }; + + /** + * Rotate a 3D vector around the y-axis + * @param {vec3} out The receiving vec3 + * @param {vec3} a The vec3 point to rotate + * @param {vec3} b The origin of the rotation + * @param {Number} c The angle of rotation + * @returns {vec3} out + */ + vec3.rotateY = function(out, a, b, c){ + var p = [], r=[]; + //Translate point to the origin + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; + + //perform rotation + r[0] = p[2]*Math.sin(c) + p[0]*Math.cos(c); + r[1] = p[1]; + r[2] = p[2]*Math.cos(c) - p[0]*Math.sin(c); + + //translate to correct position + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + + return out; + }; + + /** + * Rotate a 3D vector around the z-axis + * @param {vec3} out The receiving vec3 + * @param {vec3} a The vec3 point to rotate + * @param {vec3} b The origin of the rotation + * @param {Number} c The angle of rotation + * @returns {vec3} out + */ + vec3.rotateZ = function(out, a, b, c){ + var p = [], r=[]; + //Translate point to the origin + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; + + //perform rotation + r[0] = p[0]*Math.cos(c) - p[1]*Math.sin(c); + r[1] = p[0]*Math.sin(c) + p[1]*Math.cos(c); + r[2] = p[2]; + + //translate to correct position + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + + return out; + }; + + /** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + vec3.forEach = (function() { + var vec = vec3.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 3; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + + return a; + }; + })(); + + /** + * Get the angle between two 3D vectors + * @param {vec3} a The first operand + * @param {vec3} b The second operand + * @returns {Number} The angle in radians + */ + vec3.angle = function(a, b) { + + var tempA = vec3.fromValues(a[0], a[1], a[2]); + var tempB = vec3.fromValues(b[0], b[1], b[2]); + + vec3.normalize(tempA, tempA); + vec3.normalize(tempB, tempB); + + var cosine = vec3.dot(tempA, tempB); + + if(cosine > 1.0){ + return 0; + } else { + return Math.acos(cosine); + } + }; + + /** + * Returns a string representation of a vector + * + * @param {vec3} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + vec3.str = function (a) { + return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; + }; + + module.exports = vec3; + + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 4 Dimensional Vector + * @name vec4 + */ + var vec4 = {}; + + /** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ + vec4.create = function() { + var out = new glMatrix.ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + return out; + }; + + /** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {vec4} a vector to clone + * @returns {vec4} a new 4D vector + */ + vec4.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ + vec4.fromValues = function(x, y, z, w) { + var out = new glMatrix.ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + }; + + /** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {vec4} a the source vector + * @returns {vec4} out + */ + vec4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ + vec4.set = function(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + }; + + /** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; + }; + + /** + * Subtracts vector b from vector a + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; + }; + + /** + * Alias for {@link vec4.subtract} + * @function + */ + vec4.sub = vec4.subtract; + + /** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; + }; + + /** + * Alias for {@link vec4.multiply} + * @function + */ + vec4.mul = vec4.multiply; + + /** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; + }; + + /** + * Alias for {@link vec4.divide} + * @function + */ + vec4.div = vec4.divide; + + /** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; + }; + + /** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; + }; + + /** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec4} out + */ + vec4.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; + }; + + /** + * Adds two vec4's after scaling the second operand by a scalar value + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec4} out + */ + vec4.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + out[3] = a[3] + (b[3] * scale); + return out; + }; + + /** + * Calculates the euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} distance between a and b + */ + vec4.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); + }; + + /** + * Alias for {@link vec4.distance} + * @function + */ + vec4.dist = vec4.distance; + + /** + * Calculates the squared euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} squared distance between a and b + */ + vec4.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x*x + y*y + z*z + w*w; + }; + + /** + * Alias for {@link vec4.squaredDistance} + * @function + */ + vec4.sqrDist = vec4.squaredDistance; + + /** + * Calculates the length of a vec4 + * + * @param {vec4} a vector to calculate length of + * @returns {Number} length of a + */ + vec4.length = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); + }; + + /** + * Alias for {@link vec4.length} + * @function + */ + vec4.len = vec4.length; + + /** + * Calculates the squared length of a vec4 + * + * @param {vec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + vec4.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x*x + y*y + z*z + w*w; + }; + + /** + * Alias for {@link vec4.squaredLength} + * @function + */ + vec4.sqrLen = vec4.squaredLength; + + /** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to negate + * @returns {vec4} out + */ + vec4.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; + }; + + /** + * Returns the inverse of the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to invert + * @returns {vec4} out + */ + vec4.inverse = function(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + out[2] = 1.0 / a[2]; + out[3] = 1.0 / a[3]; + return out; + }; + + /** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to normalize + * @returns {vec4} out + */ + vec4.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = x * len; + out[1] = y * len; + out[2] = z * len; + out[3] = w * len; + } + return out; + }; + + /** + * Calculates the dot product of two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} dot product of a and b + */ + vec4.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + }; + + /** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec4} out + */ + vec4.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; + }; + + /** + * Generates a random vector with the given scale + * + * @param {vec4} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec4} out + */ + vec4.random = function (out, scale) { + scale = scale || 1.0; + + //TODO: This is a pretty awful way of doing this. Find something better. + out[0] = glMatrix.RANDOM(); + out[1] = glMatrix.RANDOM(); + out[2] = glMatrix.RANDOM(); + out[3] = glMatrix.RANDOM(); + vec4.normalize(out, out); + vec4.scale(out, out, scale); + return out; + }; + + /** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec4} out + */ + vec4.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; + }; + + /** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec4} out + */ + vec4.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + out[3] = a[3]; + return out; + }; + + /** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + vec4.forEach = (function() { + var vec = vec4.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 4; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + + return a; + }; + })(); + + /** + * Returns a string representation of a vector + * + * @param {vec4} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + vec4.str = function (a) { + return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; + }; + + module.exports = vec4; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ + + var glMatrix = __webpack_require__(8); + + /** + * @class 2 Dimensional Vector + * @name vec2 + */ + var vec2 = {}; + + /** + * Creates a new, empty vec2 + * + * @returns {vec2} a new 2D vector + */ + vec2.create = function() { + var out = new glMatrix.ARRAY_TYPE(2); + out[0] = 0; + out[1] = 0; + return out; + }; + + /** + * Creates a new vec2 initialized with values from an existing vector + * + * @param {vec2} a vector to clone + * @returns {vec2} a new 2D vector + */ + vec2.clone = function(a) { + var out = new glMatrix.ARRAY_TYPE(2); + out[0] = a[0]; + out[1] = a[1]; + return out; + }; + + /** + * Creates a new vec2 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} a new 2D vector + */ + vec2.fromValues = function(x, y) { + var out = new glMatrix.ARRAY_TYPE(2); + out[0] = x; + out[1] = y; + return out; + }; + + /** + * Copy the values from one vec2 to another + * + * @param {vec2} out the receiving vector + * @param {vec2} a the source vector + * @returns {vec2} out + */ + vec2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + return out; + }; + + /** + * Set the components of a vec2 to the given values + * + * @param {vec2} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} out + */ + vec2.set = function(out, x, y) { + out[0] = x; + out[1] = y; + return out; + }; + + /** + * Adds two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + return out; + }; + + /** + * Subtracts vector b from vector a + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + return out; + }; + + /** + * Alias for {@link vec2.subtract} + * @function + */ + vec2.sub = vec2.subtract; + + /** + * Multiplies two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; + }; + + /** + * Alias for {@link vec2.multiply} + * @function + */ + vec2.mul = vec2.multiply; + + /** + * Divides two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; + }; + + /** + * Alias for {@link vec2.divide} + * @function + */ + vec2.div = vec2.divide; + + /** + * Returns the minimum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + return out; + }; + + /** + * Returns the maximum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + return out; + }; + + /** + * Scales a vec2 by a scalar number + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec2} out + */ + vec2.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + return out; + }; + + /** + * Adds two vec2's after scaling the second operand by a scalar value + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec2} out + */ + vec2.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + return out; + }; + + /** + * Calculates the euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} distance between a and b + */ + vec2.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return Math.sqrt(x*x + y*y); + }; + + /** + * Alias for {@link vec2.distance} + * @function + */ + vec2.dist = vec2.distance; + + /** + * Calculates the squared euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} squared distance between a and b + */ + vec2.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return x*x + y*y; + }; + + /** + * Alias for {@link vec2.squaredDistance} + * @function + */ + vec2.sqrDist = vec2.squaredDistance; + + /** + * Calculates the length of a vec2 + * + * @param {vec2} a vector to calculate length of + * @returns {Number} length of a + */ + vec2.length = function (a) { + var x = a[0], + y = a[1]; + return Math.sqrt(x*x + y*y); + }; + + /** + * Alias for {@link vec2.length} + * @function + */ + vec2.len = vec2.length; + + /** + * Calculates the squared length of a vec2 + * + * @param {vec2} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + vec2.squaredLength = function (a) { + var x = a[0], + y = a[1]; + return x*x + y*y; + }; + + /** + * Alias for {@link vec2.squaredLength} + * @function + */ + vec2.sqrLen = vec2.squaredLength; + + /** + * Negates the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to negate + * @returns {vec2} out + */ + vec2.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + return out; + }; + + /** + * Returns the inverse of the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to invert + * @returns {vec2} out + */ + vec2.inverse = function(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + return out; + }; + + /** + * Normalize a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to normalize + * @returns {vec2} out + */ + vec2.normalize = function(out, a) { + var x = a[0], + y = a[1]; + var len = x*x + y*y; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + } + return out; + }; + + /** + * Calculates the dot product of two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} dot product of a and b + */ + vec2.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1]; + }; + + /** + * Computes the cross product of two vec2's + * Note that the cross product must by definition produce a 3D vector + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec3} out + */ + vec2.cross = function(out, a, b) { + var z = a[0] * b[1] - a[1] * b[0]; + out[0] = out[1] = 0; + out[2] = z; + return out; + }; + + /** + * Performs a linear interpolation between two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec2} out + */ + vec2.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + return out; + }; + + /** + * Generates a random vector with the given scale + * + * @param {vec2} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec2} out + */ + vec2.random = function (out, scale) { + scale = scale || 1.0; + var r = glMatrix.RANDOM() * 2.0 * Math.PI; + out[0] = Math.cos(r) * scale; + out[1] = Math.sin(r) * scale; + return out; + }; + + /** + * Transforms the vec2 with a mat2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat2 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[2] * y; + out[1] = m[1] * x + m[3] * y; + return out; + }; + + /** + * Transforms the vec2 with a mat2d + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2d} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat2d = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[2] * y + m[4]; + out[1] = m[1] * x + m[3] * y + m[5]; + return out; + }; + + /** + * Transforms the vec2 with a mat3 + * 3rd vector component is implicitly '1' + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat3} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat3 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[3] * y + m[6]; + out[1] = m[1] * x + m[4] * y + m[7]; + return out; + }; + + /** + * Transforms the vec2 with a mat4 + * 3rd vector component is implicitly '0' + * 4th vector component is implicitly '1' + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat4 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[4] * y + m[12]; + out[1] = m[1] * x + m[5] * y + m[13]; + return out; + }; + + /** + * Perform some operation over an array of vec2s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + vec2.forEach = (function() { + var vec = vec2.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 2; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; + } + + return a; + }; + })(); + + /** + * Returns a string representation of a vector + * + * @param {vec2} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + vec2.str = function (a) { + return 'vec2(' + a[0] + ', ' + a[1] + ')'; + }; + + module.exports = vec2; + + +/***/ }, +/* 17 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports["default"] = { + init: function init(arr, val) { + var l = arr.length; + while (l--) { + arr[l] = val; + } + }, + + /** + * Shuffles the content of an array + * @return {Array} the array itself shuffled + */ + shuffle: function shuffle(arr) { + var i = arr.length - 1, + j, + x; + for (i; i >= 0; i--) { + j = Math.floor(Math.random() * i); + x = arr[i]; + arr[i] = arr[j]; + arr[j] = x; + } + return arr; + }, + + toPointList: function toPointList(arr) { + var i, + j, + row = [], + rows = []; + for (i = 0; i < arr.length; i++) { + row = []; + for (j = 0; j < arr[i].length; j++) { + row[j] = arr[i][j]; + } + rows[i] = "[" + row.join(",") + "]"; + } + return "[" + rows.join(",\r\n") + "]"; + }, + + /** + * returns the elements which's score is bigger than the threshold + * @return {Array} the reduced array + */ + threshold: function threshold(arr, _threshold, scoreFunc) { + var i, + queue = []; + for (i = 0; i < arr.length; i++) { + if (scoreFunc.apply(arr, [arr[i]]) >= _threshold) { + queue.push(arr[i]); + } + } + return queue; + }, + + maxIndex: function maxIndex(arr) { + var i, + max = 0; + for (i = 0; i < arr.length; i++) { + if (arr[i] > arr[max]) { + max = i; + } + } + return max; + }, + + max: function max(arr) { + var i, + max = 0; + for (i = 0; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + } + return max; + }, + + sum: function sum(arr) { + var length = arr.length, + sum = 0; + + while (length--) { + sum += arr[length]; + } + return sum; + } + }; + module.exports = exports["default"]; + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _image_wrapper = __webpack_require__(3); + + var _image_wrapper2 = _interopRequireDefault(_image_wrapper); + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var _rasterizer = __webpack_require__(19); + + var _rasterizer2 = _interopRequireDefault(_rasterizer); + + var _tracer = __webpack_require__(20); + + var _tracer2 = _interopRequireDefault(_tracer); + + var _skeletonizer2 = __webpack_require__(21); + + var _skeletonizer3 = _interopRequireDefault(_skeletonizer2); + + var _array_helper = __webpack_require__(17); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + var _image_debug = __webpack_require__(22); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + var _glMatrix = __webpack_require__(7); + + var _glMatrix2 = _interopRequireDefault(_glMatrix); + + var _config, + _currentImageWrapper, + _skelImageWrapper, + _subImageWrapper, + _labelImageWrapper, + _patchGrid, + _patchLabelGrid, + _imageToPatchGrid, + _binaryImageWrapper, + _patchSize, + _canvasContainer = { + ctx: { + binary: null + }, + dom: { + binary: null + } + }, + _numPatches = { x: 0, y: 0 }, + _inputImageWrapper, + _skeletonizer, + vec2 = _glMatrix2['default'].vec2, + mat2 = _glMatrix2['default'].mat2; + + function initBuffers() { + var skeletonImageData; + + if (_config.halfSample) { + _currentImageWrapper = new _image_wrapper2['default']({ + x: _inputImageWrapper.size.x / 2 | 0, + y: _inputImageWrapper.size.y / 2 | 0 + }); + } else { + _currentImageWrapper = _inputImageWrapper; + } + + _patchSize = _cv_utils2['default'].calculatePatchSize(_config.patchSize, _currentImageWrapper.size); + + _numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0; + _numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0; + + _binaryImageWrapper = new _image_wrapper2['default'](_currentImageWrapper.size, undefined, Uint8Array, false); + + _labelImageWrapper = new _image_wrapper2['default'](_patchSize, undefined, Array, true); + + skeletonImageData = new ArrayBuffer(64 * 1024); + _subImageWrapper = new _image_wrapper2['default'](_patchSize, new Uint8Array(skeletonImageData, 0, _patchSize.x * _patchSize.y)); + _skelImageWrapper = new _image_wrapper2['default'](_patchSize, new Uint8Array(skeletonImageData, _patchSize.x * _patchSize.y * 3, _patchSize.x * _patchSize.y), undefined, true); + _skeletonizer = (0, _skeletonizer3['default'])(typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : global, { + size: _patchSize.x + }, skeletonImageData); + + _imageToPatchGrid = new _image_wrapper2['default']({ + x: _currentImageWrapper.size.x / _subImageWrapper.size.x | 0, + y: _currentImageWrapper.size.y / _subImageWrapper.size.y | 0 + }, undefined, Array, true); + _patchGrid = new _image_wrapper2['default'](_imageToPatchGrid.size, undefined, undefined, true); + _patchLabelGrid = new _image_wrapper2['default'](_imageToPatchGrid.size, undefined, Int32Array, true); + } + + function initCanvas() { + if (_config.useWorker || typeof document === 'undefined') { + return; + } + _canvasContainer.dom.binary = document.createElement("canvas"); + _canvasContainer.dom.binary.className = "binaryBuffer"; + if (_config.showCanvas === true) { + document.querySelector("#debug").appendChild(_canvasContainer.dom.binary); + } + _canvasContainer.ctx.binary = _canvasContainer.dom.binary.getContext("2d"); + _canvasContainer.dom.binary.width = _binaryImageWrapper.size.x; + _canvasContainer.dom.binary.height = _binaryImageWrapper.size.y; + } + + /** + * Creates a bounding box which encloses all the given patches + * @returns {Array} The minimal bounding box + */ + function boxFromPatches(patches) { + var overAvg, + i, + j, + patch, + transMat, + minx = _binaryImageWrapper.size.x, + miny = _binaryImageWrapper.size.y, + maxx = -_binaryImageWrapper.size.x, + maxy = -_binaryImageWrapper.size.y, + box, + scale; + + // draw all patches which are to be taken into consideration + overAvg = 0; + for (i = 0; i < patches.length; i++) { + patch = patches[i]; + overAvg += patch.rad; + if (_config.showPatches) { + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "red" }); + } + } + + overAvg /= patches.length; + overAvg = (overAvg * 180 / Math.PI + 90) % 180 - 90; + if (overAvg < 0) { + overAvg += 180; + } + + overAvg = (180 - overAvg) * Math.PI / 180; + transMat = mat2.clone([Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]); + + // iterate over patches and rotate by angle + for (i = 0; i < patches.length; i++) { + patch = patches[i]; + for (j = 0; j < 4; j++) { + vec2.transformMat2(patch.box[j], patch.box[j], transMat); + } + + if (_config.boxFromPatches.showTransformed) { + _image_debug2['default'].drawPath(patch.box, { x: 0, y: 1 }, _canvasContainer.ctx.binary, { color: '#99ff00', lineWidth: 2 }); + } + } + + // find bounding box + for (i = 0; i < patches.length; i++) { + patch = patches[i]; + for (j = 0; j < 4; j++) { + if (patch.box[j][0] < minx) { + minx = patch.box[j][0]; + } + if (patch.box[j][0] > maxx) { + maxx = patch.box[j][0]; + } + if (patch.box[j][1] < miny) { + miny = patch.box[j][1]; + } + if (patch.box[j][1] > maxy) { + maxy = patch.box[j][1]; + } + } + } + + box = [[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy]]; + + if (_config.boxFromPatches.showTransformedBox) { + _image_debug2['default'].drawPath(box, { x: 0, y: 1 }, _canvasContainer.ctx.binary, { color: '#ff0000', lineWidth: 2 }); + } + + scale = _config.halfSample ? 2 : 1; + // reverse rotation; + transMat = mat2.invert(transMat, transMat); + for (j = 0; j < 4; j++) { + vec2.transformMat2(box[j], box[j], transMat); + } + + if (_config.boxFromPatches.showBB) { + _image_debug2['default'].drawPath(box, { x: 0, y: 1 }, _canvasContainer.ctx.binary, { color: '#ff0000', lineWidth: 2 }); + } + + for (j = 0; j < 4; j++) { + vec2.scale(box[j], box[j], scale); + } + + return box; + } + + /** + * Creates a binary image of the current image + */ + function binarizeImage() { + _cv_utils2['default'].otsuThreshold(_currentImageWrapper, _binaryImageWrapper); + _binaryImageWrapper.zeroBorder(); + if (_config.showCanvas) { + _binaryImageWrapper.show(_canvasContainer.dom.binary, 255); + } + } + + /** + * Iterate over the entire image + * extract patches + */ + function findPatches() { + var i, + j, + x, + y, + moments, + patchesFound = [], + rasterizer, + rasterResult, + patch; + for (i = 0; i < _numPatches.x; i++) { + for (j = 0; j < _numPatches.y; j++) { + x = _subImageWrapper.size.x * i; + y = _subImageWrapper.size.y * j; + + // seperate parts + skeletonize(x, y); + + // Rasterize, find individual bars + _skelImageWrapper.zeroBorder(); + _array_helper2['default'].init(_labelImageWrapper.data, 0); + rasterizer = _rasterizer2['default'].create(_skelImageWrapper, _labelImageWrapper); + rasterResult = rasterizer.rasterize(0); + + if (_config.showLabels) { + _labelImageWrapper.overlay(_canvasContainer.dom.binary, Math.floor(360 / rasterResult.count), { x: x, y: y }); + } + + // calculate moments from the skeletonized patch + moments = _labelImageWrapper.moments(rasterResult.count); + + // extract eligible patches + patchesFound = patchesFound.concat(describePatch(moments, [i, j], x, y)); + } + } + + if (_config.showFoundPatches) { + for (i = 0; i < patchesFound.length; i++) { + patch = patchesFound[i]; + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "#99ff00", lineWidth: 2 }); + } + } + + return patchesFound; + } + + /** + * Finds those connected areas which contain at least 6 patches + * and returns them ordered DESC by the number of contained patches + * @param {Number} maxLabel + */ + function findBiggestConnectedAreas(maxLabel) { + var i, + sum, + labelHist = [], + topLabels = []; + + for (i = 0; i < maxLabel; i++) { + labelHist.push(0); + } + sum = _patchLabelGrid.data.length; + while (sum--) { + if (_patchLabelGrid.data[sum] > 0) { + labelHist[_patchLabelGrid.data[sum] - 1]++; + } + } + + labelHist = labelHist.map(function (val, idx) { + return { + val: val, + label: idx + 1 + }; + }); + + labelHist.sort(function (a, b) { + return b.val - a.val; + }); + + // extract top areas with at least 6 patches present + topLabels = labelHist.filter(function (el) { + return el.val >= 5; + }); + + return topLabels; + } + + /** + * + */ + function findBoxes(topLabels, maxLabel) { + var i, + j, + sum, + patches = [], + patch, + box, + boxes = [], + hsv = [0, 1, 1], + rgb = [0, 0, 0]; + + for (i = 0; i < topLabels.length; i++) { + sum = _patchLabelGrid.data.length; + patches.length = 0; + while (sum--) { + if (_patchLabelGrid.data[sum] === topLabels[i].label) { + patch = _imageToPatchGrid.data[sum]; + patches.push(patch); + } + } + box = boxFromPatches(patches); + if (box) { + boxes.push(box); + + // draw patch-labels if requested + if (_config.showRemainingPatchLabels) { + for (j = 0; j < patches.length; j++) { + patch = patches[j]; + hsv[0] = topLabels[i].label / (maxLabel + 1) * 360; + _cv_utils2['default'].hsv2rgb(hsv, rgb); + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "rgb(" + rgb.join(",") + ")", lineWidth: 2 }); + } + } + } + } + return boxes; + } + + /** + * Find similar moments (via cluster) + * @param {Object} moments + */ + function similarMoments(moments) { + var clusters = _cv_utils2['default'].cluster(moments, 0.90); + var topCluster = _cv_utils2['default'].topGeneric(clusters, 1, function (e) { + return e.getPoints().length; + }); + var points = [], + result = []; + if (topCluster.length === 1) { + points = topCluster[0].item.getPoints(); + for (var i = 0; i < points.length; i++) { + result.push(points[i].point); + } + } + return result; + } + + function skeletonize(x, y) { + _binaryImageWrapper.subImageAsCopy(_subImageWrapper, _cv_utils2['default'].imageRef(x, y)); + _skeletonizer.skeletonize(); + + // Show skeleton if requested + if (_config.showSkeleton) { + _skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, _cv_utils2['default'].imageRef(x, y)); + } + } + + /** + * Extracts and describes those patches which seem to contain a barcode pattern + * @param {Array} moments + * @param {Object} patchPos, + * @param {Number} x + * @param {Number} y + * @returns {Array} list of patches + */ + function describePatch(moments, patchPos, x, y) { + var k, + avg, + eligibleMoments = [], + matchingMoments, + patch, + patchesFound = [], + minComponentWeight = Math.ceil(_patchSize.x / 3); + + if (moments.length >= 2) { + // only collect moments which's area covers at least minComponentWeight pixels. + for (k = 0; k < moments.length; k++) { + if (moments[k].m00 > minComponentWeight) { + eligibleMoments.push(moments[k]); + } + } + + // if at least 2 moments are found which have at least minComponentWeights covered + if (eligibleMoments.length >= 2) { + matchingMoments = similarMoments(eligibleMoments); + avg = 0; + // determine the similarity of the moments + for (k = 0; k < matchingMoments.length; k++) { + avg += matchingMoments[k].rad; + } + + // Only two of the moments are allowed not to fit into the equation + // add the patch to the set + if (matchingMoments.length > 1 && matchingMoments.length >= eligibleMoments.length / 4 * 3 && matchingMoments.length > moments.length / 4) { + avg /= matchingMoments.length; + patch = { + index: patchPos[1] * _numPatches.x + patchPos[0], + pos: { + x: x, + y: y + }, + box: [vec2.clone([x, y]), vec2.clone([x + _subImageWrapper.size.x, y]), vec2.clone([x + _subImageWrapper.size.x, y + _subImageWrapper.size.y]), vec2.clone([x, y + _subImageWrapper.size.y])], + moments: matchingMoments, + rad: avg, + vec: vec2.clone([Math.cos(avg), Math.sin(avg)]) + }; + patchesFound.push(patch); + } + } + } + return patchesFound; + } + + /** + * finds patches which are connected and share the same orientation + * @param {Object} patchesFound + */ + function rasterizeAngularSimilarity(patchesFound) { + var label = 0, + threshold = 0.95, + currIdx = 0, + j, + patch, + hsv = [0, 1, 1], + rgb = [0, 0, 0]; + + function notYetProcessed() { + var i; + for (i = 0; i < _patchLabelGrid.data.length; i++) { + if (_patchLabelGrid.data[i] === 0 && _patchGrid.data[i] === 1) { + return i; + } + } + return _patchLabelGrid.length; + } + + function trace(currentIdx) { + var x, + y, + currentPatch, + idx, + dir, + current = { + x: currentIdx % _patchLabelGrid.size.x, + y: currentIdx / _patchLabelGrid.size.x | 0 + }, + similarity; + + if (currentIdx < _patchLabelGrid.data.length) { + currentPatch = _imageToPatchGrid.data[currentIdx]; + // assign label + _patchLabelGrid.data[currentIdx] = label; + for (dir = 0; dir < _tracer2['default'].searchDirections.length; dir++) { + y = current.y + _tracer2['default'].searchDirections[dir][0]; + x = current.x + _tracer2['default'].searchDirections[dir][1]; + idx = y * _patchLabelGrid.size.x + x; + + // continue if patch empty + if (_patchGrid.data[idx] === 0) { + _patchLabelGrid.data[idx] = Number.MAX_VALUE; + continue; + } + + if (_patchLabelGrid.data[idx] === 0) { + similarity = Math.abs(vec2.dot(_imageToPatchGrid.data[idx].vec, currentPatch.vec)); + if (similarity > threshold) { + trace(idx); + } + } + } + } + } + + // prepare for finding the right patches + _array_helper2['default'].init(_patchGrid.data, 0); + _array_helper2['default'].init(_patchLabelGrid.data, 0); + _array_helper2['default'].init(_imageToPatchGrid.data, null); + + for (j = 0; j < patchesFound.length; j++) { + patch = patchesFound[j]; + _imageToPatchGrid.data[patch.index] = patch; + _patchGrid.data[patch.index] = 1; + } + + // rasterize the patches found to determine area + _patchGrid.zeroBorder(); + + while ((currIdx = notYetProcessed()) < _patchLabelGrid.data.length) { + label++; + trace(currIdx); + } + + // draw patch-labels if requested + if (_config.showPatchLabels) { + for (j = 0; j < _patchLabelGrid.data.length; j++) { + if (_patchLabelGrid.data[j] > 0 && _patchLabelGrid.data[j] <= label) { + patch = _imageToPatchGrid.data[j]; + hsv[0] = _patchLabelGrid.data[j] / (label + 1) * 360; + _cv_utils2['default'].hsv2rgb(hsv, rgb); + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "rgb(" + rgb.join(",") + ")", lineWidth: 2 }); + } + } + } + + return label; + } + + exports['default'] = { + init: function init(inputImageWrapper, config) { + _config = config; + _inputImageWrapper = inputImageWrapper; + + initBuffers(); + initCanvas(); + }, + + locate: function locate() { + var patchesFound, topLabels, boxes; + + if (_config.halfSample) { + _cv_utils2['default'].halfSample(_inputImageWrapper, _currentImageWrapper); + } + + binarizeImage(); + patchesFound = findPatches(); + // return unless 5% or more patches are found + if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) { + return null; + } + + // rasterrize area by comparing angular similarity; + var maxLabel = rasterizeAngularSimilarity(patchesFound); + if (maxLabel < 1) { + return null; + } + + // search for area with the most patches (biggest connected area) + topLabels = findBiggestConnectedAreas(maxLabel); + if (topLabels.length === 0) { + return null; + } + + boxes = findBoxes(topLabels, maxLabel); + return boxes; + }, + + checkImageConstraints: function checkImageConstraints(inputStream, config) { + var patchSize, + width = inputStream.getWidth(), + height = inputStream.getHeight(), + halfSample = config.halfSample ? 0.5 : 1, + size, + area; + + // calculate width and height based on area + if (inputStream.getConfig().area) { + area = _cv_utils2['default'].computeImageArea(width, height, inputStream.getConfig().area); + inputStream.setTopRight({ x: area.sx, y: area.sy }); + inputStream.setCanvasSize({ x: width, y: height }); + width = area.sw; + height = area.sh; + } + + size = { + x: Math.floor(width * halfSample), + y: Math.floor(height * halfSample) + }; + + patchSize = _cv_utils2['default'].calculatePatchSize(config.patchSize, size); + console.log("Patch-Size: " + JSON.stringify(patchSize)); + + inputStream.setWidth(Math.floor(Math.floor(size.x / patchSize.x) * (1 / halfSample) * patchSize.x)); + inputStream.setHeight(Math.floor(Math.floor(size.y / patchSize.y) * (1 / halfSample) * patchSize.y)); + + if (inputStream.getWidth() % patchSize.x === 0 && inputStream.getHeight() % patchSize.y === 0) { + return true; + } + + throw new Error("Image dimensions do not comply with the current settings: Width (" + width + " )and height (" + height + ") must a multiple of " + patchSize.x); + } + }; + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _tracer = __webpack_require__(20); + + var _tracer2 = _interopRequireDefault(_tracer); + + /** + * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization + */ + var Rasterizer = { + createContour2D: function createContour2D() { + return { + dir: null, + index: null, + firstVertex: null, + insideContours: null, + nextpeer: null, + prevpeer: null + }; + }, + CONTOUR_DIR: { + CW_DIR: 0, + CCW_DIR: 1, + UNKNOWN_DIR: 2 + }, + DIR: { + OUTSIDE_EDGE: -32767, + INSIDE_EDGE: -32766 + }, + create: function create(imageWrapper, labelWrapper) { + var imageData = imageWrapper.data, + labelData = labelWrapper.data, + width = imageWrapper.size.x, + height = imageWrapper.size.y, + tracer = _tracer2["default"].create(imageWrapper, labelWrapper); + + return { + rasterize: function rasterize(depthlabel) { + var color, + bc, + lc, + labelindex, + cx, + cy, + colorMap = [], + vertex, + p, + cc, + sc, + pos, + connectedCount = 0, + i; + + for (i = 0; i < 400; i++) { + colorMap[i] = 0; + } + + colorMap[0] = imageData[0]; + cc = null; + for (cy = 1; cy < height - 1; cy++) { + labelindex = 0; + bc = colorMap[0]; + for (cx = 1; cx < width - 1; cx++) { + pos = cy * width + cx; + if (labelData[pos] === 0) { + color = imageData[pos]; + if (color !== bc) { + if (labelindex === 0) { + lc = connectedCount + 1; + colorMap[lc] = color; + bc = color; + vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE); + if (vertex !== null) { + connectedCount++; + labelindex = lc; + p = Rasterizer.createContour2D(); + p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; + p.index = labelindex; + p.firstVertex = vertex; + p.nextpeer = cc; + p.insideContours = null; + if (cc !== null) { + cc.prevpeer = p; + } + cc = p; + } + } else { + vertex = tracer.contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex); + if (vertex !== null) { + p = Rasterizer.createContour2D(); + p.firstVertex = vertex; + p.insideContours = null; + if (depthlabel === 0) { + p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR; + } else { + p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; + } + p.index = depthlabel; + sc = cc; + while (sc !== null && sc.index !== labelindex) { + sc = sc.nextpeer; + } + if (sc !== null) { + p.nextpeer = sc.insideContours; + if (sc.insideContours !== null) { + sc.insideContours.prevpeer = p; + } + sc.insideContours = p; + } + } + } + } else { + labelData[pos] = labelindex; + } + } else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { + labelindex = 0; + if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { + bc = imageData[pos]; + } else { + bc = colorMap[0]; + } + } else { + labelindex = labelData[pos]; + bc = colorMap[labelindex]; + } + } + } + sc = cc; + while (sc !== null) { + sc.index = depthlabel; + sc = sc.nextpeer; + } + return { + cc: cc, + count: connectedCount + }; + }, + debug: { + drawContour: function drawContour(canvas, firstContour) { + var ctx = canvas.getContext("2d"), + pq = firstContour, + iq, + q, + p; + + ctx.strokeStyle = "red"; + ctx.fillStyle = "red"; + ctx.lineWidth = 1; + + if (pq !== null) { + iq = pq.insideContours; + } else { + iq = null; + } + + while (pq !== null) { + if (iq !== null) { + q = iq; + iq = iq.nextpeer; + } else { + q = pq; + pq = pq.nextpeer; + if (pq !== null) { + iq = pq.insideContours; + } else { + iq = null; + } + } + + switch (q.dir) { + case Rasterizer.CONTOUR_DIR.CW_DIR: + ctx.strokeStyle = "red"; + break; + case Rasterizer.CONTOUR_DIR.CCW_DIR: + ctx.strokeStyle = "blue"; + break; + case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR: + ctx.strokeStyle = "green"; + break; + } + + p = q.firstVertex; + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + do { + p = p.next; + ctx.lineTo(p.x, p.y); + } while (p !== q.firstVertex); + ctx.stroke(); + } + } + } + }; + } + }; + + exports["default"] = Rasterizer; + module.exports = exports["default"]; + +/***/ }, +/* 20 */ +/***/ function(module, exports) { + + /** + * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization + */ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var Tracer = { + searchDirections: [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]], + create: function create(imageWrapper, labelWrapper) { + var imageData = imageWrapper.data, + labelData = labelWrapper.data, + searchDirections = this.searchDirections, + width = imageWrapper.size.x, + pos; + + function _trace(current, color, label, edgelabel) { + var i, y, x; + + for (i = 0; i < 7; i++) { + y = current.cy + searchDirections[current.dir][0]; + x = current.cx + searchDirections[current.dir][1]; + pos = y * width + x; + if (imageData[pos] === color && (labelData[pos] === 0 || labelData[pos] === label)) { + labelData[pos] = label; + current.cy = y; + current.cx = x; + return true; + } else { + if (labelData[pos] === 0) { + labelData[pos] = edgelabel; + } + current.dir = (current.dir + 1) % 8; + } + } + return false; + } + + function vertex2D(x, y, dir) { + return { + dir: dir, + x: x, + y: y, + next: null, + prev: null + }; + } + + function _contourTracing(sy, sx, label, color, edgelabel) { + var Fv = null, + Cv, + P, + ldir, + current = { + cx: sx, + cy: sy, + dir: 0 + }; + + if (_trace(current, color, label, edgelabel)) { + Fv = vertex2D(sx, sy, current.dir); + Cv = Fv; + ldir = current.dir; + P = vertex2D(current.cx, current.cy, 0); + P.prev = Cv; + Cv.next = P; + P.next = null; + Cv = P; + do { + current.dir = (current.dir + 6) % 8; + _trace(current, color, label, edgelabel); + if (ldir !== current.dir) { + Cv.dir = current.dir; + P = vertex2D(current.cx, current.cy, 0); + P.prev = Cv; + Cv.next = P; + P.next = null; + Cv = P; + } else { + Cv.dir = ldir; + Cv.x = current.cx; + Cv.y = current.cy; + } + ldir = current.dir; + } while (current.cx !== sx || current.cy !== sy); + Fv.prev = Cv.prev; + Cv.prev.next = Fv; + } + return Fv; + } + + return { + trace: function trace(current, color, label, edgelabel) { + return _trace(current, color, label, edgelabel); + }, + contourTracing: function contourTracing(sy, sx, label, color, edgelabel) { + return _contourTracing(sy, sx, label, color, edgelabel); + } + }; + } + }; + + exports["default"] = Tracer; + module.exports = exports["default"]; + +/***/ }, +/* 21 */ +/***/ function(module, exports) { + + /* @preserve ASM BEGIN */ + /* eslint-disable eqeqeq*/ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + function Skeletonizer(stdlib, foreign, buffer) { + "use asm"; + + var images = new stdlib.Uint8Array(buffer), + size = foreign.size | 0, + imul = stdlib.Math.imul; + + function erode(inImagePtr, outImagePtr) { + inImagePtr = inImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var v = 0, + u = 0, + sum = 0, + yStart1 = 0, + yStart2 = 0, + xStart1 = 0, + xStart2 = 0, + offset = 0; + + for (v = 1; (v | 0) < (size - 1 | 0); v = v + 1 | 0) { + offset = offset + size | 0; + for (u = 1; (u | 0) < (size - 1 | 0); u = u + 1 | 0) { + yStart1 = offset - size | 0; + yStart2 = offset + size | 0; + xStart1 = u - 1 | 0; + xStart2 = u + 1 | 0; + sum = (images[inImagePtr + yStart1 + xStart1 | 0] | 0) + (images[inImagePtr + yStart1 + xStart2 | 0] | 0) + (images[inImagePtr + offset + u | 0] | 0) + (images[inImagePtr + yStart2 + xStart1 | 0] | 0) + (images[inImagePtr + yStart2 + xStart2 | 0] | 0) | 0; + if ((sum | 0) == (5 | 0)) { + images[outImagePtr + offset + u | 0] = 1; + } else { + images[outImagePtr + offset + u | 0] = 0; + } + } + } + return; + } + + function subtract(aImagePtr, bImagePtr, outImagePtr) { + aImagePtr = aImagePtr | 0; + bImagePtr = bImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[outImagePtr + length | 0] = (images[aImagePtr + length | 0] | 0) - (images[bImagePtr + length | 0] | 0) | 0; + } + } + + function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) { + aImagePtr = aImagePtr | 0; + bImagePtr = bImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[outImagePtr + length | 0] = images[aImagePtr + length | 0] | 0 | (images[bImagePtr + length | 0] | 0) | 0; + } + } + + function countNonZero(imagePtr) { + imagePtr = imagePtr | 0; + + var sum = 0, + length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + sum = (sum | 0) + (images[imagePtr + length | 0] | 0) | 0; + } + + return sum | 0; + } + + function init(imagePtr, value) { + imagePtr = imagePtr | 0; + value = value | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[imagePtr + length | 0] = value; + } + } + + function dilate(inImagePtr, outImagePtr) { + inImagePtr = inImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var v = 0, + u = 0, + sum = 0, + yStart1 = 0, + yStart2 = 0, + xStart1 = 0, + xStart2 = 0, + offset = 0; + + for (v = 1; (v | 0) < (size - 1 | 0); v = v + 1 | 0) { + offset = offset + size | 0; + for (u = 1; (u | 0) < (size - 1 | 0); u = u + 1 | 0) { + yStart1 = offset - size | 0; + yStart2 = offset + size | 0; + xStart1 = u - 1 | 0; + xStart2 = u + 1 | 0; + sum = (images[inImagePtr + yStart1 + xStart1 | 0] | 0) + (images[inImagePtr + yStart1 + xStart2 | 0] | 0) + (images[inImagePtr + offset + u | 0] | 0) + (images[inImagePtr + yStart2 + xStart1 | 0] | 0) + (images[inImagePtr + yStart2 + xStart2 | 0] | 0) | 0; + if ((sum | 0) > (0 | 0)) { + images[outImagePtr + offset + u | 0] = 1; + } else { + images[outImagePtr + offset + u | 0] = 0; + } + } + } + return; + } + + function memcpy(srcImagePtr, dstImagePtr) { + srcImagePtr = srcImagePtr | 0; + dstImagePtr = dstImagePtr | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[dstImagePtr + length | 0] = images[srcImagePtr + length | 0] | 0; + } + } + + function zeroBorder(imagePtr) { + imagePtr = imagePtr | 0; + + var x = 0, + y = 0; + + for (x = 0; (x | 0) < (size - 1 | 0); x = x + 1 | 0) { + images[imagePtr + x | 0] = 0; + images[imagePtr + y | 0] = 0; + y = y + size - 1 | 0; + images[imagePtr + y | 0] = 0; + y = y + 1 | 0; + } + for (x = 0; (x | 0) < (size | 0); x = x + 1 | 0) { + images[imagePtr + y | 0] = 0; + y = y + 1 | 0; + } + } + + function skeletonize() { + var subImagePtr = 0, + erodedImagePtr = 0, + tempImagePtr = 0, + skelImagePtr = 0, + sum = 0, + done = 0; + + erodedImagePtr = imul(size, size) | 0; + tempImagePtr = erodedImagePtr + erodedImagePtr | 0; + skelImagePtr = tempImagePtr + erodedImagePtr | 0; + + // init skel-image + init(skelImagePtr, 0); + zeroBorder(subImagePtr); + + do { + erode(subImagePtr, erodedImagePtr); + dilate(erodedImagePtr, tempImagePtr); + subtract(subImagePtr, tempImagePtr, tempImagePtr); + bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr); + memcpy(erodedImagePtr, subImagePtr); + sum = countNonZero(subImagePtr) | 0; + done = (sum | 0) == 0 | 0; + } while (!done); + } + + return { + skeletonize: skeletonize + }; + } + + exports["default"] = Skeletonizer; + + /* eslint-enable eqeqeq*/ + /* @preserve ASM END */ + module.exports = exports["default"]; + +/***/ }, +/* 22 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports["default"] = { + drawRect: function drawRect(pos, size, ctx, style) { + ctx.strokeStyle = style.color; + ctx.fillStyle = style.color; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.strokeRect(pos.x, pos.y, size.x, size.y); + }, + drawPath: function drawPath(path, def, ctx, style) { + ctx.strokeStyle = style.color; + ctx.fillStyle = style.color; + ctx.lineWidth = style.lineWidth; + ctx.beginPath(); + ctx.moveTo(path[0][def.x], path[0][def.y]); + for (var j = 1; j < path.length; j++) { + ctx.lineTo(path[j][def.x], path[j][def.y]); + } + ctx.closePath(); + ctx.stroke(); + }, + drawImage: function drawImage(imageData, size, ctx) { + var canvasData = ctx.getImageData(0, 0, size.x, size.y), + data = canvasData.data, + imageDataPos = imageData.length, + canvasDataPos = data.length, + value; + + if (canvasDataPos / imageDataPos !== 4) { + return false; + } + while (imageDataPos--) { + value = imageData[imageDataPos]; + data[--canvasDataPos] = 255; + data[--canvasDataPos] = value; + data[--canvasDataPos] = value; + data[--canvasDataPos] = value; + } + ctx.putImageData(canvasData, 0, 0); + return true; + } + }; + module.exports = exports["default"]; + +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _bresenham = __webpack_require__(24); + + var _bresenham2 = _interopRequireDefault(_bresenham); + + var _image_debug = __webpack_require__(22); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + var _code_128_reader = __webpack_require__(25); + + var _code_128_reader2 = _interopRequireDefault(_code_128_reader); + + var _ean_reader = __webpack_require__(27); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + var _code_39_reader = __webpack_require__(28); + + var _code_39_reader2 = _interopRequireDefault(_code_39_reader); + + var _code_39_vin_reader = __webpack_require__(29); + + var _code_39_vin_reader2 = _interopRequireDefault(_code_39_vin_reader); + + var _codabar_reader = __webpack_require__(30); + + var _codabar_reader2 = _interopRequireDefault(_codabar_reader); + + var _upc_reader = __webpack_require__(31); + + var _upc_reader2 = _interopRequireDefault(_upc_reader); + + var _ean_8_reader = __webpack_require__(32); + + var _ean_8_reader2 = _interopRequireDefault(_ean_8_reader); + + var _upc_e_reader = __webpack_require__(33); + + var _upc_e_reader2 = _interopRequireDefault(_upc_e_reader); + + var _i2of5_reader = __webpack_require__(34); + + var _i2of5_reader2 = _interopRequireDefault(_i2of5_reader); + + var READERS = { + code_128_reader: _code_128_reader2['default'], + ean_reader: _ean_reader2['default'], + ean_8_reader: _ean_8_reader2['default'], + code_39_reader: _code_39_reader2['default'], + code_39_vin_reader: _code_39_vin_reader2['default'], + codabar_reader: _codabar_reader2['default'], + upc_reader: _upc_reader2['default'], + upc_e_reader: _upc_e_reader2['default'], + i2of5_reader: _i2of5_reader2['default'] + }; + exports['default'] = { + create: function create(config, inputImageWrapper) { + var _canvas = { + ctx: { + frequency: null, + pattern: null, + overlay: null + }, + dom: { + frequency: null, + pattern: null, + overlay: null + } + }, + _barcodeReaders = []; + + initCanvas(); + initReaders(); + initConfig(); + + function initCanvas() { + if (typeof document !== 'undefined') { + var $debug = document.querySelector("#debug.detection"); + _canvas.dom.frequency = document.querySelector("canvas.frequency"); + if (!_canvas.dom.frequency) { + _canvas.dom.frequency = document.createElement("canvas"); + _canvas.dom.frequency.className = "frequency"; + if ($debug) { + $debug.appendChild(_canvas.dom.frequency); + } + } + _canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d"); + + _canvas.dom.pattern = document.querySelector("canvas.patternBuffer"); + if (!_canvas.dom.pattern) { + _canvas.dom.pattern = document.createElement("canvas"); + _canvas.dom.pattern.className = "patternBuffer"; + if ($debug) { + $debug.appendChild(_canvas.dom.pattern); + } + } + _canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d"); + + _canvas.dom.overlay = document.querySelector("canvas.drawingBuffer"); + if (_canvas.dom.overlay) { + _canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d"); + } + } + } + + function initReaders() { + config.readers.forEach(function (readerConfig) { + var reader, + configuration = {}; + + if (typeof readerConfig === 'object') { + reader = readerConfig.format; + configuration = readerConfig.config; + } else if (typeof readerConfig === 'string') { + reader = readerConfig; + } + console.log("Before registering reader: ", reader); + _barcodeReaders.push(new READERS[reader](configuration)); + }); + console.log("Registered Readers: " + _barcodeReaders.map(function (reader) { + return JSON.stringify({ format: reader.FORMAT, config: reader.config }); + }).join(', ')); + } + + function initConfig() { + if (typeof document !== 'undefined') { + var i, + vis = [{ + node: _canvas.dom.frequency, + prop: config.showFrequency + }, { + node: _canvas.dom.pattern, + prop: config.showPattern + }]; + + for (i = 0; i < vis.length; i++) { + if (vis[i].prop === true) { + vis[i].node.style.display = "block"; + } else { + vis[i].node.style.display = "none"; + } + } + } + } + + /** + * extend the line on both ends + * @param {Array} line + * @param {Number} angle + */ + function getExtendedLine(line, angle, ext) { + function extendLine(amount) { + var extension = { + y: amount * Math.sin(angle), + x: amount * Math.cos(angle) + }; + + line[0].y -= extension.y; + line[0].x -= extension.x; + line[1].y += extension.y; + line[1].x += extension.x; + } + + // check if inside image + extendLine(ext); + while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0))) { + ext -= Math.ceil(ext / 2); + extendLine(-ext); + } + return line; + } + + function getLine(box) { + return [{ + x: (box[1][0] - box[0][0]) / 2 + box[0][0], + y: (box[1][1] - box[0][1]) / 2 + box[0][1] + }, { + x: (box[3][0] - box[2][0]) / 2 + box[2][0], + y: (box[3][1] - box[2][1]) / 2 + box[2][1] + }]; + } + + function tryDecode(line) { + var result = null, + i, + barcodeLine = _bresenham2['default'].getBarcodeLine(inputImageWrapper, line[0], line[1]); + + if (config.showFrequency) { + _image_debug2['default'].drawPath(line, { x: 'x', y: 'y' }, _canvas.ctx.overlay, { color: 'red', lineWidth: 3 }); + _bresenham2['default'].debug.printFrequency(barcodeLine.line, _canvas.dom.frequency); + } + _bresenham2['default'].toBinaryLine(barcodeLine); + if (config.showPattern) { + _bresenham2['default'].debug.printPattern(barcodeLine.line, _canvas.dom.pattern); + } + + for (i = 0; i < _barcodeReaders.length && result === null; i++) { + result = _barcodeReaders[i].decodePattern(barcodeLine.line); + } + if (result === null) { + return null; + } + return { + codeResult: result, + barcodeLine: barcodeLine + }; + } + + /** + * This method slices the given area apart and tries to detect a barcode-pattern + * for each slice. It returns the decoded barcode, or null if nothing was found + * @param {Array} box + * @param {Array} line + * @param {Number} lineAngle + */ + function tryDecodeBruteForce(box, line, lineAngle) { + var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow(box[1][1] - box[0][1], 2)), + i, + slices = 16, + result = null, + dir, + extension, + xdir = Math.sin(lineAngle), + ydir = Math.cos(lineAngle); + + for (i = 1; i < slices && result === null; i++) { + // move line perpendicular to angle + dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1); + extension = { + y: dir * xdir, + x: dir * ydir + }; + line[0].y += extension.x; + line[0].x -= extension.y; + line[1].y += extension.x; + line[1].x -= extension.y; + + result = tryDecode(line); + } + return result; + } + + function getLineLength(line) { + return Math.sqrt(Math.pow(Math.abs(line[1].y - line[0].y), 2) + Math.pow(Math.abs(line[1].x - line[0].x), 2)); + } + + /** + * With the help of the configured readers (Code128 or EAN) this function tries to detect a + * valid barcode pattern within the given area. + * @param {Object} box The area to search in + * @returns {Object} the result {codeResult, line, angle, pattern, threshold} + */ + function _decodeFromBoundingBox(box) { + var line, + lineAngle, + ctx = _canvas.ctx.overlay, + result, + lineLength; + + if (config.drawBoundingBox && ctx) { + _image_debug2['default'].drawPath(box, { x: 0, y: 1 }, ctx, { color: "blue", lineWidth: 2 }); + } + + line = getLine(box); + lineLength = getLineLength(line); + lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); + line = getExtendedLine(line, lineAngle, Math.floor(lineLength * 0.1)); + if (line === null) { + return null; + } + + result = tryDecode(line); + if (result === null) { + result = tryDecodeBruteForce(box, line, lineAngle); + } + + if (result === null) { + return null; + } + + if (result && config.drawScanline && ctx) { + _image_debug2['default'].drawPath(line, { x: 'x', y: 'y' }, ctx, { color: 'red', lineWidth: 3 }); + } + + return { + codeResult: result.codeResult, + line: line, + angle: lineAngle, + pattern: result.barcodeLine.line, + threshold: result.barcodeLine.threshold + }; + } + + return { + decodeFromBoundingBox: function decodeFromBoundingBox(box) { + return _decodeFromBoundingBox(box); + }, + decodeFromBoundingBoxes: function decodeFromBoundingBoxes(boxes) { + var i, result; + for (i = 0; i < boxes.length; i++) { + result = _decodeFromBoundingBox(boxes[i]); + if (result && result.codeResult) { + result.box = boxes[i]; + return result; + } + } + }, + setReaders: function setReaders(readers) { + config.readers = readers; + _barcodeReaders.length = 0; + initReaders(); + } + }; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var _image_wrapper = __webpack_require__(3); + + var _image_wrapper2 = _interopRequireDefault(_image_wrapper); + + var Bresenham = {}; + + var Slope = { + DIR: { + UP: 1, + DOWN: -1 + } + }; + /** + * Scans a line of the given image from point p1 to p2 and returns a result object containing + * gray-scale values (0-255) of the underlying pixels in addition to the min + * and max values. + * @param {Object} imageWrapper + * @param {Object} p1 The start point {x,y} + * @param {Object} p2 The end point {x,y} + * @returns {line, min, max} + */ + Bresenham.getBarcodeLine = function (imageWrapper, p1, p2) { + var x0 = p1.x | 0, + y0 = p1.y | 0, + x1 = p2.x | 0, + y1 = p2.y | 0, + steep = Math.abs(y1 - y0) > Math.abs(x1 - x0), + deltax, + deltay, + error, + ystep, + y, + tmp, + x, + line = [], + imageData = imageWrapper.data, + width = imageWrapper.size.x, + sum = 0, + val, + min = 255, + max = 0; + + function read(a, b) { + val = imageData[b * width + a]; + sum += val; + min = val < min ? val : min; + max = val > max ? val : max; + line.push(val); + } + + if (steep) { + tmp = x0; + x0 = y0; + y0 = tmp; + + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + + tmp = y0; + y0 = y1; + y1 = tmp; + } + deltax = x1 - x0; + deltay = Math.abs(y1 - y0); + error = deltax / 2 | 0; + y = y0; + ystep = y0 < y1 ? 1 : -1; + for (x = x0; x < x1; x++) { + if (steep) { + read(y, x); + } else { + read(x, y); + } + error = error - deltay; + if (error < 0) { + y = y + ystep; + error = error + deltax; + } + } + + return { + line: line, + min: min, + max: max + }; + }; + + Bresenham.toOtsuBinaryLine = function (result) { + var line = result.line, + image = new _image_wrapper2['default']({ x: line.length - 1, y: 1 }, line), + threshold = _cv_utils2['default'].determineOtsuThreshold(image, 5); + + line = _cv_utils2['default'].sharpenLine(line); + _cv_utils2['default'].thresholdImage(image, threshold); + + return { + line: line, + threshold: threshold + }; + }; + + /** + * Converts the result from getBarcodeLine into a binary representation + * also considering the frequency and slope of the signal for more robust results + * @param {Object} result {line, min, max} + */ + Bresenham.toBinaryLine = function (result) { + var min = result.min, + max = result.max, + line = result.line, + slope, + slope2, + center = min + (max - min) / 2, + extrema = [], + currentDir, + dir, + threshold = (max - min) / 12, + rThreshold = -threshold, + i, + j; + + // 1. find extrema + currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN; + extrema.push({ + pos: 0, + val: line[0] + }); + for (i = 0; i < line.length - 2; i++) { + slope = line[i + 1] - line[i]; + slope2 = line[i + 2] - line[i + 1]; + if (slope + slope2 < rThreshold && line[i + 1] < center * 1.5) { + dir = Slope.DIR.DOWN; + } else if (slope + slope2 > threshold && line[i + 1] > center * 0.5) { + dir = Slope.DIR.UP; + } else { + dir = currentDir; + } + + if (currentDir !== dir) { + extrema.push({ + pos: i, + val: line[i] + }); + currentDir = dir; + } + } + extrema.push({ + pos: line.length, + val: line[line.length - 1] + }); + + for (j = extrema[0].pos; j < extrema[1].pos; j++) { + line[j] = line[j] > center ? 0 : 1; + } + + // iterate over extrema and convert to binary based on avg between minmax + for (i = 1; i < extrema.length - 1; i++) { + if (extrema[i + 1].val > extrema[i].val) { + threshold = extrema[i].val + (extrema[i + 1].val - extrema[i].val) / 3 * 2 | 0; + } else { + threshold = extrema[i + 1].val + (extrema[i].val - extrema[i + 1].val) / 3 | 0; + } + + for (j = extrema[i].pos; j < extrema[i + 1].pos; j++) { + line[j] = line[j] > threshold ? 0 : 1; + } + } + + return { + line: line, + threshold: threshold + }; + }; + + /** + * Used for development only + */ + Bresenham.debug = { + printFrequency: function printFrequency(line, canvas) { + var i, + ctx = canvas.getContext("2d"); + canvas.width = line.length; + canvas.height = 256; + + ctx.beginPath(); + ctx.strokeStyle = "blue"; + for (i = 0; i < line.length; i++) { + ctx.moveTo(i, 255); + ctx.lineTo(i, 255 - line[i]); + } + ctx.stroke(); + ctx.closePath(); + }, + + printPattern: function printPattern(line, canvas) { + var ctx = canvas.getContext("2d"), + i; + + canvas.width = line.length; + ctx.fillColor = "black"; + for (i = 0; i < line.length; i++) { + if (line[i] === 1) { + ctx.fillRect(i, 0, 1, 100); + } + } + } + }; + + exports['default'] = Bresenham; + module.exports = exports['default']; + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _barcode_reader = __webpack_require__(26); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + function Code128Reader() { + _barcode_reader2["default"].call(this); + } + + var properties = { + CODE_SHIFT: { value: 98 }, + CODE_C: { value: 99 }, + CODE_B: { value: 100 }, + CODE_A: { value: 101 }, + START_CODE_A: { value: 103 }, + START_CODE_B: { value: 104 }, + START_CODE_C: { value: 105 }, + STOP_CODE: { value: 106 }, + MODULO: { value: 11 }, + CODE_PATTERN: { value: [[2, 1, 2, 2, 2, 2], [2, 2, 2, 1, 2, 2], [2, 2, 2, 2, 2, 1], [1, 2, 1, 2, 2, 3], [1, 2, 1, 3, 2, 2], [1, 3, 1, 2, 2, 2], [1, 2, 2, 2, 1, 3], [1, 2, 2, 3, 1, 2], [1, 3, 2, 2, 1, 2], [2, 2, 1, 2, 1, 3], [2, 2, 1, 3, 1, 2], [2, 3, 1, 2, 1, 2], [1, 1, 2, 2, 3, 2], [1, 2, 2, 1, 3, 2], [1, 2, 2, 2, 3, 1], [1, 1, 3, 2, 2, 2], [1, 2, 3, 1, 2, 2], [1, 2, 3, 2, 2, 1], [2, 2, 3, 2, 1, 1], [2, 2, 1, 1, 3, 2], [2, 2, 1, 2, 3, 1], [2, 1, 3, 2, 1, 2], [2, 2, 3, 1, 1, 2], [3, 1, 2, 1, 3, 1], [3, 1, 1, 2, 2, 2], [3, 2, 1, 1, 2, 2], [3, 2, 1, 2, 2, 1], [3, 1, 2, 2, 1, 2], [3, 2, 2, 1, 1, 2], [3, 2, 2, 2, 1, 1], [2, 1, 2, 1, 2, 3], [2, 1, 2, 3, 2, 1], [2, 3, 2, 1, 2, 1], [1, 1, 1, 3, 2, 3], [1, 3, 1, 1, 2, 3], [1, 3, 1, 3, 2, 1], [1, 1, 2, 3, 1, 3], [1, 3, 2, 1, 1, 3], [1, 3, 2, 3, 1, 1], [2, 1, 1, 3, 1, 3], [2, 3, 1, 1, 1, 3], [2, 3, 1, 3, 1, 1], [1, 1, 2, 1, 3, 3], [1, 1, 2, 3, 3, 1], [1, 3, 2, 1, 3, 1], [1, 1, 3, 1, 2, 3], [1, 1, 3, 3, 2, 1], [1, 3, 3, 1, 2, 1], [3, 1, 3, 1, 2, 1], [2, 1, 1, 3, 3, 1], [2, 3, 1, 1, 3, 1], [2, 1, 3, 1, 1, 3], [2, 1, 3, 3, 1, 1], [2, 1, 3, 1, 3, 1], [3, 1, 1, 1, 2, 3], [3, 1, 1, 3, 2, 1], [3, 3, 1, 1, 2, 1], [3, 1, 2, 1, 1, 3], [3, 1, 2, 3, 1, 1], [3, 3, 2, 1, 1, 1], [3, 1, 4, 1, 1, 1], [2, 2, 1, 4, 1, 1], [4, 3, 1, 1, 1, 1], [1, 1, 1, 2, 2, 4], [1, 1, 1, 4, 2, 2], [1, 2, 1, 1, 2, 4], [1, 2, 1, 4, 2, 1], [1, 4, 1, 1, 2, 2], [1, 4, 1, 2, 2, 1], [1, 1, 2, 2, 1, 4], [1, 1, 2, 4, 1, 2], [1, 2, 2, 1, 1, 4], [1, 2, 2, 4, 1, 1], [1, 4, 2, 1, 1, 2], [1, 4, 2, 2, 1, 1], [2, 4, 1, 2, 1, 1], [2, 2, 1, 1, 1, 4], [4, 1, 3, 1, 1, 1], [2, 4, 1, 1, 1, 2], [1, 3, 4, 1, 1, 1], [1, 1, 1, 2, 4, 2], [1, 2, 1, 1, 4, 2], [1, 2, 1, 2, 4, 1], [1, 1, 4, 2, 1, 2], [1, 2, 4, 1, 1, 2], [1, 2, 4, 2, 1, 1], [4, 1, 1, 2, 1, 2], [4, 2, 1, 1, 1, 2], [4, 2, 1, 2, 1, 1], [2, 1, 2, 1, 4, 1], [2, 1, 4, 1, 2, 1], [4, 1, 2, 1, 2, 1], [1, 1, 1, 1, 4, 3], [1, 1, 1, 3, 4, 1], [1, 3, 1, 1, 4, 1], [1, 1, 4, 1, 1, 3], [1, 1, 4, 3, 1, 1], [4, 1, 1, 1, 1, 3], [4, 1, 1, 3, 1, 1], [1, 1, 3, 1, 4, 1], [1, 1, 4, 1, 3, 1], [3, 1, 1, 1, 4, 1], [4, 1, 1, 1, 3, 1], [2, 1, 1, 4, 1, 2], [2, 1, 1, 2, 1, 4], [2, 1, 1, 2, 3, 2], [2, 3, 3, 1, 1, 1, 2]] }, + SINGLE_CODE_ERROR: { value: 1 }, + AVG_CODE_ERROR: { value: 0.5 }, + FORMAT: { value: "code_128", writeable: false } + }; + + Code128Reader.prototype = Object.create(_barcode_reader2["default"].prototype, properties); + Code128Reader.prototype.constructor = Code128Reader; + + Code128Reader.prototype._decodeCode = function (start) { + var counter = [0, 0, 0, 0, 0, 0], + i, + self = this, + offset = start, + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: start, + end: start + }, + code, + error, + normalized; + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + bestMatch.end = i; + return bestMatch; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + Code128Reader.prototype._findStart = function () { + var counter = [0, 0, 0, 0, 0, 0], + i, + self = this, + offset = self._nextSet(self._row), + isWhite = false, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + code, + error, + j, + sum, + normalized; + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + if (bestMatch.error < self.AVG_CODE_ERROR) { + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + } + + for (j = 0; j < 4; j++) { + counter[j] = counter[j + 2]; + } + counter[4] = 0; + counter[5] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + Code128Reader.prototype._decode = function () { + var self = this, + startInfo = self._findStart(), + code = null, + done = false, + result = [], + multiplier = 0, + checksum = 0, + codeset, + rawResult = [], + decodedCodes = [], + shiftNext = false, + unshift; + + if (startInfo === null) { + return null; + } + code = { + code: startInfo.code, + start: startInfo.start, + end: startInfo.end + }; + decodedCodes.push(code); + checksum = code.code; + switch (code.code) { + case self.START_CODE_A: + codeset = self.CODE_A; + break; + case self.START_CODE_B: + codeset = self.CODE_B; + break; + case self.START_CODE_C: + codeset = self.CODE_C; + break; + default: + return null; + } + + while (!done) { + unshift = shiftNext; + shiftNext = false; + code = self._decodeCode(code.end); + if (code !== null) { + if (code.code !== self.STOP_CODE) { + rawResult.push(code.code); + multiplier++; + checksum += multiplier * code.code; + } + decodedCodes.push(code); + + switch (codeset) { + case self.CODE_A: + if (code.code < 64) { + result.push(String.fromCharCode(32 + code.code)); + } else if (code.code < 96) { + result.push(String.fromCharCode(code.code - 64)); + } else { + switch (code.code) { + case self.CODE_SHIFT: + shiftNext = true; + codeset = self.CODE_B; + break; + case self.CODE_B: + codeset = self.CODE_B; + break; + case self.CODE_C: + codeset = self.CODE_C; + break; + case self.STOP_CODE: + done = true; + break; + } + } + break; + case self.CODE_B: + if (code.code < 96) { + result.push(String.fromCharCode(32 + code.code)); + } else { + switch (code.code) { + case self.CODE_SHIFT: + shiftNext = true; + codeset = self.CODE_A; + break; + case self.CODE_A: + codeset = self.CODE_A; + break; + case self.CODE_C: + codeset = self.CODE_C; + break; + case self.STOP_CODE: + done = true; + break; + } + } + break; + case self.CODE_C: + if (code.code < 100) { + result.push(code.code < 10 ? "0" + code.code : code.code); + } + switch (code.code) { + case self.CODE_A: + codeset = self.CODE_A; + break; + case self.CODE_B: + codeset = self.CODE_B; + break; + case self.STOP_CODE: + done = true; + break; + } + break; + } + } else { + done = true; + } + if (unshift) { + codeset = codeset === self.CODE_A ? self.CODE_B : self.CODE_A; + } + } + + if (code === null) { + return null; + } + + // find end bar + code.end = self._nextUnset(self._row, code.end); + if (!self._verifyTrailingWhitespace(code)) { + return null; + } + + // checksum + // Does not work correctly yet!!! startcode - endcode? + checksum -= multiplier * rawResult[rawResult.length - 1]; + if (checksum % 103 !== rawResult[rawResult.length - 1]) { + return null; + } + + if (!result.length) { + return null; + } + + // remove last code from result (checksum) + result.splice(result.length - 1, 1); + + return { + code: result.join(""), + start: startInfo.start, + end: code.end, + codeset: codeset, + startInfo: startInfo, + decodedCodes: decodedCodes, + endInfo: code + }; + }; + + _barcode_reader2["default"].prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start) / 2; + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; + }; + + exports["default"] = Code128Reader; + module.exports = exports["default"]; + +/***/ }, +/* 26 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + function BarcodeReader(config) { + this._row = []; + this.config = config || {}; + return this; + } + + BarcodeReader.prototype._nextUnset = function (line, start) { + var i; + + if (start === undefined) { + start = 0; + } + for (i = start; i < line.length; i++) { + if (!line[i]) { + return i; + } + } + return line.length; + }; + + BarcodeReader.prototype._matchPattern = function (counter, code) { + var i, + error = 0, + singleError = 0, + modulo = this.MODULO, + maxSingleError = this.SINGLE_CODE_ERROR || 1; + + for (i = 0; i < counter.length; i++) { + singleError = Math.abs(code[i] - counter[i]); + if (singleError > maxSingleError) { + return Number.MAX_VALUE; + } + error += singleError; + } + return error / modulo; + }; + + BarcodeReader.prototype._nextSet = function (line, offset) { + var i; + + offset = offset || 0; + for (i = offset; i < line.length; i++) { + if (line[i]) { + return i; + } + } + return line.length; + }; + + BarcodeReader.prototype._normalize = function (counter, modulo) { + var i, + self = this, + sum = 0, + ratio, + numOnes = 0, + normalized = [], + norm = 0; + + if (!modulo) { + modulo = self.MODULO; + } + for (i = 0; i < counter.length; i++) { + if (counter[i] === 1) { + numOnes++; + } else { + sum += counter[i]; + } + } + ratio = sum / (modulo - numOnes); + if (ratio > 1.0) { + for (i = 0; i < counter.length; i++) { + norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; + normalized.push(norm); + } + } else { + ratio = (sum + numOnes) / modulo; + for (i = 0; i < counter.length; i++) { + norm = counter[i] / ratio; + normalized.push(norm); + } + } + return normalized; + }; + + BarcodeReader.prototype._matchTrace = function (cmpCounter, epsilon) { + var counter = [], + i, + self = this, + offset = self._nextSet(self._row), + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0 + }, + error; + + if (cmpCounter) { + for (i = 0; i < cmpCounter.length; i++) { + counter.push(0); + } + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + error = self._matchPattern(counter, cmpCounter); + + if (error < epsilon) { + bestMatch.start = i - offset; + bestMatch.end = i; + bestMatch.counter = counter; + return bestMatch; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } else { + counter.push(0); + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + counter.push(0); + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + // if cmpCounter was not given + bestMatch.start = offset; + bestMatch.end = self._row.length - 1; + bestMatch.counter = counter; + return bestMatch; + }; + + BarcodeReader.prototype.decodePattern = function (pattern) { + var self = this, + result; + + self._row = pattern; + result = self._decode(); + if (result === null) { + self._row.reverse(); + result = self._decode(); + if (result) { + result.direction = BarcodeReader.DIRECTION.REVERSE; + result.start = self._row.length - result.start; + result.end = self._row.length - result.end; + } + } else { + result.direction = BarcodeReader.DIRECTION.FORWARD; + } + if (result) { + result.format = self.FORMAT; + } + return result; + }; + + BarcodeReader.prototype._matchRange = function (start, end, value) { + var i; + + start = start < 0 ? 0 : start; + for (i = start; i < end; i++) { + if (this._row[i] !== value) { + return false; + } + } + return true; + }; + + BarcodeReader.prototype._fillCounters = function (offset, end, isWhite) { + var self = this, + counterPos = 0, + i, + counters = []; + + isWhite = typeof isWhite !== 'undefined' ? isWhite : true; + offset = typeof offset !== 'undefined' ? offset : self._nextUnset(self._row); + end = end || self._row.length; + + counters[counterPos] = 0; + for (i = offset; i < end; i++) { + if (self._row[i] ^ isWhite) { + counters[counterPos]++; + } else { + counterPos++; + counters[counterPos] = 1; + isWhite = !isWhite; + } + } + return counters; + }; + + Object.defineProperty(BarcodeReader.prototype, "FORMAT", { + value: 'unknown', + writeable: false + }); + + BarcodeReader.DIRECTION = { + FORWARD: 1, + REVERSE: -1 + }; + + BarcodeReader.Exception = { + StartNotFoundException: "Start-Info was not found!", + CodeNotFoundException: "Code could not be found!", + PatternNotFoundException: "Pattern could not be found!" + }; + + BarcodeReader.CONFIG_KEYS = {}; + + exports['default'] = BarcodeReader; + module.exports = exports['default']; + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _barcode_reader = __webpack_require__(26); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + function EANReader(opts) { + _barcode_reader2["default"].call(this, opts); + } + + var properties = { + CODE_L_START: { value: 0 }, + MODULO: { value: 7 }, + CODE_G_START: { value: 10 }, + START_PATTERN: { value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7] }, + STOP_PATTERN: { value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7] }, + MIDDLE_PATTERN: { value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7] }, + CODE_PATTERN: { value: [[3, 2, 1, 1], [2, 2, 2, 1], [2, 1, 2, 2], [1, 4, 1, 1], [1, 1, 3, 2], [1, 2, 3, 1], [1, 1, 1, 4], [1, 3, 1, 2], [1, 2, 1, 3], [3, 1, 1, 2], [1, 1, 2, 3], [1, 2, 2, 2], [2, 2, 1, 2], [1, 1, 4, 1], [2, 3, 1, 1], [1, 3, 2, 1], [4, 1, 1, 1], [2, 1, 3, 1], [3, 1, 2, 1], [2, 1, 1, 3]] }, + CODE_FREQUENCY: { value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26] }, + SINGLE_CODE_ERROR: { value: 0.67 }, + AVG_CODE_ERROR: { value: 0.27 }, + FORMAT: { value: "ean_13", writeable: false } + }; + + EANReader.prototype = Object.create(_barcode_reader2["default"].prototype, properties); + EANReader.prototype.constructor = EANReader; + + EANReader.prototype._decodeCode = function (start, coderange) { + var counter = [0, 0, 0, 0], + i, + self = this, + offset = start, + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: start, + end: start + }, + code, + error, + normalized; + + if (!coderange) { + coderange = self.CODE_PATTERN.length; + } + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < coderange; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + bestMatch.end = i; + if (bestMatch.error > self.AVG_CODE_ERROR) { + return null; + } + return bestMatch; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + EANReader.prototype._findPattern = function (pattern, offset, isWhite, tryHarder, epsilon) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized; + + if (!offset) { + offset = self._nextSet(self._row); + } + + if (isWhite === undefined) { + isWhite = false; + } + + if (tryHarder === undefined) { + tryHarder = true; + } + + if (epsilon === undefined) { + epsilon = self.AVG_CODE_ERROR; + } + + for (i = 0; i < pattern.length; i++) { + counter[i] = 0; + } + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + error = self._matchPattern(normalized, pattern); + + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + } + if (tryHarder) { + for (j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; + } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + EANReader.prototype._findStart = function () { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset); + if (!startInfo) { + return null; + } + leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start); + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; + } + } + offset = startInfo.end; + startInfo = null; + } + }; + + EANReader.prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; + }; + + EANReader.prototype._findEnd = function (offset, isWhite) { + var self = this, + endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false); + + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; + }; + + EANReader.prototype._calculateFirstDigit = function (codeFrequency) { + var i, + self = this; + + for (i = 0; i < self.CODE_FREQUENCY.length; i++) { + if (codeFrequency === self.CODE_FREQUENCY[i]) { + return i; + } + } + return null; + }; + + EANReader.prototype._decodePayload = function (code, result, decodedCodes) { + var i, + self = this, + codeFrequency = 0x0, + firstDigit; + + for (i = 0; i < 6; i++) { + code = self._decodeCode(code.end); + if (!code) { + return null; + } + if (code.code >= self.CODE_G_START) { + code.code = code.code - self.CODE_G_START; + codeFrequency |= 1 << 5 - i; + } else { + codeFrequency |= 0 << 5 - i; + } + result.push(code.code); + decodedCodes.push(code); + } + + firstDigit = self._calculateFirstDigit(codeFrequency); + if (firstDigit === null) { + return null; + } + result.unshift(firstDigit); + + code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); + if (code === null) { + return null; + } + decodedCodes.push(code); + + for (i = 0; i < 6; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + decodedCodes.push(code); + result.push(code.code); + } + + return code; + }; + + EANReader.prototype._decode = function () { + var startInfo, + self = this, + code, + result = [], + decodedCodes = []; + + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + code = { + code: startInfo.code, + start: startInfo.start, + end: startInfo.end + }; + decodedCodes.push(code); + code = self._decodePayload(code, result, decodedCodes); + if (!code) { + return null; + } + code = self._findEnd(code.end, false); + if (!code) { + return null; + } + + decodedCodes.push(code); + + // Checksum + if (!self._checksum(result)) { + return null; + } + + return { + code: result.join(""), + start: startInfo.start, + end: code.end, + codeset: "", + startInfo: startInfo, + decodedCodes: decodedCodes + }; + }; + + EANReader.prototype._checksum = function (result) { + var sum = 0, + i; + + for (i = result.length - 2; i >= 0; i -= 2) { + sum += result[i]; + } + sum *= 3; + for (i = result.length - 1; i >= 0; i -= 2) { + sum += result[i]; + } + return sum % 10 === 0; + }; + + exports["default"] = EANReader; + module.exports = exports["default"]; + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _barcode_reader = __webpack_require__(26); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + var _array_helper = __webpack_require__(17); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + function Code39Reader() { + _barcode_reader2['default'].call(this); + } + + var properties = { + ALPHABETH_STRING: { value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%" }, + ALPHABET: { value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37] }, + CHARACTER_ENCODINGS: { value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A] }, + ASTERISK: { value: 0x094 }, + FORMAT: { value: "code_39", writeable: false } + }; + + Code39Reader.prototype = Object.create(_barcode_reader2['default'].prototype, properties); + Code39Reader.prototype.constructor = Code39Reader; + + Code39Reader.prototype._toCounters = function (start, counter) { + var self = this, + numCounters = counter.length, + end = self._row.length, + isWhite = !self._row[start], + i, + counterPos = 0; + + _array_helper2['default'].init(counter, 0); + + for (i = start; i < end; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + if (counterPos === numCounters) { + break; + } else { + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + return counter; + }; + + Code39Reader.prototype._decode = function () { + var self = this, + counters = [0, 0, 0, 0, 0, 0, 0, 0, 0], + result = [], + start = self._findStart(), + decodedChar, + lastStart, + pattern, + nextStart; + + if (!start) { + return null; + } + nextStart = self._nextSet(self._row, start.end); + + do { + counters = self._toCounters(nextStart, counters); + pattern = self._toPattern(counters); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + if (decodedChar < 0) { + return null; + } + result.push(decodedChar); + lastStart = nextStart; + nextStart += _array_helper2['default'].sum(counters); + nextStart = self._nextSet(self._row, nextStart); + } while (decodedChar !== '*'); + result.pop(); + + if (!result.length) { + return null; + } + + if (!self._verifyTrailingWhitespace(lastStart, nextStart, counters)) { + return null; + } + + return { + code: result.join(""), + start: start.start, + end: nextStart, + startInfo: start, + decodedCodes: result + }; + }; + + Code39Reader.prototype._verifyTrailingWhitespace = function (lastStart, nextStart, counters) { + var trailingWhitespaceEnd, + patternSize = _array_helper2['default'].sum(counters); + + trailingWhitespaceEnd = nextStart - lastStart - patternSize; + if (trailingWhitespaceEnd * 3 >= patternSize) { + return true; + } + return false; + }; + + Code39Reader.prototype._patternToChar = function (pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + }; + + Code39Reader.prototype._findNextWidth = function (counters, current) { + var i, + minWidth = Number.MAX_VALUE; + + for (i = 0; i < counters.length; i++) { + if (counters[i] < minWidth && counters[i] > current) { + minWidth = counters[i]; + } + } + + return minWidth; + }; + + Code39Reader.prototype._toPattern = function (counters) { + var numCounters = counters.length, + maxNarrowWidth = 0, + numWideBars = numCounters, + wideBarWidth = 0, + self = this, + pattern, + i; + + while (numWideBars > 3) { + maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); + numWideBars = 0; + pattern = 0; + for (i = 0; i < numCounters; i++) { + if (counters[i] > maxNarrowWidth) { + pattern |= 1 << numCounters - 1 - i; + numWideBars++; + wideBarWidth += counters[i]; + } + } + + if (numWideBars === 3) { + for (i = 0; i < numCounters && numWideBars > 0; i++) { + if (counters[i] > maxNarrowWidth) { + numWideBars--; + if (counters[i] * 2 >= wideBarWidth) { + return -1; + } + } + } + return pattern; + } + } + return -1; + }; + + Code39Reader.prototype._findStart = function () { + var self = this, + offset = self._nextSet(self._row), + patternStart = offset, + counter = [0, 0, 0, 0, 0, 0, 0, 0, 0], + counterPos = 0, + isWhite = false, + i, + j, + whiteSpaceMustStart; + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + // find start pattern + if (self._toPattern(counter) === self.ASTERISK) { + whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - (i - patternStart) / 4)); + if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { + return { + start: patternStart, + end: i + }; + } + } + + patternStart += counter[0] + counter[1]; + for (j = 0; j < 7; j++) { + counter[j] = counter[j + 2]; + } + counter[7] = 0; + counter[8] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + exports['default'] = Code39Reader; + module.exports = exports['default']; + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _code_39_reader = __webpack_require__(28); + + var _code_39_reader2 = _interopRequireDefault(_code_39_reader); + + function Code39VINReader() { + _code_39_reader2['default'].call(this); + } + + var patterns = { + IOQ: /[IOQ]/g, + AZ09: /[A-Z0-9]{17}/ + }; + + Code39VINReader.prototype = Object.create(_code_39_reader2['default'].prototype); + Code39VINReader.prototype.constructor = Code39VINReader; + + // Cribbed from: + // https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java + Code39VINReader.prototype._decode = function () { + var result = _code_39_reader2['default'].prototype._decode.apply(this); + if (!result) { + return null; + } + + var code = result.code; + + if (!code) { + return null; + } + + code = code.replace(patterns.IOQ, ''); + + if (!code.match(patterns.AZ09)) { + console.log('Failed AZ09 pattern code:', code); + return null; + } + + if (!this._checkChecksum(code)) { + return null; + } + + result.code = code; + return result; + }; + + Code39VINReader.prototype._checkChecksum = function (code) { + // TODO + return !!code; + }; + + exports['default'] = Code39VINReader; + module.exports = exports['default']; + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _barcode_reader = __webpack_require__(26); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + function CodabarReader() { + _barcode_reader2["default"].call(this); + this._counters = []; + } + + var properties = { + ALPHABETH_STRING: { value: "0123456789-$:/.+ABCD" }, + ALPHABET: { value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68] }, + CHARACTER_ENCODINGS: { value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E] }, + START_END: { value: [0x01A, 0x029, 0x00B, 0x00E] }, + MIN_ENCODED_CHARS: { value: 4 }, + MAX_ACCEPTABLE: { value: 2.0 }, + PADDING: { value: 1.5 }, + FORMAT: { value: "codabar", writeable: false } + }; + + CodabarReader.prototype = Object.create(_barcode_reader2["default"].prototype, properties); + CodabarReader.prototype.constructor = CodabarReader; + + CodabarReader.prototype._decode = function () { + var self = this, + result = [], + start, + decodedChar, + pattern, + nextStart, + end; + + this._counters = self._fillCounters(); + start = self._findStart(); + if (!start) { + return null; + } + nextStart = start.startCounter; + + do { + pattern = self._toPattern(nextStart); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + if (decodedChar < 0) { + return null; + } + result.push(decodedChar); + nextStart += 8; + if (result.length > 1 && self._isStartEnd(pattern)) { + break; + } + } while (nextStart < self._counters.length); + + // verify end + if (result.length - 2 < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) { + return null; + } + + // verify end white space + if (!self._verifyWhitespace(start.startCounter, nextStart - 8)) { + return null; + } + + if (!self._validateResult(result, start.startCounter)) { + return null; + } + + nextStart = nextStart > self._counters.length ? self._counters.length : nextStart; + end = start.start + self._sumCounters(start.startCounter, nextStart - 8); + + return { + code: result.join(""), + start: start.start, + end: end, + startInfo: start, + decodedCodes: result + }; + }; + + CodabarReader.prototype._verifyWhitespace = function (startCounter, endCounter) { + if (startCounter - 1 <= 0 || this._counters[startCounter - 1] >= this._calculatePatternLength(startCounter) / 2.0) { + if (endCounter + 8 >= this._counters.length || this._counters[endCounter + 7] >= this._calculatePatternLength(endCounter) / 2.0) { + return true; + } + } + return false; + }; + + CodabarReader.prototype._calculatePatternLength = function (offset) { + var i, + sum = 0; + + for (i = offset; i < offset + 7; i++) { + sum += this._counters[i]; + } + + return sum; + }; + + CodabarReader.prototype._thresholdResultPattern = function (result, startCounter) { + var self = this, + categorization = { + space: { + narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE }, + wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE } + }, + bar: { + narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE }, + wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE } + } + }, + kind, + cat, + i, + j, + pos = startCounter, + pattern; + + for (i = 0; i < result.length; i++) { + pattern = self._charToPattern(result[i]); + for (j = 6; j >= 0; j--) { + kind = (j & 1) === 2 ? categorization.bar : categorization.space; + cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; + cat.size += self._counters[pos + j]; + cat.counts++; + pattern >>= 1; + } + pos += 8; + } + + ["space", "bar"].forEach(function (key) { + var newkind = categorization[key]; + newkind.wide.min = Math.floor((newkind.narrow.size / newkind.narrow.counts + newkind.wide.size / newkind.wide.counts) / 2); + newkind.narrow.max = Math.ceil(newkind.wide.min); + newkind.wide.max = Math.ceil((newkind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / newkind.wide.counts); + }); + + return categorization; + }; + + CodabarReader.prototype._charToPattern = function (char) { + var self = this, + charCode = char.charCodeAt(0), + i; + + for (i = 0; i < self.ALPHABET.length; i++) { + if (self.ALPHABET[i] === charCode) { + return self.CHARACTER_ENCODINGS[i]; + } + } + return 0x0; + }; + + CodabarReader.prototype._validateResult = function (result, startCounter) { + var self = this, + thresholds = self._thresholdResultPattern(result, startCounter), + i, + j, + kind, + cat, + size, + pos = startCounter, + pattern; + + for (i = 0; i < result.length; i++) { + pattern = self._charToPattern(result[i]); + for (j = 6; j >= 0; j--) { + kind = (j & 1) === 0 ? thresholds.bar : thresholds.space; + cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; + size = self._counters[pos + j]; + if (size < cat.min || size > cat.max) { + return false; + } + pattern >>= 1; + } + pos += 8; + } + return true; + }; + + CodabarReader.prototype._patternToChar = function (pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + return -1; + }; + + CodabarReader.prototype._computeAlternatingThreshold = function (offset, end) { + var i, + min = Number.MAX_VALUE, + max = 0, + counter; + + for (i = offset; i < end; i += 2) { + counter = this._counters[i]; + if (counter > max) { + max = counter; + } + if (counter < min) { + min = counter; + } + } + + return (min + max) / 2.0 | 0; + }; + + CodabarReader.prototype._toPattern = function (offset) { + var numCounters = 7, + end = offset + numCounters, + barThreshold, + spaceThreshold, + bitmask = 1 << numCounters - 1, + pattern = 0, + i, + threshold; + + if (end > this._counters.length) { + return -1; + } + + barThreshold = this._computeAlternatingThreshold(offset, end); + spaceThreshold = this._computeAlternatingThreshold(offset + 1, end); + + for (i = 0; i < numCounters; i++) { + threshold = (i & 1) === 0 ? barThreshold : spaceThreshold; + if (this._counters[offset + i] > threshold) { + pattern |= bitmask; + } + bitmask >>= 1; + } + + return pattern; + }; + + CodabarReader.prototype._isStartEnd = function (pattern) { + var i; + + for (i = 0; i < this.START_END.length; i++) { + if (this.START_END[i] === pattern) { + return true; + } + } + return false; + }; + + CodabarReader.prototype._sumCounters = function (start, end) { + var i, + sum = 0; + + for (i = start; i < end; i++) { + sum += this._counters[i]; + } + return sum; + }; + + CodabarReader.prototype._findStart = function () { + var self = this, + i, + pattern, + start = self._nextUnset(self._row), + end; + + for (i = 1; i < this._counters.length; i++) { + pattern = self._toPattern(i); + if (pattern !== -1 && self._isStartEnd(pattern)) { + // TODO: Look for whitespace ahead + start += self._sumCounters(0, i); + end = start + self._sumCounters(i, i + 8); + return { + start: start, + end: end, + startCounter: i, + endCounter: i + 8 + }; + } + } + }; + + exports["default"] = CodabarReader; + module.exports = exports["default"]; + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _ean_reader = __webpack_require__(27); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + function UPCReader() { + _ean_reader2["default"].call(this); + } + + var properties = { + FORMAT: { value: "upc_a", writeable: false } + }; + + UPCReader.prototype = Object.create(_ean_reader2["default"].prototype, properties); + UPCReader.prototype.constructor = UPCReader; + + UPCReader.prototype._decode = function () { + var result = _ean_reader2["default"].prototype._decode.call(this); + + if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") { + result.code = result.code.substring(1); + return result; + } + return null; + }; + + exports["default"] = UPCReader; + module.exports = exports["default"]; + +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _ean_reader = __webpack_require__(27); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + function EAN8Reader() { + _ean_reader2["default"].call(this); + } + + var properties = { + FORMAT: { value: "ean_8", writeable: false } + }; + + EAN8Reader.prototype = Object.create(_ean_reader2["default"].prototype, properties); + EAN8Reader.prototype.constructor = EAN8Reader; + + EAN8Reader.prototype._decodePayload = function (code, result, decodedCodes) { + var i, + self = this; + + for (i = 0; i < 4; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + result.push(code.code); + decodedCodes.push(code); + } + + code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); + if (code === null) { + return null; + } + decodedCodes.push(code); + + for (i = 0; i < 4; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + decodedCodes.push(code); + result.push(code.code); + } + + return code; + }; + + exports["default"] = EAN8Reader; + module.exports = exports["default"]; + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _ean_reader = __webpack_require__(27); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + function UPCEReader() { + _ean_reader2["default"].call(this); + } + + var properties = { + CODE_FREQUENCY: { value: [[56, 52, 50, 49, 44, 38, 35, 42, 41, 37], [7, 11, 13, 14, 19, 25, 28, 21, 22, 26]] }, + STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7] }, + FORMAT: { value: "upc_e", writeable: false } + }; + + UPCEReader.prototype = Object.create(_ean_reader2["default"].prototype, properties); + UPCEReader.prototype.constructor = UPCEReader; + + UPCEReader.prototype._decodePayload = function (code, result, decodedCodes) { + var i, + self = this, + codeFrequency = 0x0; + + for (i = 0; i < 6; i++) { + code = self._decodeCode(code.end); + if (!code) { + return null; + } + if (code.code >= self.CODE_G_START) { + code.code = code.code - self.CODE_G_START; + codeFrequency |= 1 << 5 - i; + } + result.push(code.code); + decodedCodes.push(code); + } + if (!self._determineParity(codeFrequency, result)) { + return null; + } + + return code; + }; + + UPCEReader.prototype._determineParity = function (codeFrequency, result) { + var i, nrSystem; + + for (nrSystem = 0; nrSystem < this.CODE_FREQUENCY.length; nrSystem++) { + for (i = 0; i < this.CODE_FREQUENCY[nrSystem].length; i++) { + if (codeFrequency === this.CODE_FREQUENCY[nrSystem][i]) { + result.unshift(nrSystem); + result.push(i); + return true; + } + } + } + return false; + }; + + UPCEReader.prototype._convertToUPCA = function (result) { + var upca = [result[0]], + lastDigit = result[result.length - 2]; + + if (lastDigit <= 2) { + upca = upca.concat(result.slice(1, 3)).concat([lastDigit, 0, 0, 0, 0]).concat(result.slice(3, 6)); + } else if (lastDigit === 3) { + upca = upca.concat(result.slice(1, 4)).concat([0, 0, 0, 0, 0]).concat(result.slice(4, 6)); + } else if (lastDigit === 4) { + upca = upca.concat(result.slice(1, 5)).concat([0, 0, 0, 0, 0, result[5]]); + } else { + upca = upca.concat(result.slice(1, 6)).concat([0, 0, 0, 0, lastDigit]); + } + + upca.push(result[result.length - 1]); + return upca; + }; + + UPCEReader.prototype._checksum = function (result) { + return _ean_reader2["default"].prototype._checksum.call(this, this._convertToUPCA(result)); + }; + + UPCEReader.prototype._findEnd = function (offset, isWhite) { + isWhite = true; + return _ean_reader2["default"].prototype._findEnd.call(this, offset, isWhite); + }; + + UPCEReader.prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start) / 2; + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + }; + + exports["default"] = UPCEReader; + module.exports = exports["default"]; + +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _barcode_reader = __webpack_require__(26); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + var merge = __webpack_require__(35); + + function I2of5Reader(opts) { + opts = merge(getDefaulConfig(), opts); + _barcode_reader2['default'].call(this, opts); + this.barSpaceRatio = [1, 1]; + if (opts.normalizeBarSpaceWidth) { + this.SINGLE_CODE_ERROR = 0.38; + this.AVG_CODE_ERROR = 0.09; + } + } + + function getDefaulConfig() { + var config = {}; + + Object.keys(I2of5Reader.CONFIG_KEYS).forEach(function (key) { + config[key] = I2of5Reader.CONFIG_KEYS[key]['default']; + }); + return config; + } + + var N = 1, + W = 3, + properties = { + MODULO: { value: 10 }, + START_PATTERN: { value: [N * 2.5, N * 2.5, N * 2.5, N * 2.5] }, + STOP_PATTERN: { value: [N * 2, N * 2, W * 2] }, + CODE_PATTERN: { value: [[N, N, W, W, N], [W, N, N, N, W], [N, W, N, N, W], [W, W, N, N, N], [N, N, W, N, W], [W, N, W, N, N], [N, W, W, N, N], [N, N, N, W, W], [W, N, N, W, N], [N, W, N, W, N]] }, + SINGLE_CODE_ERROR: { value: 0.78, writable: true }, + AVG_CODE_ERROR: { value: 0.38, writable: true }, + MAX_CORRECTION_FACTOR: { value: 5 }, + FORMAT: { value: "i2of5" } + }; + + I2of5Reader.prototype = Object.create(_barcode_reader2['default'].prototype, properties); + I2of5Reader.prototype.constructor = I2of5Reader; + + I2of5Reader.prototype._matchPattern = function (counter, code) { + if (this.config.normalizeBarSpaceWidth) { + var i, + counterSum = [0, 0], + codeSum = [0, 0], + correction = [0, 0], + correctionRatio = this.MAX_CORRECTION_FACTOR, + correctionRatioInverse = 1 / correctionRatio; + + for (i = 0; i < counter.length; i++) { + counterSum[i % 2] += counter[i]; + codeSum[i % 2] += code[i]; + } + correction[0] = codeSum[0] / counterSum[0]; + correction[1] = codeSum[1] / counterSum[1]; + + correction[0] = Math.max(Math.min(correction[0], correctionRatio), correctionRatioInverse); + correction[1] = Math.max(Math.min(correction[1], correctionRatio), correctionRatioInverse); + this.barSpaceRatio = correction; + for (i = 0; i < counter.length; i++) { + counter[i] *= this.barSpaceRatio[i % 2]; + } + } + return _barcode_reader2['default'].prototype._matchPattern.call(this, counter, code); + }; + + I2of5Reader.prototype._findPattern = function (pattern, offset, isWhite, tryHarder) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized, + epsilon = self.AVG_CODE_ERROR; + + isWhite = isWhite || false; + tryHarder = tryHarder || false; + + if (!offset) { + offset = self._nextSet(self._row); + } + + for (i = 0; i < pattern.length; i++) { + counter[i] = 0; + } + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + error = self._matchPattern(normalized, pattern); + + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + } + if (tryHarder) { + for (j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; + } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + I2of5Reader.prototype._findStart = function () { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo, + narrowBarWidth = 1; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset, false, true); + if (!startInfo) { + return null; + } + narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / 4); + leadingWhitespaceStart = startInfo.start - narrowBarWidth * 10; + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; + } + } + offset = startInfo.end; + startInfo = null; + } + }; + + I2of5Reader.prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start) / 2; + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; + }; + + I2of5Reader.prototype._findEnd = function () { + var self = this, + endInfo, + tmp; + + self._row.reverse(); + endInfo = self._findPattern(self.STOP_PATTERN); + self._row.reverse(); + + if (endInfo === null) { + return null; + } + + // reverse numbers + tmp = endInfo.start; + endInfo.start = self._row.length - endInfo.end; + endInfo.end = self._row.length - tmp; + + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; + }; + + I2of5Reader.prototype._decodePair = function (counterPair) { + var i, + code, + codes = [], + self = this; + + for (i = 0; i < counterPair.length; i++) { + code = self._decodeCode(counterPair[i]); + if (!code) { + return null; + } + codes.push(code); + } + return codes; + }; + + I2of5Reader.prototype._decodeCode = function (counter) { + var j, + self = this, + sum = 0, + normalized, + error, + epsilon = self.AVG_CODE_ERROR, + code, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }; + + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + if (bestMatch.error < epsilon) { + return bestMatch; + } + } + return null; + }; + + I2of5Reader.prototype._decodePayload = function (counters, result, decodedCodes) { + var i, + self = this, + pos = 0, + counterLength = counters.length, + counterPair = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], + codes; + + while (pos < counterLength) { + for (i = 0; i < 5; i++) { + counterPair[0][i] = counters[pos] * this.barSpaceRatio[0]; + counterPair[1][i] = counters[pos + 1] * this.barSpaceRatio[1]; + pos += 2; + } + codes = self._decodePair(counterPair); + if (!codes) { + return null; + } + for (i = 0; i < codes.length; i++) { + result.push(codes[i].code + ""); + decodedCodes.push(codes[i]); + } + } + return codes; + }; + + I2of5Reader.prototype._verifyCounterLength = function (counters) { + return counters.length % 10 === 0; + }; + + I2of5Reader.prototype._decode = function () { + var startInfo, + endInfo, + self = this, + code, + result = [], + decodedCodes = [], + counters; + + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + decodedCodes.push(startInfo); + + endInfo = self._findEnd(); + if (!endInfo) { + return null; + } + + counters = self._fillCounters(startInfo.end, endInfo.start, false); + if (!self._verifyCounterLength(counters)) { + return null; + } + code = self._decodePayload(counters, result, decodedCodes); + if (!code) { + return null; + } + if (result.length % 2 !== 0 || result.length < 6) { + return null; + } + + decodedCodes.push(endInfo); + return { + code: result.join(""), + start: startInfo.start, + end: endInfo.end, + startInfo: startInfo, + decodedCodes: decodedCodes + }; + }; + + I2of5Reader.CONFIG_KEYS = { + normalizeBarSpaceWidth: { + 'type': 'boolean', + 'default': false, + 'description': 'If true, the reader tries to normalize the' + 'width-difference between bars and spaces' + } + }; + + exports['default'] = I2of5Reader; + module.exports = exports['default']; + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + var baseMerge = __webpack_require__(36), + createAssigner = __webpack_require__(63); + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * overwrite property assignments of previous sources. If `customizer` is + * provided it's invoked to produce the merged values of the destination and + * source properties. If `customizer` returns `undefined` merging is handled + * by the method instead. The `customizer` is bound to `thisArg` and invoked + * with five arguments: (objectValue, sourceValue, key, object, source). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * var users = { + * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] + * }; + * + * var ages = { + * 'data': [{ 'age': 36 }, { 'age': 40 }] + * }; + * + * _.merge(users, ages); + * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } + * + * // using a customizer callback + * var object = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var other = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(object, other, function(a, b) { + * if (_.isArray(a)) { + * return a.concat(b); + * } + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } + */ + var merge = createAssigner(baseMerge); + + module.exports = merge; + + +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + + var arrayEach = __webpack_require__(37), + baseMergeDeep = __webpack_require__(38), + isArray = __webpack_require__(46), + isArrayLike = __webpack_require__(41), + isObject = __webpack_require__(50), + isObjectLike = __webpack_require__(45), + isTypedArray = __webpack_require__(58), + keys = __webpack_require__(61); + + /** + * The base implementation of `_.merge` without support for argument juggling, + * multiple sources, and `this` binding `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {Object} Returns `object`. + */ + function baseMerge(object, source, customizer, stackA, stackB) { + if (!isObject(object)) { + return object; + } + var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), + props = isSrcArr ? undefined : keys(source); + + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } + if (isObjectLike(srcValue)) { + stackA || (stackA = []); + stackB || (stackB = []); + baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); + } + else { + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + } + if ((result !== undefined || (isSrcArr && !(key in object))) && + (isCommon || (result === result ? (result !== value) : (value === value)))) { + object[key] = result; + } + } + }); + return object; + } + + module.exports = baseMerge; + + +/***/ }, +/* 37 */ +/***/ function(module, exports) { + + /** + * A specialized version of `_.forEach` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + module.exports = arrayEach; + + +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { + + var arrayCopy = __webpack_require__(39), + isArguments = __webpack_require__(40), + isArray = __webpack_require__(46), + isArrayLike = __webpack_require__(41), + isPlainObject = __webpack_require__(51), + isTypedArray = __webpack_require__(58), + toPlainObject = __webpack_require__(59); + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { + var length = stackA.length, + srcValue = source[key]; + + while (length--) { + if (stackA[length] == srcValue) { + object[key] = stackB[length]; + return; + } + } + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) { + result = isArray(value) + ? value + : (isArrayLike(value) ? arrayCopy(value) : []); + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + result = isArguments(value) + ? toPlainObject(value) + : (isPlainObject(value) ? value : {}); + } + else { + isCommon = false; + } + } + // Add the source value to the stack of traversed objects and associate + // it with its merged value. + stackA.push(srcValue); + stackB.push(result); + + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); + } else if (result === result ? (result !== value) : (value === value)) { + object[key] = result; + } + } + + module.exports = baseMergeDeep; + + +/***/ }, +/* 39 */ +/***/ function(module, exports) { + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function arrayCopy(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + module.exports = arrayCopy; + + +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + var isArrayLike = __webpack_require__(41), + isObjectLike = __webpack_require__(45); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Native method references. */ + var propertyIsEnumerable = objectProto.propertyIsEnumerable; + + /** + * Checks if `value` is classified as an `arguments` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); + } + + module.exports = isArguments; + + +/***/ }, +/* 41 */ +/***/ function(module, exports, __webpack_require__) { + + var getLength = __webpack_require__(42), + isLength = __webpack_require__(44); + + /** + * Checks if `value` is array-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + */ + function isArrayLike(value) { + return value != null && isLength(getLength(value)); + } + + module.exports = isArrayLike; + + +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { + + var baseProperty = __webpack_require__(43); + + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * that affects Safari on at least iOS 8.1-8.3 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + module.exports = getLength; + + +/***/ }, +/* 43 */ +/***/ function(module, exports) { + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + module.exports = baseProperty; + + +/***/ }, +/* 44 */ +/***/ function(module, exports) { + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + module.exports = isLength; + + +/***/ }, +/* 45 */ +/***/ function(module, exports) { + + /** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + module.exports = isObjectLike; + + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + var getNative = __webpack_require__(47), + isLength = __webpack_require__(44), + isObjectLike = __webpack_require__(45); + + /** `Object#toString` result references. */ + var arrayTag = '[object Array]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeIsArray = getNative(Array, 'isArray'); + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */ + var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; + }; + + module.exports = isArray; + + +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + var isNative = __webpack_require__(48); + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; + } + + module.exports = getNative; + + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + var isFunction = __webpack_require__(49), + isObjectLike = __webpack_require__(45); + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to resolve the decompiled source of functions. */ + var fnToString = Function.prototype.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); + } + + module.exports = isNative; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(50); + + /** `Object#toString` result references. */ + var funcTag = '[object Function]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 which returns 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; + } + + module.exports = isFunction; + + +/***/ }, +/* 50 */ +/***/ function(module, exports) { + + /** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + module.exports = isObject; + + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { + + var baseForIn = __webpack_require__(52), + isArguments = __webpack_require__(40), + isObjectLike = __webpack_require__(45); + + /** `Object#toString` result references. */ + var objectTag = '[object Object]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * **Note:** This method assumes objects created by the `Object` constructor + * have no inherited enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + var Ctor; + + // Exit early for non `Object` objects. + if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) || + (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + var result; + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + baseForIn(value, function(subValue, key) { + result = key; + }); + return result === undefined || hasOwnProperty.call(value, result); + } + + module.exports = isPlainObject; + + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + + var baseFor = __webpack_require__(53), + keysIn = __webpack_require__(56); + + /** + * The base implementation of `_.forIn` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForIn(object, iteratee) { + return baseFor(object, iteratee, keysIn); + } + + module.exports = baseForIn; + + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + var createBaseFor = __webpack_require__(54); + + /** + * The base implementation of `baseForIn` and `baseForOwn` which iterates + * over `object` properties returned by `keysFunc` invoking `iteratee` for + * each property. Iteratee functions may exit iteration early by explicitly + * returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + module.exports = baseFor; + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + var toObject = __webpack_require__(55); + + /** + * Creates a base function for `_.forIn` or `_.forInRight`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var iterable = toObject(object), + props = keysFunc(object), + length = props.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + var key = props[index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + module.exports = createBaseFor; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(50); + + /** + * Converts `value` to an object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Object} Returns the object. + */ + function toObject(value) { + return isObject(value) ? value : Object(value); + } + + module.exports = toObject; + + +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + + var isArguments = __webpack_require__(40), + isArray = __webpack_require__(46), + isIndex = __webpack_require__(57), + isLength = __webpack_require__(44), + isObject = __webpack_require__(50); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + if (object == null) { + return []; + } + if (!isObject(object)) { + object = Object(object); + } + var length = object.length; + length = (length && isLength(length) && + (isArray(object) || isArguments(object)) && length) || 0; + + var Ctor = object.constructor, + index = -1, + isProto = typeof Ctor == 'function' && Ctor.prototype === object, + result = Array(length), + skipIndexes = length > 0; + + while (++index < length) { + result[index] = (index + ''); + } + for (var key in object) { + if (!(skipIndexes && isIndex(key, length)) && + !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + module.exports = keysIn; + + +/***/ }, +/* 57 */ +/***/ function(module, exports) { + + /** Used to detect unsigned integer values. */ + var reIsUint = /^\d+$/; + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; + length = length == null ? MAX_SAFE_INTEGER : length; + return value > -1 && value % 1 == 0 && value < length; + } + + module.exports = isIndex; + + +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { + + var isLength = __webpack_require__(44), + isObjectLike = __webpack_require__(45); + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dateTag] = typedArrayTags[errorTag] = + typedArrayTags[funcTag] = typedArrayTags[mapTag] = + typedArrayTags[numberTag] = typedArrayTags[objectTag] = + typedArrayTags[regexpTag] = typedArrayTags[setTag] = + typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + function isTypedArray(value) { + return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; + } + + module.exports = isTypedArray; + + +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { + + var baseCopy = __webpack_require__(60), + keysIn = __webpack_require__(56); + + /** + * Converts `value` to a plain object flattening inherited enumerable + * properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return baseCopy(value, keysIn(value)); + } + + module.exports = toPlainObject; + + +/***/ }, +/* 60 */ +/***/ function(module, exports) { + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. + * @returns {Object} Returns `object`. + */ + function baseCopy(source, props, object) { + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + object[key] = source[key]; + } + return object; + } + + module.exports = baseCopy; + + +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + var getNative = __webpack_require__(47), + isArrayLike = __webpack_require__(41), + isObject = __webpack_require__(50), + shimKeys = __webpack_require__(62); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeKeys = getNative(Object, 'keys'); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) + * for more details. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + var keys = !nativeKeys ? shimKeys : function(object) { + var Ctor = object == null ? undefined : object.constructor; + if ((typeof Ctor == 'function' && Ctor.prototype === object) || + (typeof object != 'function' && isArrayLike(object))) { + return shimKeys(object); + } + return isObject(object) ? nativeKeys(object) : []; + }; + + module.exports = keys; + + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + var isArguments = __webpack_require__(40), + isArray = __webpack_require__(46), + isIndex = __webpack_require__(57), + isLength = __webpack_require__(44), + keysIn = __webpack_require__(56); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * A fallback implementation of `Object.keys` which creates an array of the + * own enumerable property names of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function shimKeys(object) { + var props = keysIn(object), + propsLength = props.length, + length = propsLength && object.length; + + var allowIndexes = !!length && isLength(length) && + (isArray(object) || isArguments(object)); + + var index = -1, + result = []; + + while (++index < propsLength) { + var key = props[index]; + if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { + result.push(key); + } + } + return result; + } + + module.exports = shimKeys; + + +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { + + var bindCallback = __webpack_require__(64), + isIterateeCall = __webpack_require__(66), + restParam = __webpack_require__(67); + + /** + * Creates a `_.assign`, `_.defaults`, or `_.merge` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 ? sources[length - 2] : undefined, + guard = length > 2 ? sources[2] : undefined, + thisArg = length > 1 ? sources[length - 1] : undefined; + + if (typeof customizer == 'function') { + customizer = bindCallback(customizer, thisArg, 5); + length -= 2; + } else { + customizer = typeof thisArg == 'function' ? thisArg : undefined; + length -= (customizer ? 1 : 0); + } + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, customizer); + } + } + return object; + }); + } + + module.exports = createAssigner; + + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + var identity = __webpack_require__(65); + + /** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; + } + + module.exports = bindCallback; + + +/***/ }, +/* 65 */ +/***/ function(module, exports) { + + /** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'user': 'fred' }; + * + * _.identity(object) === object; + * // => true + */ + function identity(value) { + return value; + } + + module.exports = identity; + + +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { + + var isArrayLike = __webpack_require__(41), + isIndex = __webpack_require__(57), + isObject = __webpack_require__(50); + + /** + * Checks if the provided arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object)) { + var other = object[index]; + return value === value ? (value === other) : (other !== other); + } + return false; + } + + module.exports = isIterateeCall; + + +/***/ }, +/* 67 */ +/***/ function(module, exports) { + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeMax = Math.max; + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function restParam(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + rest = Array(length); + + while (++index < length) { + rest[index] = args[start + index]; + } + switch (start) { + case 0: return func.call(this, rest); + case 1: return func.call(this, args[0], rest); + case 2: return func.call(this, args[0], args[1], rest); + } + var otherArgs = Array(start + 1); + index = -1; + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = rest; + return func.apply(this, otherArgs); + }; + } + + module.exports = restParam; + + +/***/ }, +/* 68 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports["default"] = { + inputStream: { + name: "Live", + type: "LiveStream", + constraints: { + width: 640, + height: 480, + minAspectRatio: 0, + maxAspectRatio: 100, + facing: "environment" // or user + }, + area: { + top: "0%", + right: "0%", + left: "0%", + bottom: "0%" + }, + singleChannel: false // true: only the red color-channel is read + }, + tracking: false, + debug: false, + controls: false, + locate: true, + numOfWorkers: 4, + visual: { + show: true + }, + decoder: { + drawBoundingBox: false, + showFrequency: false, + drawScanline: false, + showPattern: false, + readers: ['code_128_reader'] + }, + locator: { + halfSample: true, + patchSize: "medium", // x-small, small, medium, large, x-large + showCanvas: false, + showPatches: false, + showFoundPatches: false, + showSkeleton: false, + showLabels: false, + showPatchLabels: false, + showRemainingPatchLabels: false, + boxFromPatches: { + showTransformed: false, + showTransformedBox: false, + showBB: false + } + } + }; + module.exports = exports["default"]; + +/***/ }, +/* 69 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + exports["default"] = (function () { + var events = {}; + + function getEvent(eventName) { + if (!events[eventName]) { + events[eventName] = { + subscribers: [] + }; + } + return events[eventName]; + } + + function clearEvents() { + events = {}; + } + + function publishSubscription(subscription, data) { + if (subscription.async) { + setTimeout(function () { + subscription.callback(data); + }, 4); + } else { + subscription.callback(data); + } + } + + function _subscribe(event, callback, async) { + var subscription; + + if (typeof callback === "function") { + subscription = { + callback: callback, + async: async + }; + } else { + subscription = callback; + if (!subscription.callback) { + throw "Callback was not specified on options"; + } + } + + getEvent(event).subscribers.push(subscription); + } + + return { + subscribe: function subscribe(event, callback, async) { + return _subscribe(event, callback, async); + }, + publish: function publish(eventName, data) { + var event = getEvent(eventName), + subscribers = event.subscribers; + + event.subscribers = subscribers.filter(function (subscriber) { + publishSubscription(subscriber, data); + return !subscriber.once; + }); + }, + once: function once(event, callback, async) { + _subscribe(event, { + callback: callback, + async: async, + once: true + }); + }, + unsubscribe: function unsubscribe(eventName, callback) { + var event; + + if (eventName) { + event = getEvent(eventName); + if (event && callback) { + event.subscribers = event.subscribers.filter(function (subscriber) { + return subscriber.callback !== callback; + }); + } else { + event.subscribers = []; + } + } else { + clearEvents(); + } + } + }; + })(); + + module.exports = exports["default"]; + +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + var merge = __webpack_require__(35); + + var streamRef, loadedDataHandler; + + /** + * Wraps browser-specific getUserMedia + * @param {Object} constraints + * @param {Object} success Callback + * @param {Object} failure Callback + */ + function getUserMedia(constraints, success, failure) { + if (typeof navigator.getUserMedia !== 'undefined') { + navigator.getUserMedia(constraints, function (stream) { + streamRef = stream; + var videoSrc = window.URL && window.URL.createObjectURL(stream) || stream; + success.apply(null, [videoSrc]); + }, failure); + } else { + failure(new TypeError("getUserMedia not available")); + } + } + + function loadedData(video, callback) { + var attempts = 10; + + function checkVideo() { + if (attempts > 0) { + if (video.videoWidth > 0 && video.videoHeight > 0) { + console.log(video.videoWidth + "px x " + video.videoHeight + "px"); + callback(); + } else { + window.setTimeout(checkVideo, 500); + } + } else { + callback('Unable to play video stream. Is webcam working?'); + } + attempts--; + } + checkVideo(); + } + + /** + * Tries to attach the camera-stream to a given video-element + * and calls the callback function when the content is ready + * @param {Object} constraints + * @param {Object} video + * @param {Object} callback + */ + function initCamera(constraints, video, callback) { + getUserMedia(constraints, function (src) { + video.src = src; + if (loadedDataHandler) { + video.removeEventListener("loadeddata", loadedDataHandler, false); + } + loadedDataHandler = loadedData.bind(null, video, callback); + video.addEventListener('loadeddata', loadedDataHandler, false); + video.play(); + }, function (e) { + callback(e); + }); + } + + /** + * Normalizes the incoming constraints to satisfy the current browser + * @param config + * @param cb Callback which is called whenever constraints are created + * @returns {*} + */ + function normalizeConstraints(config, cb) { + var constraints = { + audio: false, + video: true + }, + videoConstraints = merge({ + width: 640, + height: 480, + minAspectRatio: 0, + maxAspectRatio: 100, + facing: "environment" + }, config); + + if (typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') { + MediaStreamTrack.getSources(function (sourceInfos) { + var videoSourceId; + for (var i = 0; i < sourceInfos.length; ++i) { + var sourceInfo = sourceInfos[i]; + if (sourceInfo.kind === "video" && sourceInfo.facing === videoConstraints.facing) { + videoSourceId = sourceInfo.id; + } + } + constraints.video = { + mandatory: { + minWidth: videoConstraints.width, + minHeight: videoConstraints.height, + minAspectRatio: videoConstraints.minAspectRatio, + maxAspectRatio: videoConstraints.maxAspectRatio + }, + optional: [{ + sourceId: videoSourceId + }] + }; + return cb(constraints); + }); + } else { + constraints.video = { + mediaSource: "camera", + width: { min: videoConstraints.width, max: videoConstraints.width }, + height: { min: videoConstraints.height, max: videoConstraints.height }, + require: ["width", "height"] + }; + return cb(constraints); + } + } + + /** + * Requests the back-facing camera of the user. The callback is called + * whenever the stream is ready to be consumed, or if an error occures. + * @param {Object} video + * @param {Object} callback + */ + function _request(video, videoConstraints, callback) { + normalizeConstraints(videoConstraints, function (constraints) { + initCamera(constraints, video, callback); + }); + } + + exports['default'] = { + request: function request(video, constraints, callback) { + _request(video, constraints, callback); + }, + release: function release() { + var tracks = streamRef && streamRef.getVideoTracks(); + if (tracks.length) { + tracks[0].stop(); + } + streamRef = null; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _image_debug = __webpack_require__(22); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + function contains(codeResult, list) { + if (list) { + return list.some(function (item) { + return Object.keys(item).every(function (key) { + return item[key] === codeResult[key]; + }); + }); + } + return false; + } + + function passesFilter(codeResult, filter) { + if (typeof filter === 'function') { + return filter(codeResult); + } + return true; + } + + exports['default'] = { + create: function create(config) { + var canvas = document.createElement("canvas"), + ctx = canvas.getContext("2d"), + results = [], + capacity = config.capacity || 20, + capture = config.capture === true; + + function matchesConstraints(codeResult) { + return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter); + } + + return { + addResult: function addResult(data, imageSize, codeResult) { + var result = {}; + + if (matchesConstraints(codeResult)) { + capacity--; + result.codeResult = codeResult; + if (capture) { + canvas.width = imageSize.x; + canvas.height = imageSize.y; + _image_debug2['default'].drawImage(data, imageSize, ctx); + result.frame = canvas.toDataURL(); + } + results.push(result); + } + }, + getResults: function getResults() { + return results; + } + }; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _image_loader = __webpack_require__(73); + + var _image_loader2 = _interopRequireDefault(_image_loader); + + var InputStream = {}; + InputStream.createVideoStream = function (video) { + var that = {}, + _config = null, + _eventNames = ['canrecord', 'ended'], + _eventHandlers = {}, + _calculatedWidth, + _calculatedHeight, + _topRight = { x: 0, y: 0 }, + _canvasSize = { x: 0, y: 0 }; + + function initSize() { + var width = video.videoWidth, + height = video.videoHeight; + + _calculatedWidth = _config.size ? width / height > 1 ? _config.size : Math.floor(width / height * _config.size) : width; + _calculatedHeight = _config.size ? width / height > 1 ? Math.floor(height / width * _config.size) : _config.size : height; + + _canvasSize.x = _calculatedWidth; + _canvasSize.y = _calculatedHeight; + } + + that.getRealWidth = function () { + return video.videoWidth; + }; + + that.getRealHeight = function () { + return video.videoHeight; + }; + + that.getWidth = function () { + return _calculatedWidth; + }; + + that.getHeight = function () { + return _calculatedHeight; + }; + + that.setWidth = function (width) { + _calculatedWidth = width; + }; + + that.setHeight = function (height) { + _calculatedHeight = height; + }; + + that.setInputStream = function (config) { + _config = config; + video.src = typeof config.src !== 'undefined' ? config.src : ''; + }; + + that.ended = function () { + return video.ended; + }; + + that.getConfig = function () { + return _config; + }; + + that.setAttribute = function (name, value) { + video.setAttribute(name, value); + }; + + that.pause = function () { + video.pause(); + }; + + that.play = function () { + video.play(); + }; + + that.setCurrentTime = function (time) { + if (_config.type !== "LiveStream") { + video.currentTime = time; + } + }; + + that.addEventListener = function (event, f, bool) { + if (_eventNames.indexOf(event) !== -1) { + if (!_eventHandlers[event]) { + _eventHandlers[event] = []; + } + _eventHandlers[event].push(f); + } else { + video.addEventListener(event, f, bool); + } + }; + + that.clearEventHandlers = function () { + _eventNames.forEach(function (eventName) { + var handlers = _eventHandlers[eventName]; + if (handlers && handlers.length > 0) { + handlers.forEach(function (handler) { + video.removeEventListener(eventName, handler); + }); + } + }); + }; + + that.trigger = function (eventName, args) { + var j, + handlers = _eventHandlers[eventName]; + + if (eventName === 'canrecord') { + initSize(); + } + if (handlers && handlers.length > 0) { + for (j = 0; j < handlers.length; j++) { + handlers[j].apply(that, args); + } + } + }; + + that.setTopRight = function (topRight) { + _topRight.x = topRight.x; + _topRight.y = topRight.y; + }; + + that.getTopRight = function () { + return _topRight; + }; + + that.setCanvasSize = function (size) { + _canvasSize.x = size.x; + _canvasSize.y = size.y; + }; + + that.getCanvasSize = function () { + return _canvasSize; + }; + + that.getFrame = function () { + return video; + }; + + return that; + }; + + InputStream.createLiveStream = function (video) { + video.setAttribute("autoplay", true); + var that = InputStream.createVideoStream(video); + + that.ended = function () { + return false; + }; + + return that; + }; + + InputStream.createImageStream = function () { + var that = {}; + var _config = null; + + var width = 0, + height = 0, + frameIdx = 0, + paused = true, + loaded = false, + imgArray = null, + size = 0, + offset = 1, + baseUrl = null, + ended = false, + calculatedWidth, + calculatedHeight, + _eventNames = ['canrecord', 'ended'], + _eventHandlers = {}, + _topRight = { x: 0, y: 0 }, + _canvasSize = { x: 0, y: 0 }; + + function loadImages() { + loaded = false; + _image_loader2['default'].load(baseUrl, function (imgs) { + imgArray = imgs; + width = imgs[0].width; + height = imgs[0].height; + calculatedWidth = _config.size ? width / height > 1 ? _config.size : Math.floor(width / height * _config.size) : width; + calculatedHeight = _config.size ? width / height > 1 ? Math.floor(height / width * _config.size) : _config.size : height; + _canvasSize.x = calculatedWidth; + _canvasSize.y = calculatedHeight; + loaded = true; + frameIdx = 0; + setTimeout(function () { + publishEvent("canrecord", []); + }, 0); + }, offset, size, _config.sequence); + } + + function publishEvent(eventName, args) { + var j, + handlers = _eventHandlers[eventName]; + + if (handlers && handlers.length > 0) { + for (j = 0; j < handlers.length; j++) { + handlers[j].apply(that, args); + } + } + } + + that.trigger = publishEvent; + + that.getWidth = function () { + return calculatedWidth; + }; + + that.getHeight = function () { + return calculatedHeight; + }; + + that.setWidth = function (newWidth) { + calculatedWidth = newWidth; + }; + + that.setHeight = function (newHeight) { + calculatedHeight = newHeight; + }; + + that.getRealWidth = function () { + return width; + }; + + that.getRealHeight = function () { + return height; + }; + + that.setInputStream = function (stream) { + _config = stream; + if (stream.sequence === false) { + baseUrl = stream.src; + size = 1; + } else { + baseUrl = stream.src; + size = stream.length; + } + loadImages(); + }; + + that.ended = function () { + return ended; + }; + + that.setAttribute = function () {}; + + that.getConfig = function () { + return _config; + }; + + that.pause = function () { + paused = true; + }; + + that.play = function () { + paused = false; + }; + + that.setCurrentTime = function (time) { + frameIdx = time; + }; + + that.addEventListener = function (event, f) { + if (_eventNames.indexOf(event) !== -1) { + if (!_eventHandlers[event]) { + _eventHandlers[event] = []; + } + _eventHandlers[event].push(f); + } + }; + + that.setTopRight = function (topRight) { + _topRight.x = topRight.x; + _topRight.y = topRight.y; + }; + + that.getTopRight = function () { + return _topRight; + }; + + that.setCanvasSize = function (canvasSize) { + _canvasSize.x = canvasSize.x; + _canvasSize.y = canvasSize.y; + }; + + that.getCanvasSize = function () { + return _canvasSize; + }; + + that.getFrame = function () { + var frame; + + if (!loaded) { + return null; + } + if (!paused) { + frame = imgArray[frameIdx]; + if (frameIdx < size - 1) { + frameIdx++; + } else { + setTimeout(function () { + ended = true; + publishEvent("ended", []); + }, 0); + } + } + return frame; + }; + + return that; + }; + + exports['default'] = InputStream; + module.exports = exports['default']; + +/***/ }, +/* 73 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var ImageLoader = {}; + ImageLoader.load = function (directory, callback, offset, size, sequence) { + var htmlImagesSrcArray = new Array(size), + htmlImagesArray = new Array(htmlImagesSrcArray.length), + i, + img, + num; + + if (sequence === false) { + htmlImagesSrcArray[0] = directory; + } else { + for (i = 0; i < htmlImagesSrcArray.length; i++) { + num = offset + i; + htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg"; + } + } + htmlImagesArray.notLoaded = []; + htmlImagesArray.addImage = function (image) { + htmlImagesArray.notLoaded.push(image); + }; + htmlImagesArray.loaded = function (loadedImg) { + var notloadedImgs = htmlImagesArray.notLoaded; + for (var x = 0; x < notloadedImgs.length; x++) { + if (notloadedImgs[x] === loadedImg) { + notloadedImgs.splice(x, 1); + for (var y = 0; y < htmlImagesSrcArray.length; y++) { + var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/")); + if (loadedImg.src.lastIndexOf(imgName) !== -1) { + htmlImagesArray[y] = loadedImg; + break; + } + } + break; + } + } + if (notloadedImgs.length === 0) { + console.log("Images loaded"); + callback.apply(null, [htmlImagesArray]); + } + }; + + for (i = 0; i < htmlImagesSrcArray.length; i++) { + img = new Image(); + htmlImagesArray.addImage(img); + addOnloadHandler(img, htmlImagesArray); + img.src = htmlImagesSrcArray[i]; + } + }; + + function addOnloadHandler(img, htmlImagesArray) { + img.onload = function () { + htmlImagesArray.loaded(this); + }; + } + + exports["default"] = ImageLoader; + module.exports = exports["default"]; + +/***/ }, +/* 74 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var FrameGrabber = {}; + + FrameGrabber.create = function (inputStream, canvas) { + var _that = {}, + _streamConfig = inputStream.getConfig(), + _video_size = _cv_utils2["default"].imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), + _canvasSize = inputStream.getCanvasSize(), + _size = _cv_utils2["default"].imageRef(inputStream.getWidth(), inputStream.getHeight()), + topRight = inputStream.getTopRight(), + _sx = topRight.x, + _sy = topRight.y, + _canvas, + _ctx = null, + _data = null; + + _canvas = canvas ? canvas : document.createElement("canvas"); + _canvas.width = _canvasSize.x; + _canvas.height = _canvasSize.y; + _ctx = _canvas.getContext("2d"); + _data = new Uint8Array(_size.x * _size.y); + console.log("FrameGrabber", JSON.stringify({ + size: _size, + topRight: topRight, + videoSize: _video_size, + canvasSize: _canvasSize + })); + + /** + * Uses the given array as frame-buffer + */ + _that.attachData = function (data) { + _data = data; + }; + + /** + * Returns the used frame-buffer + */ + _that.getData = function () { + return _data; + }; + + /** + * Fetches a frame from the input-stream and puts into the frame-buffer. + * The image-data is converted to gray-scale and then half-sampled if configured. + */ + _that.grab = function () { + var doHalfSample = _streamConfig.halfSample, + frame = inputStream.getFrame(), + ctxData; + if (frame) { + _ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y); + ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data; + if (doHalfSample) { + _cv_utils2["default"].grayAndHalfSampleFromCanvasData(ctxData, _size, _data); + } else { + _cv_utils2["default"].computeGray(ctxData, _data, _streamConfig); + } + return true; + } else { + return false; + } + }; + + _that.getSize = function () { + return _size; + }; + + return _that; + }; + + exports["default"] = FrameGrabber; + module.exports = exports["default"]; + +/***/ } +/******/ ]) }); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('input_stream',["image_loader"], function(ImageLoader) { - "use strict"; - - var InputStream = {}; - InputStream.createVideoStream = function(video) { - var that = {}, - _config = null, - _eventNames = ['canrecord', 'ended'], - _eventHandlers = {}, - _calculatedWidth, - _calculatedHeight, - _topRight = {x: 0, y: 0}, - _canvasSize = {x: 0, y: 0}; - - function initSize() { - var width = video.videoWidth, - height = video.videoHeight; - - _calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; - _calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; - - _canvasSize.x = _calculatedWidth; - _canvasSize.y = _calculatedHeight; - } - - that.getRealWidth = function() { - return video.videoWidth; - }; - - that.getRealHeight = function() { - return video.videoHeight; - }; - - that.getWidth = function() { - return _calculatedWidth; - }; - - that.getHeight = function() { - return _calculatedHeight; - }; - - that.setWidth = function(width) { - _calculatedWidth = width; - }; - - that.setHeight = function(height) { - _calculatedHeight = height; - }; - - that.setInputStream = function(config) { - _config = config; - video.src = (typeof config.src !== 'undefined') ? config.src : ''; - }; - - that.ended = function() { - return video.ended; - }; - - that.getConfig = function() { - return _config; - }; - - that.setAttribute = function(name, value) { - video.setAttribute(name, value); - }; - - that.pause = function() { - video.pause(); - }; - - that.play = function() { - video.play(); - }; - - that.setCurrentTime = function(time) { - if (_config.type !== "LiveStream") - video.currentTime = time; - }; - - that.addEventListener = function(event, f, bool) { - if (_eventNames.indexOf(event) !== -1) { - if (!_eventHandlers[event]) { - _eventHandlers[event] = []; - } - _eventHandlers[event].push(f); - } else { - video.addEventListener(event, f, bool); - } - }; - - that.clearEventHandlers = function() { - _eventNames.forEach(function(eventName) { - var handlers = _eventHandlers[eventName]; - if (handlers && handlers.length > 0) { - handlers.forEach(function(handler) { - video.removeEventListener(eventName, handler); - }); - } - }); - }; - - that.trigger = function(eventName, args) { - var j, - handlers = _eventHandlers[eventName]; - - if (eventName === 'canrecord') { - initSize(); - } - if (handlers && handlers.length > 0) { - for ( j = 0; j < handlers.length; j++) { - handlers[j].apply(that, args); - } - } - }; - - that.setTopRight = function(topRight) { - _topRight.x = topRight.x; - _topRight.y = topRight.y; - }; - - that.getTopRight = function() { - return _topRight; - }; - - that.setCanvasSize = function(size) { - _canvasSize.x = size.x; - _canvasSize.y = size.y; - }; - - that.getCanvasSize = function() { - return _canvasSize; - }; - - that.getFrame = function() { - return video; - }; - - return that; - }; - - InputStream.createLiveStream = function(video) { - video.setAttribute("autoplay", true); - var that = InputStream.createVideoStream(video); - - that.ended = function() { - return false; - }; - - return that; - }; - - InputStream.createImageStream = function() { - var that = {}; - var _config = null; - - var width = 0, - height = 0, - frameIdx = 0, - paused = true, - loaded = false, - imgArray = null, - size = 0, - offset = 1, - baseUrl = null, - ended = false, - calculatedWidth, - calculatedHeight, - _eventNames = ['canrecord', 'ended'], - _eventHandlers = {}, - _topRight = {x: 0, y: 0}, - _canvasSize = {x: 0, y: 0}; - - function loadImages() { - loaded = false; - ImageLoader.load(baseUrl, function(imgs) { - imgArray = imgs; - width = imgs[0].width; - height = imgs[0].height; - calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; - calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; - _canvasSize.x = calculatedWidth; - _canvasSize.y = calculatedHeight; - loaded = true; - frameIdx = 0; - setTimeout(function() { - publishEvent("canrecord", []); - }, 0); - }, offset, size, _config.sequence); - } - - function publishEvent(eventName, args) { - var j, - handlers = _eventHandlers[eventName]; - - if (handlers && handlers.length > 0) { - for ( j = 0; j < handlers.length; j++) { - handlers[j].apply(that, args); - } - } - } - - - that.trigger = publishEvent; - - that.getWidth = function() { - return calculatedWidth; - }; - - that.getHeight = function() { - return calculatedHeight; - }; - - that.setWidth = function(width) { - calculatedWidth = width; - }; - - that.setHeight = function(height) { - calculatedHeight = height; - }; - - that.getRealWidth = function() { - return width; - }; - - that.getRealHeight = function() { - return height; - }; - - that.setInputStream = function(stream) { - _config = stream; - if (stream.sequence === false) { - baseUrl = stream.src; - size = 1; - } else { - baseUrl = stream.src; - size = stream.length; - } - loadImages(); - }; - - that.ended = function() { - return ended; - }; - - that.setAttribute = function() {}; - - that.getConfig = function() { - return _config; - }; - - that.pause = function() { - paused = true; - }; - - that.play = function() { - paused = false; - }; - - that.setCurrentTime = function(time) { - frameIdx = time; - }; - - that.addEventListener = function(event, f) { - if (_eventNames.indexOf(event) !== -1) { - if (!_eventHandlers[event]) { - _eventHandlers[event] = []; - } - _eventHandlers[event].push(f); - } - }; - - that.setTopRight = function(topRight) { - _topRight.x = topRight.x; - _topRight.y = topRight.y; - }; - - that.getTopRight = function() { - return _topRight; - }; - - that.setCanvasSize = function(size) { - _canvasSize.x = size.x; - _canvasSize.y = size.y; - }; - - that.getCanvasSize = function() { - return _canvasSize; - }; - - that.getFrame = function() { - var frame; - - if (!loaded){ - return null; - } - if (!paused) { - frame = imgArray[frameIdx]; - if (frameIdx < (size - 1)) { - frameIdx++; - } else { - setTimeout(function() { - ended = true; - publishEvent("ended", []); - }, 0); - } - } - return frame; - }; - - return that; - }; - - return (InputStream); -}); - -/* - * typedefs.js - * Normalizes browser-specific prefixes - */ - -glMatrixArrayType = Float32Array; -if (typeof window !== 'undefined') { - window.requestAnimFrame = (function () { - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { - window.setTimeout(callback, 1000 / 60); - }; - })(); - - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; - window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; -} -Math.imul = Math.imul || function(a, b) { - var ah = (a >>> 16) & 0xffff, - al = a & 0xffff, - bh = (b >>> 16) & 0xffff, - bl = b & 0xffff; - // the shift by 0 fixes the sign on the high part - // the final |0 converts the unsigned value into a signed value - return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0); -}; -define("typedefs", (function (global) { - return function () { - var ret, fn; - return ret || global.typedefs; - }; -}(this))); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('subImage',["typedefs"], function() { - "use strict"; - - /** - * Construct representing a part of another {ImageWrapper}. Shares data - * between the parent and the child. - * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) - * @param size {ImageRef} The size of the resulting image - * @param I {ImageWrapper} The {ImageWrapper} to share from - * @returns {SubImage} A shared part of the original image - */ - function SubImage(from, size, I) { - if (!I) { - I = { - data : null, - size : size - }; - } - this.data = I.data; - this.originalSize = I.size; - this.I = I; - - this.from = from; - this.size = size; - } - - /** - * Displays the {SubImage} in a given canvas - * @param canvas {Canvas} The canvas element to write to - * @param scale {Number} Scale which is applied to each pixel-value - */ - SubImage.prototype.show = function(canvas, scale) { - var ctx, - frame, - data, - current, - y, - x, - pixel; - - if (!scale) { - scale = 1.0; - } - ctx = canvas.getContext('2d'); - canvas.width = this.size.x; - canvas.height = this.size.y; - frame = ctx.getImageData(0, 0, canvas.width, canvas.height); - data = frame.data; - current = 0; - for (y = 0; y < this.size.y; y++) { - for (x = 0; x < this.size.x; x++) { - pixel = y * this.size.x + x; - current = this.get(x, y) * scale; - data[pixel * 4 + 0] = current; - data[pixel * 4 + 1] = current; - data[pixel * 4 + 2] = current; - data[pixel * 4 + 3] = 255; - } - } - frame.data = data; - ctx.putImageData(frame, 0, 0); - }; - - /** - * Retrieves a given pixel position from the {SubImage} - * @param x {Number} The x-position - * @param y {Number} The y-position - * @returns {Number} The grayscale value at the pixel-position - */ - SubImage.prototype.get = function(x, y) { - return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x]; - }; - - /** - * Updates the underlying data from a given {ImageWrapper} - * @param image {ImageWrapper} The updated image - */ - SubImage.prototype.updateData = function(image) { - this.originalSize = image.size; - this.data = image.data; - }; - - /** - * Updates the position of the shared area - * @param from {x,y} The new location - * @returns {SubImage} returns {this} for possible chaining - */ - SubImage.prototype.updateFrom = function(from) { - this.from = from; - return this; - }; - - return (SubImage); -}); -/** - * @fileoverview gl-matrix - High performance matrix and vector operations - * @author Brandon Jones - * @author Colin MacKenzie IV - * @version 2.3.0 - */ - -/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. */ - -!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define('gl-matrix',n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}(this,function(){return function(t){function n(a){if(r[a])return r[a].exports;var e=r[a]={exports:{},id:a,loaded:!1};return t[a].call(e.exports,e,e.exports,n),e.loaded=!0,e.exports}var r={};return n.m=t,n.c=r,n.p="",n(0)}([function(t,n,r){n.glMatrix=r(1),n.mat2=r(2),n.mat2d=r(3),n.mat3=r(4),n.mat4=r(5),n.quat=r(6),n.vec2=r(9),n.vec3=r(7),n.vec4=r(8)},function(t,n){var r={};r.EPSILON=1e-6,r.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,r.RANDOM=Math.random,r.setMatrixArrayType=function(t){GLMAT_ARRAY_TYPE=t};var a=Math.PI/180;r.toRadian=function(t){return t*a},t.exports=r},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;return o?(o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t):null},e.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},e.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*i+u*c,t[1]=e*i+o*c,t[2]=a*f+u*s,t[3]=e*f+o*s,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+u*i,t[1]=e*c+o*i,t[2]=a*-i+u*c,t[3]=e*-i+o*c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*c,t[3]=o*c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},e.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},e.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(6);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=r*u-a*e;return c?(c=1/c,t[0]=u*c,t[1]=-a*c,t[2]=-e*c,t[3]=r*c,t[4]=(e*i-u*o)*c,t[5]=(a*o-r*i)*c,t):null},e.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1],h=r[2],M=r[3],l=r[4],v=r[5];return t[0]=a*f+u*s,t[1]=e*f+o*s,t[2]=a*h+u*M,t[3]=e*h+o*M,t[4]=a*l+u*v+i,t[5]=e*l+o*v+c,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*f,t[1]=e*s+o*f,t[2]=a*-f+u*s,t[3]=e*-f+o*s,t[4]=i,t[5]=c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a*f,t[1]=e*f,t[2]=u*s,t[3]=o*s,t[4]=i,t[5]=c,t},e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*f+u*s+i,t[5]=e*f+o*s+c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},e.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},e.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=s*o-i*f,M=-s*u+i*c,l=f*u-o*c,v=r*h+a*M+e*l;return v?(v=1/v,t[0]=h*v,t[1]=(-s*a+e*f)*v,t[2]=(i*a-e*o)*v,t[3]=M*v,t[4]=(s*r-e*c)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-f*r+a*c)*v,t[8]=(o*r-a*u)*v,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8];return t[0]=o*s-i*f,t[1]=e*f-a*s,t[2]=a*i-e*o,t[3]=i*c-u*s,t[4]=r*s-e*c,t[5]=e*u-r*i,t[6]=u*f-o*c,t[7]=a*c-r*f,t[8]=r*o-a*u,t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8];return n*(f*u-o*c)+r*(-f*e+o*i)+a*(c*e-u*i)},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1],v=r[2],m=r[3],p=r[4],d=r[5],A=r[6],R=r[7],w=r[8];return t[0]=M*a+l*o+v*f,t[1]=M*e+l*i+v*s,t[2]=M*u+l*c+v*h,t[3]=m*a+p*o+d*f,t[4]=m*e+p*i+d*s,t[5]=m*u+p*c+d*h,t[6]=A*a+R*o+w*f,t[7]=A*e+R*i+w*s,t[8]=A*u+R*c+w*h,t},e.mul=e.multiply,e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=M*a+l*o+f,t[7]=M*e+l*i+s,t[8]=M*u+l*c+h,t},e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=Math.sin(r),l=Math.cos(r);return t[0]=l*a+M*o,t[1]=l*e+M*i,t[2]=l*u+M*c,t[3]=l*o-M*a,t[4]=l*i-M*e,t[5]=l*c-M*u,t[6]=f,t[7]=s,t[8]=h,t},e.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[3]=s-d,t[6]=M+p,t[1]=s+d,t[4]=1-f-v,t[7]=l-m,t[2]=M-p,t[5]=l+m,t[8]=1-f-h,t},e.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(c*P-o*b-f*x)*D,t[2]=(o*T-i*P+f*y)*D,t[3]=(e*T-a*b-u*E)*D,t[4]=(r*b-e*P+u*x)*D,t[5]=(a*P-r*T-u*y)*D,t[6]=(m*g-p*Y+d*q)*D,t[7]=(p*w-v*g-d*R)*D,t[8]=(v*Y-m*w+d*A)*D,t):null},e.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(e*T-a*b-u*E)*D,t[2]=(m*g-p*Y+d*q)*D,t[3]=(M*Y-h*g-l*q)*D,t[4]=(c*P-o*b-f*x)*D,t[5]=(r*b-e*P+u*x)*D,t[6]=(p*w-v*g-d*R)*D,t[7]=(s*g-M*w+l*R)*D,t[8]=(o*T-i*P+f*y)*D,t[9]=(a*P-r*T-u*y)*D,t[10]=(v*Y-m*w+d*A)*D,t[11]=(h*w-s*Y-l*A)*D,t[12]=(i*x-o*E-c*y)*D,t[13]=(r*E-a*x+e*y)*D,t[14]=(m*R-v*q-p*A)*D,t[15]=(s*q-h*R+M*A)*D,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15];return t[0]=i*(M*d-l*p)-h*(c*d-f*p)+m*(c*l-f*M),t[1]=-(a*(M*d-l*p)-h*(e*d-u*p)+m*(e*l-u*M)),t[2]=a*(c*d-f*p)-i*(e*d-u*p)+m*(e*f-u*c),t[3]=-(a*(c*l-f*M)-i*(e*l-u*M)+h*(e*f-u*c)),t[4]=-(o*(M*d-l*p)-s*(c*d-f*p)+v*(c*l-f*M)),t[5]=r*(M*d-l*p)-s*(e*d-u*p)+v*(e*l-u*M),t[6]=-(r*(c*d-f*p)-o*(e*d-u*p)+v*(e*f-u*c)),t[7]=r*(c*l-f*M)-o*(e*l-u*M)+s*(e*f-u*c),t[8]=o*(h*d-l*m)-s*(i*d-f*m)+v*(i*l-f*h),t[9]=-(r*(h*d-l*m)-s*(a*d-u*m)+v*(a*l-u*h)),t[10]=r*(i*d-f*m)-o*(a*d-u*m)+v*(a*f-u*i),t[11]=-(r*(i*l-f*h)-o*(a*l-u*h)+s*(a*f-u*i)),t[12]=-(o*(h*p-M*m)-s*(i*p-c*m)+v*(i*M-c*h)),t[13]=r*(h*p-M*m)-s*(a*p-e*m)+v*(a*M-e*h),t[14]=-(r*(i*p-c*m)-o*(a*p-e*m)+v*(a*c-e*i)),t[15]=r*(i*M-c*h)-o*(a*M-e*h)+s*(a*c-e*i),t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8],s=t[9],h=t[10],M=t[11],l=t[12],v=t[13],m=t[14],p=t[15],d=n*o-r*u,A=n*i-a*u,R=n*c-e*u,w=r*i-a*o,q=r*c-e*o,Y=a*c-e*i,g=f*v-s*l,y=f*m-h*l,x=f*p-M*l,P=s*m-h*v,E=s*p-M*v,T=h*p-M*m;return d*T-A*E+R*P+w*x-q*y+Y*g},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],m=n[12],p=n[13],d=n[14],A=n[15],R=r[0],w=r[1],q=r[2],Y=r[3];return t[0]=R*a+w*i+q*h+Y*m,t[1]=R*e+w*c+q*M+Y*p,t[2]=R*u+w*f+q*l+Y*d,t[3]=R*o+w*s+q*v+Y*A,R=r[4],w=r[5],q=r[6],Y=r[7],t[4]=R*a+w*i+q*h+Y*m,t[5]=R*e+w*c+q*M+Y*p,t[6]=R*u+w*f+q*l+Y*d,t[7]=R*o+w*s+q*v+Y*A,R=r[8],w=r[9],q=r[10],Y=r[11],t[8]=R*a+w*i+q*h+Y*m,t[9]=R*e+w*c+q*M+Y*p,t[10]=R*u+w*f+q*l+Y*d,t[11]=R*o+w*s+q*v+Y*A,R=r[12],w=r[13],q=r[14],Y=r[15],t[12]=R*a+w*i+q*h+Y*m,t[13]=R*e+w*c+q*M+Y*p,t[14]=R*u+w*f+q*l+Y*d,t[15]=R*o+w*s+q*v+Y*A,t},e.mul=e.multiply,e.translate=function(t,n,r){var a,e,u,o,i,c,f,s,h,M,l,v,m=r[0],p=r[1],d=r[2];return n===t?(t[12]=n[0]*m+n[4]*p+n[8]*d+n[12],t[13]=n[1]*m+n[5]*p+n[9]*d+n[13],t[14]=n[2]*m+n[6]*p+n[10]*d+n[14],t[15]=n[3]*m+n[7]*p+n[11]*d+n[15]):(a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=f,t[7]=s,t[8]=h,t[9]=M,t[10]=l,t[11]=v,t[12]=a*m+i*p+h*d+n[12],t[13]=e*m+c*p+M*d+n[13],t[14]=u*m+f*p+l*d+n[14],t[15]=o*m+s*p+v*d+n[15]),t},e.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.rotate=function(t,n,r,e){var u,o,i,c,f,s,h,M,l,v,m,p,d,A,R,w,q,Y,g,y,x,P,E,T,b=e[0],D=e[1],L=e[2],_=Math.sqrt(b*b+D*D+L*L);return Math.abs(_)c?(u.cross(t,n,e),u.length(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),i.setAxisAngle(a,t,Math.PI),a):c>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+c,i.normalize(a,a))}}(),i.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],i.normalize(n,i.fromMat3(n,t))}}(),i.clone=o.clone,i.fromValues=o.fromValues,i.copy=o.copy,i.set=o.set,i.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.setAxisAngle=function(t,n,r){r=.5*r;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t},i.add=o.add,i.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*s+o*i+e*f-u*c,t[1]=e*s+o*c+u*i-a*f,t[2]=u*s+o*f+a*c-e*i,t[3]=o*s-a*i-e*c-u*f,t},i.mul=i.multiply,i.scale=o.scale,i.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+o*i,t[1]=e*c+u*i,t[2]=u*c-e*i,t[3]=o*c-a*i,t},i.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c-u*i,t[1]=e*c+o*i,t[2]=u*c+a*i,t[3]=o*c-e*i,t},i.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+e*i,t[1]=e*c-a*i,t[2]=u*c+o*i,t[3]=o*c-u*i,t},i.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},i.dot=o.dot,i.lerp=o.lerp,i.slerp=function(t,n,r,a){var e,u,o,i,c,f=n[0],s=n[1],h=n[2],M=n[3],l=r[0],v=r[1],m=r[2],p=r[3];return u=f*l+s*v+h*m+M*p,0>u&&(u=-u,l=-l,v=-v,m=-m,p=-p),1-u>1e-6?(e=Math.acos(u),o=Math.sin(e),i=Math.sin((1-a)*e)/o,c=Math.sin(a*e)/o):(i=1-a,c=a),t[0]=i*f+c*l,t[1]=i*s+c*v,t[2]=i*h+c*m,t[3]=i*M+c*p,t},i.sqlerp=function(){var t=i.create(),n=i.create();return function(r,a,e,u,o,c){return i.slerp(t,a,o,c),i.slerp(n,e,u,c),i.slerp(r,t,n,2*c*(1-c)),r}}(),i.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},i.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},i.length=o.length,i.len=i.length,i.squaredLength=o.squaredLength,i.sqrLen=i.squaredLength,i.normalize=o.normalize,i.fromMat3=function(t,n){var r,a=n[0]+n[4]+n[8];if(a>0)r=Math.sqrt(a+1),t[3]=.5*r,r=.5/r,t[0]=(n[5]-n[7])*r,t[1]=(n[6]-n[2])*r,t[2]=(n[1]-n[3])*r;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;r=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*r,r=.5/r,t[3]=(n[3*u+o]-n[3*o+u])*r,t[u]=(n[3*u+e]+n[3*e+u])*r,t[o]=(n[3*o+e]+n[3*e+o])*r}return t},i.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=i},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(3);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n},e.fromValues=function(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t},e.set=function(t,n,r,a){return t[0]=n,t[1]=r,t[2]=a,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]},e.cross=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2];return t[0]=e*c-u*i,t[1]=u*o-a*c,t[2]=a*i-e*o,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t},e.hermite=function(t,n,r,a,e,u){var o=u*u,i=o*(2*u-3)+1,c=o*(u-2)+u,f=o*(u-1),s=o*(3-2*u);return t[0]=n[0]*i+r[0]*c+a[0]*f+e[0]*s,t[1]=n[1]*i+r[1]*c+a[1]*f+e[1]*s,t[2]=n[2]*i+r[2]*c+a[2]*f+e[2]*s,t},e.bezier=function(t,n,r,a,e,u){var o=1-u,i=o*o,c=u*u,f=i*o,s=3*u*i,h=3*c*o,M=c*u;return t[0]=n[0]*f+r[0]*s+a[0]*h+e[0]*M,t[1]=n[1]*f+r[1]*s+a[1]*h+e[1]*M,t[2]=n[2]*f+r[2]*s+a[2]*h+e[2]*M,t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI,e=2*a.RANDOM()-1,u=Math.sqrt(1-e*e)*n;return t[0]=Math.cos(r)*u,t[1]=Math.sin(r)*u,t[2]=e*n,t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[3]*a+r[7]*e+r[11]*u+r[15];return o=o||1,t[0]=(r[0]*a+r[4]*e+r[8]*u+r[12])/o,t[1]=(r[1]*a+r[5]*e+r[9]*u+r[13])/o,t[2]=(r[2]*a+r[6]*e+r[10]*u+r[14])/o,t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1],u=n[2];return t[0]=a*r[0]+e*r[3]+u*r[6],t[1]=a*r[1]+e*r[4]+u*r[7],t[2]=a*r[2]+e*r[5]+u*r[8],t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t},e.rotateX=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0],u[1]=e[1]*Math.cos(a)-e[2]*Math.sin(a),u[2]=e[1]*Math.sin(a)+e[2]*Math.cos(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateY=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[2]*Math.sin(a)+e[0]*Math.cos(a),u[1]=e[1],u[2]=e[2]*Math.cos(a)-e[0]*Math.sin(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateZ=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0]*Math.cos(a)-e[1]*Math.sin(a),u[1]=e[0]*Math.sin(a)+e[1]*Math.cos(a),u[2]=e[2],t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=3),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2];return n}}(),e.angle=function(t,n){var r=e.fromValues(t[0],t[1],t[2]),a=e.fromValues(n[0],n[1],n[2]);e.normalize(r,r),e.normalize(a,a);var u=e.dot(r,a);return u>1?0:Math.acos(u)},e.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;return o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},e.random=function(t,n){return n=n||1,t[0]=a.RANDOM(),t[1]=a.RANDOM(),t[2]=a.RANDOM(),t[3]=a.RANDOM(),e.normalize(t,t),e.scale(t,t,n),t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t[3]=n[3],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=4),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],t[3]=n[i+3],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2],n[i+3]=t[3];return n}}(),e.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},e.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},e.set=function(t,n,r){return t[0]=n,t[1]=r,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1];return n*n+r*r},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;return e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},e.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},e.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},e.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},e.transformMat3=function(t,n,r){ -var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=2),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],u(t,t,o),n[i]=t[0],n[i+1]=t[1];return n}}(),e.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},t.exports=e}])}); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('cluster',["gl-matrix"], function(glMatrix) { - "use strict"; - - var vec2 = glMatrix.vec2; - /** - * Creates a cluster for grouping similar orientations of datapoints - */ - var Cluster = { - create : function(point, threshold) { - var points = [], center = { - rad : 0, - vec : vec2.clone([0, 0]) - }, pointMap = {}; - - function init() { - add(point); - updateCenter(); - } - - function add(point) { - pointMap[point.id] = point; - points.push(point); - } - - function updateCenter() { - var i, sum = 0; - for ( i = 0; i < points.length; i++) { - sum += points[i].rad; - } - center.rad = sum / points.length; - center.vec = vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]); - } - - init(); - - return { - add : function(point) { - if (!pointMap[point.id]) { - add(point); - updateCenter(); - } - }, - fits : function(point) { - // check cosine similarity to center-angle - var similarity = Math.abs(vec2.dot(point.point.vec, center.vec)); - if (similarity > threshold) { - return true; - } - return false; - }, - getPoints : function() { - return points; - }, - getCenter : function() { - return center; - } - }; - }, - createPoint : function(point, id, property) { - return { - rad : point[property], - point : point, - id : id - }; - } - }; - - return (Cluster); -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('array_helper',[],function() { - "use strict"; - - return { - init : function(arr, val) { - var l = arr.length; - while (l--) { - arr[l] = val; - } - }, - - /** - * Shuffles the content of an array - * @return {Array} the array itself shuffled - */ - shuffle : function(arr) { - var i = arr.length - 1, j, x; - for (i; i >= 0; i--) { - j = Math.floor(Math.random() * i); - x = arr[i]; - arr[i] = arr[j]; - arr[j] = x; - } - return arr; - }, - - toPointList : function(arr) { - var i, j, row = [], rows = []; - for ( i = 0; i < arr.length; i++) { - row = []; - for ( j = 0; j < arr[i].length; j++) { - row[j] = arr[i][j]; - } - rows[i] = "[" + row.join(",") + "]"; - } - return "[" + rows.join(",\r\n") + "]"; - }, - - /** - * returns the elements which's score is bigger than the threshold - * @return {Array} the reduced array - */ - threshold : function(arr, threshold, scoreFunc) { - var i, queue = []; - for ( i = 0; i < arr.length; i++) { - if (scoreFunc.apply(arr, [arr[i]]) >= threshold) { - queue.push(arr[i]); - } - } - return queue; - }, - - maxIndex : function(arr) { - var i, max = 0; - for ( i = 0; i < arr.length; i++) { - if (arr[i] > arr[max]) { - max = i; - } - } - return max; - }, - - max : function(arr) { - var i, max = 0; - for ( i = 0; i < arr.length; i++) { - if (arr[i] > max) { - max = arr[i]; - } - } - return max; - }, - - sum: function(arr) { - var length = arr.length, - sum = 0; - - while(length--) { - sum += arr[length]; - } - return sum; - } - }; -}); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('cv_utils',['cluster', "array_helper", "gl-matrix"], function(Cluster2, ArrayHelper, glMatrix) { - - "use strict"; - /* - * cv_utils.js - * Collection of CV functions and libraries - */ - - /** - * Namespace for various CV alorithms - * @class Represents a collection of useful CV algorithms/functions - */ - - var CVUtils = {}, - vec2 = glMatrix.vec2, - vec3 = glMatrix.vec3; - - /** - * @param x x-coordinate - * @param y y-coordinate - * @return ImageReference {x,y} Coordinate - */ - CVUtils.imageRef = function(x, y) { - var that = { - x : x, - y : y, - toVec2 : function() { - return vec2.clone([this.x, this.y]); - }, - toVec3 : function() { - return vec3.clone([this.x, this.y, 1]); - }, - round : function() { - this.x = this.x > 0.0 ? Math.floor(this.x + 0.5) : Math.floor(this.x - 0.5); - this.y = this.y > 0.0 ? Math.floor(this.y + 0.5) : Math.floor(this.y - 0.5); - return this; - } - }; - return that; - }; - - /** - * Computes an integral image of a given grayscale image. - * @param imageDataContainer {ImageDataContainer} the image to be integrated - */ - CVUtils.computeIntegralImage2 = function(imageWrapper, integralWrapper) { - var imageData = imageWrapper.data; - var width = imageWrapper.size.x; - var height = imageWrapper.size.y; - var integralImageData = integralWrapper.data; - var sum = 0, posA = 0, posB = 0, posC = 0, posD = 0, x, y; - - // sum up first column - posB = width; - sum = 0; - for ( y = 1; y < height; y++) { - sum += imageData[posA]; - integralImageData[posB] += sum; - posA += width; - posB += width; - } - - posA = 0; - posB = 1; - sum = 0; - for ( x = 1; x < width; x++) { - sum += imageData[posA]; - integralImageData[posB] += sum; - posA++; - posB++; - } - - for ( y = 1; y < height; y++) { - posA = y * width + 1; - posB = (y - 1) * width + 1; - posC = y * width; - posD = (y - 1) * width; - for ( x = 1; x < width; x++) { - integralImageData[posA] += imageData[posA] + integralImageData[posB] + integralImageData[posC] - integralImageData[posD]; - posA++; - posB++; - posC++; - posD++; - } - } - }; - - CVUtils.computeIntegralImage = function(imageWrapper, integralWrapper) { - var imageData = imageWrapper.data; - var width = imageWrapper.size.x; - var height = imageWrapper.size.y; - var integralImageData = integralWrapper.data; - var sum = 0; - - // sum up first row - for (var i = 0; i < width; i++) { - sum += imageData[i]; - integralImageData[i] = sum; - } - - for (var v = 1; v < height; v++) { - sum = 0; - for (var u = 0; u < width; u++) { - sum += imageData[v * width + u]; - integralImageData[((v) * width) + u] = sum + integralImageData[(v - 1) * width + u]; - } - } - }; - - CVUtils.thresholdImage = function(imageWrapper, threshold, targetWrapper) { - if (!targetWrapper) { - targetWrapper = imageWrapper; - } - var imageData = imageWrapper.data, length = imageData.length, targetData = targetWrapper.data; - - while (length--) { - targetData[length] = imageData[length] < threshold ? 1 : 0; - } - }; - - CVUtils.computeHistogram = function(imageWrapper, bitsPerPixel) { - if (!bitsPerPixel) { - bitsPerPixel = 8; - } - var imageData = imageWrapper.data, - length = imageData.length, - bitShift = 8 - bitsPerPixel, - bucketCnt = 1 << bitsPerPixel, - hist = new Int32Array(bucketCnt); - - while (length--) { - hist[imageData[length] >> bitShift]++; - } - return hist; - }; - - CVUtils.sharpenLine = function(line) { - var i, - length = line.length, - left = line[0], - center = line[1], - right; - - for (i = 1; i < length - 1; i++) { - right = line[i + 1]; - // -1 4 -1 kernel - line[i-1] = (((center * 2) - left - right)) & 255; - left = center; - center = right; - } - return line; - }; - - CVUtils.determineOtsuThreshold = function(imageWrapper, bitsPerPixel) { - if (!bitsPerPixel) { - bitsPerPixel = 8; - } - var hist, - threshold, - bitShift = 8 - bitsPerPixel; - - function px(init, end) { - var sum = 0, i; - for ( i = init; i <= end; i++) { - sum += hist[i]; - } - return sum; - } - - function mx(init, end) { - var i, sum = 0; - - for ( i = init; i <= end; i++) { - sum += i * hist[i]; - } - - return sum; - } - - function determineThreshold() { - var vet = [0], p1, p2, p12, k, m1, m2, m12, - max = (1 << bitsPerPixel) - 1; - - hist = CVUtils.computeHistogram(imageWrapper, bitsPerPixel); - for ( k = 1; k < max; k++) { - p1 = px(0, k); - p2 = px(k + 1, max); - p12 = p1 * p2; - if (p12 === 0) { - p12 = 1; - } - m1 = mx(0, k) * p2; - m2 = mx(k + 1, max) * p1; - m12 = m1 - m2; - vet[k] = m12 * m12 / p12; - } - return ArrayHelper.maxIndex(vet); - } - - threshold = determineThreshold(); - return threshold << bitShift; - }; - - CVUtils.otsuThreshold = function(imageWrapper, targetWrapper) { - var threshold = CVUtils.determineOtsuThreshold(imageWrapper); - - CVUtils.thresholdImage(imageWrapper, threshold, targetWrapper); - return threshold; - }; - - // local thresholding - CVUtils.computeBinaryImage = function(imageWrapper, integralWrapper, targetWrapper) { - CVUtils.computeIntegralImage(imageWrapper, integralWrapper); - - if (!targetWrapper) { - targetWrapper = imageWrapper; - } - var imageData = imageWrapper.data; - var targetData = targetWrapper.data; - var width = imageWrapper.size.x; - var height = imageWrapper.size.y; - var integralImageData = integralWrapper.data; - var sum = 0, v, u, kernel = 3, A, B, C, D, avg, size = (kernel * 2 + 1) * (kernel * 2 + 1); - - // clear out top & bottom-border - for ( v = 0; v <= kernel; v++) { - for ( u = 0; u < width; u++) { - targetData[((v) * width) + u] = 0; - targetData[(((height - 1) - v) * width) + u] = 0; - } - } - - // clear out left & right border - for ( v = kernel; v < height - kernel; v++) { - for ( u = 0; u <= kernel; u++) { - targetData[((v) * width) + u] = 0; - targetData[((v) * width) + (width - 1 - u)] = 0; - } - } - - for ( v = kernel + 1; v < height - kernel - 1; v++) { - for ( u = kernel + 1; u < width - kernel; u++) { - A = integralImageData[(v - kernel - 1) * width + (u - kernel - 1)]; - B = integralImageData[(v - kernel - 1) * width + (u + kernel)]; - C = integralImageData[(v + kernel) * width + (u - kernel - 1)]; - D = integralImageData[(v + kernel) * width + (u + kernel)]; - sum = D - C - B + A; - avg = sum / (size); - targetData[v * width + u] = imageData[v * width + u] > (avg + 5) ? 0 : 1; - } - } - }; - - CVUtils.cluster = function(points, threshold, property) { - var i, k, cluster, point, clusters = []; - - if (!property) { - property = "rad"; - } - - function addToCluster(point) { - var found = false; - for ( k = 0; k < clusters.length; k++) { - cluster = clusters[k]; - if (cluster.fits(point)) { - cluster.add(point); - found = true; - } - } - return found; - } - - // iterate over each cloud - for ( i = 0; i < points.length; i++) { - point = Cluster2.createPoint(points[i], i, property); - if (!addToCluster(point)) { - clusters.push(Cluster2.create(point, threshold)); - } - } - - return clusters; - - }; - - CVUtils.Tracer = { - trace : function(points, vec) { - var iteration, maxIterations = 10, top = [], result = [], centerPos = 0, currentPos = 0; - - function trace(idx, forward) { - var from, to, toIdx, predictedPos, thresholdX = 1, thresholdY = Math.abs(vec[1] / 10), found = false; - - function match(pos, predicted) { - if (pos.x > (predicted.x - thresholdX) && pos.x < (predicted.x + thresholdX) && pos.y > (predicted.y - thresholdY) && pos.y < (predicted.y + thresholdY)) { - return true; - } else { - return false; - } - } - - // check if the next index is within the vec specifications - // if not, check as long as the threshold is met - - from = points[idx]; - if (forward) { - predictedPos = { - x : from.x + vec[0], - y : from.y + vec[1] - }; - } else { - predictedPos = { - x : from.x - vec[0], - y : from.y - vec[1] - }; - } - - toIdx = forward ? idx + 1 : idx - 1; - to = points[toIdx]; - while (to && ( found = match(to, predictedPos)) !== true && (Math.abs(to.y - from.y) < vec[1])) { - toIdx = forward ? toIdx + 1 : toIdx - 1; - to = points[toIdx]; - } - - return found ? toIdx : null; - } - - for ( iteration = 0; iteration < maxIterations; iteration++) { - // randomly select point to start with - centerPos = Math.floor(Math.random() * points.length); - - // trace forward - top = []; - currentPos = centerPos; - top.push(points[currentPos]); - while (( currentPos = trace(currentPos, true)) !== null) { - top.push(points[currentPos]); - } - if (centerPos > 0) { - currentPos = centerPos; - while (( currentPos = trace(currentPos, false)) !== null) { - top.push(points[currentPos]); - } - } - - if (top.length > result.length) { - result = top; - } - } - - return result; - - } - }; - - CVUtils.DILATE = 1; - CVUtils.ERODE = 2; - - CVUtils.dilate = function(inImageWrapper, outImageWrapper) { - var v, u, inImageData = inImageWrapper.data, outImageData = outImageWrapper.data, height = inImageWrapper.size.y, width = inImageWrapper.size.x, sum, yStart1, yStart2, xStart1, xStart2; - - for ( v = 1; v < height - 1; v++) { - for ( u = 1; u < width - 1; u++) { - yStart1 = v - 1; - yStart2 = v + 1; - xStart1 = u - 1; - xStart2 = u + 1; - sum = inImageData[yStart1 * width + xStart1]/* + inImageData[yStart1*width+u] */ + inImageData[yStart1 * width + xStart2] + - /* inImageData[v*width+xStart1] + */ - inImageData[v * width + u] + /* inImageData[v*width+xStart2] +*/ - inImageData[yStart2 * width + xStart1]/* + inImageData[yStart2*width+u]*/ + inImageData[yStart2 * width + xStart2]; - outImageData[v * width + u] = sum > 0 ? 1 : 0; - } - } - }; - - CVUtils.erode = function(inImageWrapper, outImageWrapper) { - var v, u, inImageData = inImageWrapper.data, outImageData = outImageWrapper.data, height = inImageWrapper.size.y, width = inImageWrapper.size.x, sum, yStart1, yStart2, xStart1, xStart2; - - for ( v = 1; v < height - 1; v++) { - for ( u = 1; u < width - 1; u++) { - yStart1 = v - 1; - yStart2 = v + 1; - xStart1 = u - 1; - xStart2 = u + 1; - sum = inImageData[yStart1 * width + xStart1]/* + inImageData[yStart1*width+u] */ + inImageData[yStart1 * width + xStart2] + - /* inImageData[v*width+xStart1] + */ - inImageData[v * width + u] + /* inImageData[v*width+xStart2] +*/ - inImageData[yStart2 * width + xStart1]/* + inImageData[yStart2*width+u]*/ + inImageData[yStart2 * width + xStart2]; - outImageData[v * width + u] = sum === 5 ? 1 : 0; - } - } - }; - - CVUtils.subtract = function(aImageWrapper, bImageWrapper, resultImageWrapper) { - if (!resultImageWrapper) { - resultImageWrapper = aImageWrapper; - } - var length = aImageWrapper.data.length, aImageData = aImageWrapper.data, bImageData = bImageWrapper.data, cImageData = resultImageWrapper.data; - - while (length--) { - cImageData[length] = aImageData[length] - bImageData[length]; - } - }; - - CVUtils.bitwiseOr = function(aImageWrapper, bImageWrapper, resultImageWrapper) { - if (!resultImageWrapper) { - resultImageWrapper = aImageWrapper; - } - var length = aImageWrapper.data.length, aImageData = aImageWrapper.data, bImageData = bImageWrapper.data, cImageData = resultImageWrapper.data; - - while (length--) { - cImageData[length] = aImageData[length] || bImageData[length]; - } - }; - - CVUtils.countNonZero = function(imageWrapper) { - var length = imageWrapper.data.length, data = imageWrapper.data, sum = 0; - - while (length--) { - sum += data[length]; - } - return sum; - }; - - CVUtils.topGeneric = function(list, top, scoreFunc) { - var i, minIdx = 0, min = 0, queue = [], score, hit, pos; - - for ( i = 0; i < top; i++) { - queue[i] = { - score : 0, - item : null - }; - } - - for ( i = 0; i < list.length; i++) { - score = scoreFunc.apply(this, [list[i]]); - if (score > min) { - hit = queue[minIdx]; - hit.score = score; - hit.item = list[i]; - min = Number.MAX_VALUE; - for ( pos = 0; pos < top; pos++) { - if (queue[pos].score < min) { - min = queue[pos].score; - minIdx = pos; - } - } - } - } - - return queue; - }; - - CVUtils.grayArrayFromImage = function(htmlImage, offsetX, ctx, array) { - ctx.drawImage(htmlImage, offsetX, 0, htmlImage.width, htmlImage.height); - var ctxData = ctx.getImageData(offsetX, 0, htmlImage.width, htmlImage.height).data; - CVUtils.computeGray(ctxData, array); - }; - - CVUtils.grayArrayFromContext = function(ctx, size, offset, array) { - var ctxData = ctx.getImageData(offset.x, offset.y, size.x, size.y).data; - CVUtils.computeGray(ctxData, array); - }; - - CVUtils.grayAndHalfSampleFromCanvasData = function(canvasData, size, outArray) { - var topRowIdx = 0; - var bottomRowIdx = size.x; - var endIdx = Math.floor(canvasData.length / 4); - var outWidth = size.x / 2; - var outImgIdx = 0; - var inWidth = size.x; - var i; - - while (bottomRowIdx < endIdx) { - for ( i = 0; i < outWidth; i++) { - outArray[outImgIdx] = Math.floor(((0.299 * canvasData[topRowIdx * 4 + 0] + 0.587 * canvasData[topRowIdx * 4 + 1] + 0.114 * canvasData[topRowIdx * 4 + 2]) + (0.299 * canvasData[(topRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(topRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(topRowIdx + 1) * 4 + 2]) + (0.299 * canvasData[(bottomRowIdx) * 4 + 0] + 0.587 * canvasData[(bottomRowIdx) * 4 + 1] + 0.114 * canvasData[(bottomRowIdx) * 4 + 2]) + (0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4); - outImgIdx++; - topRowIdx = topRowIdx + 2; - bottomRowIdx = bottomRowIdx + 2; - } - topRowIdx = topRowIdx + inWidth; - bottomRowIdx = bottomRowIdx + inWidth; - } - - }; - - CVUtils.computeGray = function(imageData, outArray, config) { - var l = (imageData.length / 4) | 0, - i, - singleChannel = config && config.singleChannel === true; - - if (singleChannel) { - for (i = 0; i < l; i++) { - outArray[i] = imageData[i * 4 + 0]; - } - } else { - for (i = 0; i < l; i++) { - outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); - } - } - }; - - CVUtils.loadImageArray = function(src, callback, canvas) { - if (!canvas) - canvas = document.createElement('canvas'); - var img = new Image(); - img.callback = callback; - img.onload = function() { - canvas.width = this.width; - canvas.height = this.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(this, 0, 0); - var array = new Uint8Array(this.width * this.height); - ctx.drawImage(this, 0, 0); - var data = ctx.getImageData(0, 0, this.width, this.height).data; - CVUtils.computeGray(data, array); - this.callback(array, { - x : this.width, - y : this.height - }, this); - }; - img.src = src; - }; - - /** - * @param inImg {ImageWrapper} input image to be sampled - * @param outImg {ImageWrapper} to be stored in - */ - CVUtils.halfSample = function(inImgWrapper, outImgWrapper) { - var inImg = inImgWrapper.data; - var inWidth = inImgWrapper.size.x; - var outImg = outImgWrapper.data; - var topRowIdx = 0; - var bottomRowIdx = inWidth; - var endIdx = inImg.length; - var outWidth = inWidth / 2; - var outImgIdx = 0; - while (bottomRowIdx < endIdx) { - for (var i = 0; i < outWidth; i++) { - outImg[outImgIdx] = Math.floor((inImg[topRowIdx] + inImg[topRowIdx + 1] + inImg[bottomRowIdx] + inImg[bottomRowIdx + 1]) / 4); - outImgIdx++; - topRowIdx = topRowIdx + 2; - bottomRowIdx = bottomRowIdx + 2; - } - topRowIdx = topRowIdx + inWidth; - bottomRowIdx = bottomRowIdx + inWidth; - } - }; - - CVUtils.hsv2rgb = function(hsv, rgb) { - var h = hsv[0], s = hsv[1], v = hsv[2], c = v * s, x = c * (1 - Math.abs((h / 60) % 2 - 1)), m = v - c, r = 0, g = 0, b = 0; - rgb = rgb || [0, 0, 0]; - - if (h < 60) { - r = c; - g = x; - } else if (h < 120) { - r = x; - g = c; - } else if (h < 180) { - g = c; - b = x; - } else if (h < 240) { - g = x; - b = c; - } else if (h < 300) { - r = x; - b = c; - } else if (h < 360) { - r = c; - b = x; - } - rgb[0] = ((r + m) * 255) | 0; - rgb[1] = ((g + m) * 255) | 0; - rgb[2] = ((b + m) * 255) | 0; - return rgb; - }; - - CVUtils._computeDivisors = function(n) { - var largeDivisors = [], - divisors = [], - i; - - for (i = 1; i < Math.sqrt(n) + 1; i++) { - if (n % i === 0) { - divisors.push(i); - if (i !== n/i) { - largeDivisors.unshift(Math.floor(n/i)); - } - } - } - return divisors.concat(largeDivisors); - }; - - CVUtils._computeIntersection = function(arr1, arr2) { - var i = 0, - j = 0, - result = []; - - while (i < arr1.length && j < arr2.length) { - if (arr1[i] === arr2[j]) { - result.push(arr1[i]); - i++; - j++; - } else if (arr1[i] > arr2[j]) { - j++; - } else { - i++; - } - } - return result; - }; - - CVUtils.calculatePatchSize = function(patchSize, imgSize) { - var divisorsX = this._computeDivisors(imgSize.x), - divisorsY = this._computeDivisors(imgSize.y), - wideSide = Math.max(imgSize.x, imgSize.y), - common = this._computeIntersection(divisorsX, divisorsY), - nrOfPatchesList = [8, 10, 15, 20, 32, 60, 80], - nrOfPatchesMap = { - "x-small": 5, - "small": 4, - "medium": 3, - "large": 2, - "x-large": 1 - }, - nrOfPatchesIdx = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium, - nrOfPatches = nrOfPatchesList[nrOfPatchesIdx], - desiredPatchSize = Math.floor(wideSide/nrOfPatches), - optimalPatchSize; - - function findPatchSizeForDivisors(divisors) { - var i = 0, - found = divisors[Math.floor(divisors.length/2)]; - - while(i < (divisors.length - 1) && divisors[i] < desiredPatchSize) { - i++; - } - if (i > 0) { - if (Math.abs(divisors[i] - desiredPatchSize) > Math.abs(divisors[i-1] - desiredPatchSize)) { - found = divisors[i-1]; - } else { - found = divisors[i]; - } - } - if (desiredPatchSize / found < nrOfPatchesList[nrOfPatchesIdx+1] / nrOfPatchesList[nrOfPatchesIdx] && - desiredPatchSize / found > nrOfPatchesList[nrOfPatchesIdx-1]/nrOfPatchesList[nrOfPatchesIdx] ) { - return {x: found, y: found}; - } - return null; - } - - optimalPatchSize = findPatchSizeForDivisors(common); - if (!optimalPatchSize) { - optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(wideSide)); - if (!optimalPatchSize) { - optimalPatchSize = findPatchSizeForDivisors((this._computeDivisors(desiredPatchSize * nrOfPatches))); - } - } - return optimalPatchSize; - }; - - CVUtils._parseCSSDimensionValues = function(value) { - var dimension = { - value: parseFloat(value), - unit: value.indexOf("%") === value.length-1 ? "%" : "%" - }; - - return dimension; - }; - - CVUtils._dimensionsConverters = { - top: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.height * (dimension.value / 100)); - } - }, - right: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.width - (context.width * (dimension.value / 100))); - } - }, - bottom: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.height - (context.height * (dimension.value / 100))); - } - }, - left: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.width * (dimension.value / 100)); - } - } - }; - - CVUtils.computeImageArea = function(inputWidth, inputHeight, area) { - var context = {width: inputWidth, height: inputHeight}; - - var parsedArea = Object.keys(area).reduce(function(result, key) { - var value = area[key], - parsed = CVUtils._parseCSSDimensionValues(value), - calculated = CVUtils._dimensionsConverters[key](parsed, context); - - result[key] = calculated; - return result; - }, {}); - - return { - sx: parsedArea.left, - sy: parsedArea.top, - sw: parsedArea.right - parsedArea.left, - sh: parsedArea.bottom - parsedArea.top - }; - }; - - return (CVUtils); -}); - - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('image_wrapper',[ - "subImage", - "cv_utils", - "array_helper", - "gl-matrix" - ], - function(SubImage, CVUtils, ArrayHelper, glMatrix) { - - 'use strict'; - var vec2 = glMatrix.vec2, - mat2 = glMatrix.mat2; - - /** - * Represents a basic image combining the data and size. - * In addition, some methods for manipulation are contained. - * @param size {x,y} The size of the image in pixel - * @param data {Array} If given, a flat array containing the pixel data - * @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed) - * @param initialize {Boolean} Indicating if the array should be initialized on creation. - * @returns {ImageWrapper} - */ - function ImageWrapper(size, data, ArrayType, initialize) { - if (!data) { - if (ArrayType) { - this.data = new ArrayType(size.x * size.y); - if (ArrayType === Array && initialize) { - ArrayHelper.init(this.data, 0); - } - } else { - this.data = new Uint8Array(size.x * size.y); - if (Uint8Array === Array && initialize) { - ArrayHelper.init(this.data, 0); - } - } - - } else { - this.data = data; - } - this.size = size; - } - - /** - * tests if a position is within the image with a given offset - * @param imgRef {x, y} The location to test - * @param border Number the padding value in pixel - * @returns {Boolean} true if location inside the image's border, false otherwise - * @see cvd/image.h - */ - ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) { - return (imgRef.x >= border) && (imgRef.y >= border) && (imgRef.x < (this.size.x - border)) && (imgRef.y < (this.size.y - border)); - }; - - /** - * Transforms an image according to the given affine-transformation matrix. - * @param inImg ImageWrapper a image containing the information to be extracted. - * @param outImg ImageWrapper the image to be filled. The whole image out image is filled by the in image. - * @param M mat2 the matrix used to map point in the out matrix to those in the in matrix - * @param inOrig vec2 origin in the in image - * @param outOrig vec2 origin in the out image - * @returns Number the number of pixels not in the in image - * @see cvd/vision.h - */ - ImageWrapper.transform = function(inImg, outImg, M, inOrig, outOrig) { - var w = outImg.size.x, h = outImg.size.y, iw = inImg.size.x, ih = inImg.size.y; - var across = vec2.clone([M[0], M[2]]); - var down = vec2.clone([M[1], M[3]]); - var defaultValue = 0; - - var p0 = vec2.subtract(inOrig, mat2.xVec2(M, outOrig, vec2.clone()), vec2.clone()); - - var min_x = p0[0], min_y = p0[1]; - var max_x = min_x, max_y = min_y; - var p, i, j; - - var sampleFunc = ImageWrapper.sample; - - if (across[0] < 0) - min_x += w * across[0]; - else - max_x += w * across[0]; - - if (down[0] < 0) - min_x += h * down[0]; - else - max_x += h * down[0]; - - if (across[1] < 0) - min_y += w * across[1]; - else - max_y += w * across[1]; - - if (down[1] < 0) - min_y += h * down[1]; - else - max_y += h * down[1]; - - var carrigeReturn = vec2.subtract(down, vec2.scale(across, w, vec2.clone()), vec2.clone()); - - if (min_x >= 0 && min_y >= 0 && max_x < iw - 1 && max_y < ih - 1) { - p = p0; - for ( i = 0; i < h; ++i, vec2.add(p, carrigeReturn)) - for ( j = 0; j < w; ++j, vec2.add(p, across)) - outImg.set(j, i, sampleFunc(inImg, p[0], p[1])); - return 0; - } else { - var x_bound = iw - 1; - var y_bound = ih - 1; - var count = 0; - p = p0; - for ( i = 0; i < h; ++i, vec2.add(p, carrigeReturn)) { - for ( j = 0; j < w; ++j, vec2.add(p, across)) { - if (0 <= p[0] && 0 <= p[1] && p[0] < x_bound && p[1] < y_bound) { - outImg.set(j, i, sampleFunc(inImg, p[0], p[1])); - } else { - outImg.set(j, i, defaultValue); ++count; - } - } - } - return count; - } - }; - - /** - * Performs bilinear sampling - * @param inImg Image to extract sample from - * @param x the x-coordinate - * @param y the y-coordinate - * @returns the sampled value - * @see cvd/vision.h - */ - ImageWrapper.sample = function(inImg, x, y) { - var lx = Math.floor(x); - var ly = Math.floor(y); - var w = inImg.size.x; - var base = ly * inImg.size.x + lx; - var a = inImg.data[base + 0]; - var b = inImg.data[base + 1]; - var c = inImg.data[base + w]; - var d = inImg.data[base + w + 1]; - var e = a - b; - x -= lx; - y -= ly; - - var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a); - return result; - }; - - /** - * Initializes a given array. Sets each element to zero. - * @param array {Array} The array to initialize - */ - ImageWrapper.clearArray = function(array) { - var l = array.length; - while (l--) { - array[l] = 0; - } - }; - - /** - * Creates a {SubImage} from the current image ({this}). - * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) - * @param size {ImageRef} The size of the resulting image - * @returns {SubImage} A shared part of the original image - */ - ImageWrapper.prototype.subImage = function(from, size) { - return new SubImage(from, size, this); - }; - - /** - * Creates an {ImageWrapper) and copies the needed underlying image-data area - * @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied - * @param from {ImageRef} The location where to copy from (top-left location) - */ - ImageWrapper.prototype.subImageAsCopy = function(imageWrapper, from) { - var sizeY = imageWrapper.size.y, sizeX = imageWrapper.size.x; - var x, y; - for ( x = 0; x < sizeX; x++) { - for ( y = 0; y < sizeY; y++) { - imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x]; - } - } - }; - - ImageWrapper.prototype.copyTo = function(imageWrapper) { - var length = this.data.length, srcData = this.data, dstData = imageWrapper.data; - - while (length--) { - dstData[length] = srcData[length]; - } - }; - - /** - * Retrieves a given pixel position from the image - * @param x {Number} The x-position - * @param y {Number} The y-position - * @returns {Number} The grayscale value at the pixel-position - */ - ImageWrapper.prototype.get = function(x, y) { - return this.data[y * this.size.x + x]; - }; - - /** - * Retrieves a given pixel position from the image - * @param x {Number} The x-position - * @param y {Number} The y-position - * @returns {Number} The grayscale value at the pixel-position - */ - ImageWrapper.prototype.getSafe = function(x, y) { - var i; - - if (!this.indexMapping) { - this.indexMapping = { - x : [], - y : [] - }; - for (i = 0; i < this.size.x; i++) { - this.indexMapping.x[i] = i; - this.indexMapping.x[i + this.size.x] = i; - } - for (i = 0; i < this.size.y; i++) { - this.indexMapping.y[i] = i; - this.indexMapping.y[i + this.size.y] = i; - } - } - return this.data[(this.indexMapping.y[y + this.size.y]) * this.size.x + this.indexMapping.x[x + this.size.x]]; - }; - - /** - * Sets a given pixel position in the image - * @param x {Number} The x-position - * @param y {Number} The y-position - * @param value {Number} The grayscale value to set - * @returns {ImageWrapper} The Image itself (for possible chaining) - */ - ImageWrapper.prototype.set = function(x, y, value) { - this.data[y * this.size.x + x] = value; - return this; - }; - - /** - * Sets the border of the image (1 pixel) to zero - */ - ImageWrapper.prototype.zeroBorder = function() { - var i, width = this.size.x, height = this.size.y, data = this.data; - for ( i = 0; i < width; i++) { - data[i] = data[(height - 1) * width + i] = 0; - } - for ( i = 1; i < height - 1; i++) { - data[i * width] = data[i * width + (width - 1)] = 0; - } - }; - - /** - * Inverts a binary image in place - */ - ImageWrapper.prototype.invert = function() { - var data = this.data, length = data.length; - - while (length--) { - data[length] = data[length] ? 0 : 1; - } - - }; - - ImageWrapper.prototype.convolve = function(kernel) { - var x, y, kx, ky, kSize = (kernel.length / 2) | 0, accu = 0; - for ( y = 0; y < this.size.y; y++) { - for ( x = 0; x < this.size.x; x++) { - accu = 0; - for ( ky = -kSize; ky <= kSize; ky++) { - for ( kx = -kSize; kx <= kSize; kx++) { - accu += kernel[ky+kSize][kx + kSize] * this.getSafe(x + kx, y + ky); - } - } - this.data[y * this.size.x + x] = accu; - } - } - }; - - ImageWrapper.prototype.moments = function(labelcount) { - var data = this.data, - x, - y, - height = this.size.y, - width = this.size.x, - val, - ysq, - labelsum = [], - i, - label, - mu11, - mu02, - mu20, - x_, - y_, - tmp, - result = [], - PI = Math.PI, - PI_4 = PI / 4; - - if (labelcount <= 0) { - return result; - } - - for ( i = 0; i < labelcount; i++) { - labelsum[i] = { - m00 : 0, - m01 : 0, - m10 : 0, - m11 : 0, - m02 : 0, - m20 : 0, - theta : 0, - rad : 0 - }; - } - - for ( y = 0; y < height; y++) { - ysq = y * y; - for ( x = 0; x < width; x++) { - val = data[y * width + x]; - if (val > 0) { - label = labelsum[val - 1]; - label.m00 += 1; - label.m01 += y; - label.m10 += x; - label.m11 += x * y; - label.m02 += ysq; - label.m20 += x * x; - } - } - } - - for ( i = 0; i < labelcount; i++) { - label = labelsum[i]; - if (!isNaN(label.m00) && label.m00 !== 0) { - x_ = label.m10 / label.m00; - y_ = label.m01 / label.m00; - mu11 = label.m11 / label.m00 - x_ * y_; - mu02 = label.m02 / label.m00 - y_ * y_; - mu20 = label.m20 / label.m00 - x_ * x_; - tmp = (mu02 - mu20) / (2 * mu11); - tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4 ) + PI; - label.theta = (tmp * 180 / PI + 90) % 180 - 90; - if (label.theta < 0) { - label.theta += 180; - } - label.rad = tmp > PI ? tmp - PI : tmp; - label.vec = vec2.clone([Math.cos(tmp), Math.sin(tmp)]); - result.push(label); - } - } - - return result; - }; - - /** - * Displays the {ImageWrapper} in a given canvas - * @param canvas {Canvas} The canvas element to write to - * @param scale {Number} Scale which is applied to each pixel-value - */ - ImageWrapper.prototype.show = function(canvas, scale) { - var ctx, - frame, - data, - current, - pixel, - x, - y; - - if (!scale) { - scale = 1.0; - } - ctx = canvas.getContext('2d'); - canvas.width = this.size.x; - canvas.height = this.size.y; - frame = ctx.getImageData(0, 0, canvas.width, canvas.height); - data = frame.data; - current = 0; - for (y = 0; y < this.size.y; y++) { - for (x = 0; x < this.size.x; x++) { - pixel = y * this.size.x + x; - current = this.get(x, y) * scale; - data[pixel * 4 + 0] = current; - data[pixel * 4 + 1] = current; - data[pixel * 4 + 2] = current; - data[pixel * 4 + 3] = 255; - } - } - //frame.data = data; - ctx.putImageData(frame, 0, 0); - }; - - /** - * Displays the {SubImage} in a given canvas - * @param canvas {Canvas} The canvas element to write to - * @param scale {Number} Scale which is applied to each pixel-value - */ - ImageWrapper.prototype.overlay = function(canvas, scale, from) { - if (!scale || scale < 0 || scale > 360) { - scale = 360; - } - var hsv = [0, 1, 1]; - var rgb = [0, 0, 0]; - var whiteRgb = [255, 255, 255]; - var blackRgb = [0, 0, 0]; - var result = []; - var ctx = canvas.getContext('2d'); - var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y); - var data = frame.data; - var length = this.data.length; - while (length--) { - hsv[0] = this.data[length] * scale; - result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : CVUtils.hsv2rgb(hsv, rgb); - data[length * 4 + 0] = result[0]; - data[length * 4 + 1] = result[1]; - data[length * 4 + 2] = result[2]; - data[length * 4 + 3] = 255; - } - ctx.putImageData(frame, from.x, from.y); - }; - - return (ImageWrapper); -}); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -/** - * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization - */ -define('tracer',[],function() { - "use strict"; - - var Tracer = { - searchDirections : [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]], - create : function(imageWrapper, labelWrapper) { - var imageData = imageWrapper.data, - labelData = labelWrapper.data, - searchDirections = this.searchDirections, - width = imageWrapper.size.x, - pos; - - function trace(current, color, label, edgelabel) { - var i, - y, - x; - - for ( i = 0; i < 7; i++) { - y = current.cy + searchDirections[current.dir][0]; - x = current.cx + searchDirections[current.dir][1]; - pos = y * width + x; - if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) { - labelData[pos] = label; - current.cy = y; - current.cx = x; - return true; - } else { - if (labelData[pos] === 0) { - labelData[pos] = edgelabel; - } - current.dir = (current.dir + 1) % 8; - } - } - return false; - } - - function vertex2D(x, y, dir) { - return { - dir : dir, - x : x, - y : y, - next : null, - prev : null - }; - } - - function contourTracing(sy, sx, label, color, edgelabel) { - var Fv = null, - Cv, - P, - ldir, - current = { - cx : sx, - cy : sy, - dir : 0 - }; - - if (trace(current, color, label, edgelabel)) { - Fv = vertex2D(sx, sy, current.dir); - Cv = Fv; - ldir = current.dir; - P = vertex2D(current.cx, current.cy, 0); - P.prev = Cv; - Cv.next = P; - P.next = null; - Cv = P; - do { - current.dir = (current.dir + 6) % 8; - trace(current, color, label, edgelabel); - if (ldir != current.dir) { - Cv.dir = current.dir; - P = vertex2D(current.cx, current.cy, 0); - P.prev = Cv; - Cv.next = P; - P.next = null; - Cv = P; - } else { - Cv.dir = ldir; - Cv.x = current.cx; - Cv.y = current.cy; - } - ldir = current.dir; - } while(current.cx != sx || current.cy != sy); - Fv.prev = Cv.prev; - Cv.prev.next = Fv; - } - return Fv; - } - - return { - trace : function(current, color, label, edgelabel) { - return trace(current, color, label, edgelabel); - }, - contourTracing : function(sy, sx, label, color, edgelabel) { - return contourTracing(sy, sx, label, color, edgelabel); - } - }; - } - }; - - return (Tracer); -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -/** - * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization - */ -define('rasterizer',["tracer"], function(Tracer) { - "use strict"; - - var Rasterizer = { - createContour2D : function() { - return { - dir : null, - index : null, - firstVertex : null, - insideContours : null, - nextpeer : null, - prevpeer : null - }; - }, - CONTOUR_DIR : { - CW_DIR : 0, - CCW_DIR : 1, - UNKNOWN_DIR : 2 - }, - DIR : { - OUTSIDE_EDGE : -32767, - INSIDE_EDGE : -32766 - }, - create : function(imageWrapper, labelWrapper) { - var imageData = imageWrapper.data, - labelData = labelWrapper.data, - width = imageWrapper.size.x, - height = imageWrapper.size.y, - tracer = Tracer.create(imageWrapper, labelWrapper); - - return { - rasterize : function(depthlabel) { - var color, - bc, - lc, - labelindex, - cx, - cy, - colorMap = [], - vertex, - p, - cc, - sc, - pos, - connectedCount = 0, - i; - - for ( i = 0; i < 400; i++) { - colorMap[i] = 0; - } - - colorMap[0] = imageData[0]; - cc = null; - for ( cy = 1; cy < height - 1; cy++) { - labelindex = 0; - bc = colorMap[0]; - for ( cx = 1; cx < width - 1; cx++) { - pos = cy * width + cx; - if (labelData[pos] === 0) { - color = imageData[pos]; - if (color !== bc) { - if (labelindex === 0) { - lc = connectedCount + 1; - colorMap[lc] = color; - bc = color; - vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE); - if (vertex !== null) { - connectedCount++; - labelindex = lc; - p = Rasterizer.createContour2D(); - p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; - p.index = labelindex; - p.firstVertex = vertex; - p.nextpeer = cc; - p.insideContours = null; - if (cc !== null) { - cc.prevpeer = p; - } - cc = p; - } - } else { - vertex = tracer.contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex); - if (vertex !== null) { - p = Rasterizer.createContour2D(); - p.firstVertex = vertex; - p.insideContours = null; - if (depthlabel === 0) { - p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR; - } else { - p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; - } - p.index = depthlabel; - sc = cc; - while ((sc !== null) && sc.index !== labelindex) { - sc = sc.nextpeer; - } - if (sc !== null) { - p.nextpeer = sc.insideContours; - if (sc.insideContours !== null) { - sc.insideContours.prevpeer = p; - } - sc.insideContours = p; - } - } - } - } else { - labelData[pos] = labelindex; - } - } else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { - labelindex = 0; - if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { - bc = imageData[pos]; - } else { - bc = colorMap[0]; - } - } else { - labelindex = labelData[pos]; - bc = colorMap[labelindex]; - } - } - } - sc = cc; - while (sc !== null) { - sc.index = depthlabel; - sc = sc.nextpeer; - } - return { - cc : cc, - count : connectedCount - }; - }, - debug: { - drawContour : function(canvas, firstContour) { - var ctx = canvas.getContext("2d"), - pq = firstContour, - iq, - q, - p; - - ctx.strokeStyle = "red"; - ctx.fillStyle = "red"; - ctx.lineWidth = 1; - - if (pq !== null) { - iq = pq.insideContours; - } else { - iq = null; - } - - while (pq !== null) { - if (iq !== null) { - q = iq; - iq = iq.nextpeer; - } else { - q = pq; - pq = pq.nextpeer; - if (pq !== null) { - iq = pq.insideContours; - } else { - iq = null; - } - } - - switch(q.dir) { - case Rasterizer.CONTOUR_DIR.CW_DIR: - ctx.strokeStyle = "red"; - break; - case Rasterizer.CONTOUR_DIR.CCW_DIR: - ctx.strokeStyle = "blue"; - break; - case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR: - ctx.strokeStyle = "green"; - break; - } - - p = q.firstVertex; - ctx.beginPath(); - ctx.moveTo(p.x, p.y); - do { - p = p.next; - ctx.lineTo(p.x, p.y); - } while(p !== q.firstVertex); - ctx.stroke(); - } - } - } - }; - } - }; - - return (Rasterizer); -}); - -/* jshint undef: true, unused: true, browser:true, devel: true, -W041: false */ -/* global define */ - -define('skeletonizer',[],function() { - "use strict"; - - /* @preserve ASM BEGIN */ - function Skeletonizer(stdlib, foreign, buffer) { - "use asm"; - - var images = new stdlib.Uint8Array(buffer), - size = foreign.size | 0, - imul = stdlib.Math.imul; - - function erode(inImagePtr, outImagePtr) { - inImagePtr = inImagePtr | 0; - outImagePtr = outImagePtr | 0; - - var v = 0, - u = 0, - sum = 0, - yStart1 = 0, - yStart2 = 0, - xStart1 = 0, - xStart2 = 0, - offset = 0; - - for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { - offset = (offset + size) | 0; - for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { - yStart1 = (offset - size) | 0; - yStart2 = (offset + size) | 0; - xStart1 = (u - 1) | 0; - xStart2 = (u + 1) | 0; - sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + (images[(inImagePtr + offset + u) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0; - if ((sum | 0) == (5 | 0)) { - images[(outImagePtr + offset + u) | 0] = 1; - } else { - images[(outImagePtr + offset + u) | 0] = 0; - } - } - } - return; - } - - function subtract(aImagePtr, bImagePtr, outImagePtr) { - aImagePtr = aImagePtr | 0; - bImagePtr = bImagePtr | 0; - outImagePtr = outImagePtr | 0; - - var length = 0; - - length = imul(size, size) | 0; - - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(outImagePtr + length) | 0] = ((images[(aImagePtr + length) | 0] | 0) - (images[(bImagePtr + length) | 0] | 0)) | 0; - } - } - - function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) { - aImagePtr = aImagePtr | 0; - bImagePtr = bImagePtr | 0; - outImagePtr = outImagePtr | 0; - - var length = 0; - - length = imul(size, size) | 0; - - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(outImagePtr + length) | 0] = ((images[(aImagePtr + length) | 0] | 0) | (images[(bImagePtr + length) | 0] | 0)) | 0; - } - } - - function countNonZero(imagePtr) { - imagePtr = imagePtr | 0; - - var sum = 0, - length = 0; - - length = imul(size, size) | 0; - - while ((length | 0) > 0) { - length = (length - 1) | 0; - sum = ((sum | 0) + (images[(imagePtr + length) | 0] | 0)) | 0; - } - - return (sum | 0); - } - - function init(imagePtr, value) { - imagePtr = imagePtr | 0; - value = value | 0; - - var length = 0; - - length = imul(size, size) | 0; - - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(imagePtr + length) | 0] = value; - } - } - - function dilate(inImagePtr, outImagePtr) { - inImagePtr = inImagePtr | 0; - outImagePtr = outImagePtr | 0; - - var v = 0, - u = 0, - sum = 0, - yStart1 = 0, - yStart2 = 0, - xStart1 = 0, - xStart2 = 0, - offset = 0; - - for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { - offset = (offset + size) | 0; - for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { - yStart1 = (offset - size) | 0; - yStart2 = (offset + size) | 0; - xStart1 = (u - 1) | 0; - xStart2 = (u + 1) | 0; - sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + (images[(inImagePtr + offset + u) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0; - if ((sum | 0) > (0 | 0)) { - images[(outImagePtr + offset + u) | 0] = 1; - } else { - images[(outImagePtr + offset + u) | 0] = 0; - } - } - } - return; - } - - function memcpy(srcImagePtr, dstImagePtr) { - srcImagePtr = srcImagePtr | 0; - dstImagePtr = dstImagePtr | 0; - - var length = 0; - - length = imul(size, size) | 0; - - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(dstImagePtr + length) | 0] = (images[(srcImagePtr + length) | 0] | 0); - } - } - - function zeroBorder(imagePtr) { - imagePtr = imagePtr | 0; - - var x = 0, - y = 0; - - for ( x = 0; (x | 0) < ((size - 1) | 0); x = (x + 1) | 0) { - images[(imagePtr + x) | 0] = 0; - images[(imagePtr + y) | 0] = 0; - y = ((y + size) - 1) | 0; - images[(imagePtr + y) | 0] = 0; - y = (y + 1) | 0; - } - for ( x = 0; (x | 0) < (size | 0); x = (x + 1) | 0) { - images[(imagePtr + y) | 0] = 0; - y = (y + 1) | 0; - } - } - - function skeletonize() { - var subImagePtr = 0, - erodedImagePtr = 0, - tempImagePtr = 0, - skelImagePtr = 0, - sum = 0, - done = 0; - - erodedImagePtr = imul(size, size) | 0; - tempImagePtr = (erodedImagePtr + erodedImagePtr) | 0; - skelImagePtr = (tempImagePtr + erodedImagePtr) | 0; - - // init skel-image - init(skelImagePtr, 0); - zeroBorder(subImagePtr); - - do { - erode(subImagePtr, erodedImagePtr); - dilate(erodedImagePtr, tempImagePtr); - subtract(subImagePtr, tempImagePtr, tempImagePtr); - bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr); - memcpy(erodedImagePtr, subImagePtr); - sum = countNonZero(subImagePtr) | 0; - done = ((sum | 0) == 0 | 0); - } while(!done); - } - - return { - skeletonize : skeletonize - }; - } - /* @preserve ASM END */ - - return Skeletonizer; -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('image_debug',[],function() { - "use strict"; - - return { - drawRect: function(pos, size, ctx, style){ - ctx.strokeStyle = style.color; - ctx.fillStyle = style.color; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.strokeRect(pos.x, pos.y, size.x, size.y); - }, - drawPath: function(path, def, ctx, style) { - ctx.strokeStyle = style.color; - ctx.fillStyle = style.color; - ctx.lineWidth = style.lineWidth; - ctx.beginPath(); - ctx.moveTo(path[0][def.x], path[0][def.y]); - for (var j = 1; j < path.length; j++) { - ctx.lineTo(path[j][def.x], path[j][def.y]); - } - ctx.closePath(); - ctx.stroke(); - }, - drawImage: function(imageData, size, ctx) { - var canvasData = ctx.getImageData(0, 0, size.x, size.y), - data = canvasData.data, - imageDataPos = imageData.length, - canvasDataPos = data.length, - value; - - if (canvasDataPos/imageDataPos !== 4) { - return false; - } - while(imageDataPos--){ - value = imageData[imageDataPos]; - data[--canvasDataPos] = 255; - data[--canvasDataPos] = value; - data[--canvasDataPos] = value; - data[--canvasDataPos] = value; - } - ctx.putImageData(canvasData, 0, 0); - return true; - } - }; - -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define("barcode_locator", ["image_wrapper", "cv_utils", "rasterizer", "tracer", "skeletonizer", "array_helper", "image_debug", "gl-matrix"], -function(ImageWrapper, CVUtils, Rasterizer, Tracer, skeletonizer, ArrayHelper, ImageDebug, glMatrix) { - - var _config, - _currentImageWrapper, - _skelImageWrapper, - _subImageWrapper, - _labelImageWrapper, - _patchGrid, - _patchLabelGrid, - _imageToPatchGrid, - _binaryImageWrapper, - _patchSize, - _canvasContainer = { - ctx : { - binary : null - }, - dom : { - binary : null - } - }, - _numPatches = {x: 0, y: 0}, - _inputImageWrapper, - _skeletonizer, - vec2 = glMatrix.vec2, - mat2 = glMatrix.mat2, - self = this; - - function initBuffers() { - var skeletonImageData; - - if (_config.halfSample) { - _currentImageWrapper = new ImageWrapper({ - x : _inputImageWrapper.size.x / 2 | 0, - y : _inputImageWrapper.size.y / 2 | 0 - }); - } else { - _currentImageWrapper = _inputImageWrapper; - } - - _patchSize = CVUtils.calculatePatchSize(_config.patchSize, _currentImageWrapper.size); - - _numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0; - _numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0; - - _binaryImageWrapper = new ImageWrapper(_currentImageWrapper.size, undefined, Uint8Array, false); - - _labelImageWrapper = new ImageWrapper(_patchSize, undefined, Array, true); - - skeletonImageData = new ArrayBuffer(64*1024); - _subImageWrapper = new ImageWrapper(_patchSize, new Uint8Array(skeletonImageData, 0, _patchSize.x * _patchSize.y)); - _skelImageWrapper = new ImageWrapper(_patchSize, new Uint8Array(skeletonImageData, _patchSize.x * _patchSize.y * 3, _patchSize.x * _patchSize.y), undefined, true); - _skeletonizer = skeletonizer(self, { - size : _patchSize.x - }, skeletonImageData); - - _imageToPatchGrid = new ImageWrapper({ - x : (_currentImageWrapper.size.x / _subImageWrapper.size.x) | 0, - y : (_currentImageWrapper.size.y / _subImageWrapper.size.y) | 0 - }, undefined, Array, true); - _patchGrid = new ImageWrapper(_imageToPatchGrid.size, undefined, undefined, true); - _patchLabelGrid = new ImageWrapper(_imageToPatchGrid.size, undefined, Int32Array, true); - } - - function initCanvas() { - if (_config.useWorker || typeof document === 'undefined') { - return; - } - _canvasContainer.dom.binary = document.createElement("canvas"); - _canvasContainer.dom.binary.className = "binaryBuffer"; - if (_config.showCanvas === true) { - document.querySelector("#debug").appendChild(_canvasContainer.dom.binary); - } - _canvasContainer.ctx.binary = _canvasContainer.dom.binary.getContext("2d"); - _canvasContainer.dom.binary.width = _binaryImageWrapper.size.x; - _canvasContainer.dom.binary.height = _binaryImageWrapper.size.y; - } - - /** - * Creates a bounding box which encloses all the given patches - * @returns {Array} The minimal bounding box - */ - function boxFromPatches(patches) { - var overAvg, i, j, patch, transMat, minx = _binaryImageWrapper.size.x, miny = _binaryImageWrapper.size.y, maxx = -_binaryImageWrapper.size.x, maxy = -_binaryImageWrapper.size.y, box, scale; - - // draw all patches which are to be taken into consideration - overAvg = 0; - for ( i = 0; i < patches.length; i++) { - patch = patches[i]; - overAvg += patch.rad; - if (_config.showPatches) { - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "red"}); - } - } - - overAvg /= patches.length; - overAvg = (overAvg * 180 / Math.PI + 90) % 180 - 90; - if (overAvg < 0) { - overAvg += 180; - } - - overAvg = (180 - overAvg) * Math.PI / 180; - transMat = mat2.clone([Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]); - - // iterate over patches and rotate by angle - for ( i = 0; i < patches.length; i++) { - patch = patches[i]; - for ( j = 0; j < 4; j++) { - vec2.transformMat2(patch.box[j], patch.box[j], transMat); - } - - if (_config.boxFromPatches.showTransformed) { - ImageDebug.drawPath(patch.box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#99ff00', lineWidth: 2}); - } - } - - // find bounding box - for ( i = 0; i < patches.length; i++) { - patch = patches[i]; - for ( j = 0; j < 4; j++) { - if (patch.box[j][0] < minx) { - minx = patch.box[j][0]; - } - if (patch.box[j][0] > maxx) { - maxx = patch.box[j][0]; - } - if (patch.box[j][1] < miny) { - miny = patch.box[j][1]; - } - if (patch.box[j][1] > maxy) { - maxy = patch.box[j][1]; - } - } - } - - box = [[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy]]; - - if (_config.boxFromPatches.showTransformedBox) { - ImageDebug.drawPath(box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#ff0000', lineWidth: 2}); - } - - scale = _config.halfSample ? 2 : 1; - // reverse rotation; - transMat = mat2.invert(transMat, transMat); - for ( j = 0; j < 4; j++) { - vec2.transformMat2(box[j], box[j], transMat); - } - - if (_config.boxFromPatches.showBB) { - ImageDebug.drawPath(box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#ff0000', lineWidth: 2}); - } - - for ( j = 0; j < 4; j++) { - vec2.scale(box[j], box[j], scale); - } - - return box; - } - - /** - * Creates a binary image of the current image - */ - function binarizeImage() { - CVUtils.otsuThreshold(_currentImageWrapper, _binaryImageWrapper); - _binaryImageWrapper.zeroBorder(); - if (_config.showCanvas) { - _binaryImageWrapper.show(_canvasContainer.dom.binary, 255); - } - } - - /** - * Iterate over the entire image - * extract patches - */ - function findPatches() { - var i, - j, - x, - y, - moments, - patchesFound = [], - rasterizer, - rasterResult, - patch; - for ( i = 0; i < _numPatches.x; i++) { - for ( j = 0; j < _numPatches.y; j++) { - - x = _subImageWrapper.size.x * i; - y = _subImageWrapper.size.y * j; - - // seperate parts - skeletonize(x, y); - - // Rasterize, find individual bars - _skelImageWrapper.zeroBorder(); - ArrayHelper.init(_labelImageWrapper.data, 0); - rasterizer = Rasterizer.create(_skelImageWrapper, _labelImageWrapper); - rasterResult = rasterizer.rasterize(0); - - if (_config.showLabels) { - _labelImageWrapper.overlay(_canvasContainer.dom.binary, Math.floor(360 / rasterResult.count), {x : x, y : y}); - } - - // calculate moments from the skeletonized patch - moments = _labelImageWrapper.moments(rasterResult.count); - - // extract eligible patches - patchesFound = patchesFound.concat(describePatch(moments, [i, j], x, y)); - } - } - - if (_config.showFoundPatches) { - for ( i = 0; i < patchesFound.length; i++) { - patch = patchesFound[i]; - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "#99ff00", lineWidth: 2}); - } - } - - return patchesFound; - } - - /** - * Finds those connected areas which contain at least 6 patches - * and returns them ordered DESC by the number of contained patches - * @param {Number} maxLabel - */ - function findBiggestConnectedAreas(maxLabel){ - var i, - sum, - labelHist = [], - topLabels = []; - - for ( i = 0; i < maxLabel; i++) { - labelHist.push(0); - } - sum = _patchLabelGrid.data.length; - while (sum--) { - if (_patchLabelGrid.data[sum] > 0) { - labelHist[_patchLabelGrid.data[sum] - 1]++; - } - } - - labelHist = labelHist.map(function(val, idx) { - return { - val : val, - label : idx + 1 - }; - }); - - labelHist.sort(function(a, b) { - return b.val - a.val; - }); - - // extract top areas with at least 6 patches present - topLabels = labelHist.filter(function(el) { - return el.val >= 5; - }); - - return topLabels; - } - - /** - * - */ - function findBoxes(topLabels, maxLabel) { - var i, - j, - sum, - patches = [], - patch, - box, - boxes = [], - hsv = [0, 1, 1], - rgb = [0, 0, 0]; - - for ( i = 0; i < topLabels.length; i++) { - sum = _patchLabelGrid.data.length; - patches.length = 0; - while (sum--) { - if (_patchLabelGrid.data[sum] === topLabels[i].label) { - patch = _imageToPatchGrid.data[sum]; - patches.push(patch); - } - } - box = boxFromPatches(patches); - if (box) { - boxes.push(box); - - // draw patch-labels if requested - if (_config.showRemainingPatchLabels) { - for ( j = 0; j < patches.length; j++) { - patch = patches[j]; - hsv[0] = (topLabels[i].label / (maxLabel + 1)) * 360; - CVUtils.hsv2rgb(hsv, rgb); - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "rgb(" + rgb.join(",") + ")", lineWidth: 2}); - } - } - } - } - return boxes; - } - - /** - * Find similar moments (via cluster) - * @param {Object} moments - */ - function similarMoments(moments) { - var clusters = CVUtils.cluster(moments, 0.90); - var topCluster = CVUtils.topGeneric(clusters, 1, function(e) { - return e.getPoints().length; - }); - var points = [], result = []; - if (topCluster.length === 1) { - points = topCluster[0].item.getPoints(); - for (var i = 0; i < points.length; i++) { - result.push(points[i].point); - } - } - return result; - } - - function skeletonize(x, y) { - _binaryImageWrapper.subImageAsCopy(_subImageWrapper, CVUtils.imageRef(x, y)); - _skeletonizer.skeletonize(); - - // Show skeleton if requested - if (_config.showSkeleton) { - _skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, CVUtils.imageRef(x, y)); - } - } - - /** - * Extracts and describes those patches which seem to contain a barcode pattern - * @param {Array} moments - * @param {Object} patchPos, - * @param {Number} x - * @param {Number} y - * @returns {Array} list of patches - */ - function describePatch(moments, patchPos, x, y) { - var k, - avg, - sum = 0, - eligibleMoments = [], - matchingMoments, - patch, - patchesFound = [], - minComponentWeight = Math.ceil(_patchSize.x/3); - - if (moments.length >= 2) { - // only collect moments which's area covers at least minComponentWeight pixels. - for ( k = 0; k < moments.length; k++) { - if (moments[k].m00 > minComponentWeight) { - eligibleMoments.push(moments[k]); - } - } - - // if at least 2 moments are found which have at least minComponentWeights covered - if (eligibleMoments.length >= 2) { - sum = eligibleMoments.length; - matchingMoments = similarMoments(eligibleMoments); - avg = 0; - // determine the similarity of the moments - for ( k = 0; k < matchingMoments.length; k++) { - avg += matchingMoments[k].rad; - } - - // Only two of the moments are allowed not to fit into the equation - // add the patch to the set - if (matchingMoments.length > 1 && matchingMoments.length >= (eligibleMoments.length / 4) * 3 && matchingMoments.length > moments.length / 4) { - avg /= matchingMoments.length; - patch = { - index : patchPos[1] * _numPatches.x + patchPos[0], - pos : { - x : x, - y : y - }, - box : [vec2.clone([x, y]), vec2.clone([x + _subImageWrapper.size.x, y]), vec2.clone([x + _subImageWrapper.size.x, y + _subImageWrapper.size.y]), vec2.clone([x, y + _subImageWrapper.size.y])], - moments : matchingMoments, - rad : avg, - vec : vec2.clone([Math.cos(avg), Math.sin(avg)]) - }; - patchesFound.push(patch); - } - } - } - return patchesFound; - } - - /** - * finds patches which are connected and share the same orientation - * @param {Object} patchesFound - */ - function rasterizeAngularSimilarity(patchesFound) { - var label = 0, - threshold = 0.95, - currIdx = 0, - j, - patch, - hsv = [0, 1, 1], - rgb = [0, 0, 0]; - - function notYetProcessed() { - var i; - for ( i = 0; i < _patchLabelGrid.data.length; i++) { - if (_patchLabelGrid.data[i] === 0 && _patchGrid.data[i] === 1) { - return i; - } - } - return _patchLabelGrid.length; - } - - function trace(currentIdx) { - var x, y, currentPatch, patch, idx, dir, current = { - x : currentIdx % _patchLabelGrid.size.x, - y : (currentIdx / _patchLabelGrid.size.x) | 0 - }, similarity; - - if (currentIdx < _patchLabelGrid.data.length) { - currentPatch = _imageToPatchGrid.data[currentIdx]; - // assign label - _patchLabelGrid.data[currentIdx] = label; - for ( dir = 0; dir < Tracer.searchDirections.length; dir++) { - y = current.y + Tracer.searchDirections[dir][0]; - x = current.x + Tracer.searchDirections[dir][1]; - idx = y * _patchLabelGrid.size.x + x; - - // continue if patch empty - if (_patchGrid.data[idx] === 0) { - _patchLabelGrid.data[idx] = Number.MAX_VALUE; - continue; - } - - patch = _imageToPatchGrid.data[idx]; - if (_patchLabelGrid.data[idx] === 0) { - similarity = Math.abs(vec2.dot(patch.vec, currentPatch.vec)); - if (similarity > threshold) { - trace(idx); - } - } - } - } - } - - // prepare for finding the right patches - ArrayHelper.init(_patchGrid.data, 0); - ArrayHelper.init(_patchLabelGrid.data, 0); - ArrayHelper.init(_imageToPatchGrid.data, null); - - for ( j = 0; j < patchesFound.length; j++) { - patch = patchesFound[j]; - _imageToPatchGrid.data[patch.index] = patch; - _patchGrid.data[patch.index] = 1; - } - - // rasterize the patches found to determine area - _patchGrid.zeroBorder(); - - while (( currIdx = notYetProcessed()) < _patchLabelGrid.data.length) { - label++; - trace(currIdx); - } - - // draw patch-labels if requested - if (_config.showPatchLabels) { - for ( j = 0; j < _patchLabelGrid.data.length; j++) { - if (_patchLabelGrid.data[j] > 0 && _patchLabelGrid.data[j] <= label) { - patch = _imageToPatchGrid.data[j]; - hsv[0] = (_patchLabelGrid.data[j] / (label + 1)) * 360; - CVUtils.hsv2rgb(hsv, rgb); - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "rgb(" + rgb.join(",") + ")", lineWidth: 2}); - } - } - } - - return label; - } - - return { - init : function(inputImageWrapper, config) { - _config = config; - _inputImageWrapper = inputImageWrapper; - - initBuffers(); - initCanvas(); - }, - - locate : function() { - var patchesFound, - topLabels, - boxes; - - if (_config.halfSample) { - CVUtils.halfSample(_inputImageWrapper, _currentImageWrapper); - } - - binarizeImage(); - patchesFound = findPatches(); - // return unless 5% or more patches are found - if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) { - return null; - } - - // rasterrize area by comparing angular similarity; - var maxLabel = rasterizeAngularSimilarity(patchesFound); - if (maxLabel < 1) { - return null; - } - - // search for area with the most patches (biggest connected area) - topLabels = findBiggestConnectedAreas(maxLabel); - if (topLabels.length === 0) { - return null; - } - - boxes = findBoxes(topLabels, maxLabel); - return boxes; - }, - - checkImageConstraints: function(inputStream, config) { - var patchSize, - width = inputStream.getWidth(), - height = inputStream.getHeight(), - halfSample = config.halfSample ? 0.5 : 1, - size, - area; - - // calculate width and height based on area - if (inputStream.getConfig().area) { - area = CVUtils.computeImageArea(width, height, inputStream.getConfig().area); - inputStream.setTopRight({x: area.sx, y: area.sy}); - inputStream.setCanvasSize({x: width, y: height}); - width = area.sw; - height = area.sh; - } - - size = { - x: Math.floor(width * halfSample), - y: Math.floor(height * halfSample) - }; - - patchSize = CVUtils.calculatePatchSize(config.patchSize, size); - console.log("Patch-Size: " + JSON.stringify(patchSize)); - - inputStream.setWidth(Math.floor(Math.floor(size.x/patchSize.x)*(1/halfSample)*patchSize.x)); - inputStream.setHeight(Math.floor(Math.floor(size.y/patchSize.y)*(1/halfSample)*patchSize.y)); - - if ((inputStream.getWidth() % patchSize.x) === 0 && (inputStream.getHeight() % patchSize.y) === 0) { - return true; - } - - throw new Error("Image dimensions do not comply with the current settings: Width (" + - width + " )and height (" + height + - ") must a multiple of " + patchSize.x); - } - }; -}); - - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('bresenham',["cv_utils", "image_wrapper"], function(CVUtils, ImageWrapper) { - "use strict"; - var Bresenham = {}; - - var Slope = { - DIR : { - UP : 1, - DOWN : -1 - } - }; - /** - * Scans a line of the given image from point p1 to p2 and returns a result object containing - * gray-scale values (0-255) of the underlying pixels in addition to the min - * and max values. - * @param {Object} imageWrapper - * @param {Object} p1 The start point {x,y} - * @param {Object} p2 The end point {x,y} - * @returns {line, min, max} - */ - Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) { - var x0 = p1.x | 0, - y0 = p1.y | 0, - x1 = p2.x | 0, - y1 = p2.y | 0, - steep = Math.abs(y1 - y0) > Math.abs(x1 - x0), - deltax, - deltay, - error, - ystep, - y, - tmp, - x, - line = [], - imageData = imageWrapper.data, - width = imageWrapper.size.x, - sum = 0, - val, - min = 255, - max = 0; - - function read(a, b) { - val = imageData[b * width + a]; - sum += val; - min = val < min ? val : min; - max = val > max ? val : max; - line.push(val); - } - - if (steep) { - tmp = x0; - x0 = y0; - y0 = tmp; - - tmp = x1; - x1 = y1; - y1 = tmp; - } - if (x0 > x1) { - tmp = x0; - x0 = x1; - x1 = tmp; - - tmp = y0; - y0 = y1; - y1 = tmp; - } - deltax = x1 - x0; - deltay = Math.abs(y1 - y0); - error = (deltax / 2) | 0; - y = y0; - ystep = y0 < y1 ? 1 : -1; - for ( x = x0; x < x1; x++) { - if(steep){ - read(y, x); - } else { - read(x, y); - } - error = error - deltay; - if (error < 0) { - y = y + ystep; - error = error + deltax; - } - } - - return { - line : line, - min : min, - max : max - }; - }; - - Bresenham.toOtsuBinaryLine = function(result) { - var line = result.line, - image = new ImageWrapper({x: line.length - 1, y: 1}, line), - threshold = CVUtils.determineOtsuThreshold(image, 5); - - line = CVUtils.sharpenLine(line); - CVUtils.thresholdImage(image, threshold); - - return { - line: line, - threshold: threshold - }; - }; - - /** - * Converts the result from getBarcodeLine into a binary representation - * also considering the frequency and slope of the signal for more robust results - * @param {Object} result {line, min, max} - */ - Bresenham.toBinaryLine = function(result) { - - var min = result.min, - max = result.max, - line = result.line, - slope, - slope2, - center = min + (max - min) / 2, - extrema = [], - currentDir, - dir, - threshold = (max - min) / 12, - rThreshold = -threshold, - i, - j; - - // 1. find extrema - currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN; - extrema.push({ - pos : 0, - val : line[0] - }); - for ( i = 0; i < line.length - 2; i++) { - slope = (line[i + 1] - line[i]); - slope2 = (line[i + 2] - line[i + 1]); - if ((slope + slope2) < rThreshold && line[i + 1] < (center*1.5)) { - dir = Slope.DIR.DOWN; - } else if ((slope + slope2) > threshold && line[i + 1] > (center*0.5)) { - dir = Slope.DIR.UP; - } else { - dir = currentDir; - } - - if (currentDir !== dir) { - extrema.push({ - pos : i, - val : line[i] - }); - currentDir = dir; - } - } - extrema.push({ - pos : line.length, - val : line[line.length - 1] - }); - - for ( j = extrema[0].pos; j < extrema[1].pos; j++) { - line[j] = line[j] > center ? 0 : 1; - } - - // iterate over extrema and convert to binary based on avg between minmax - for ( i = 1; i < extrema.length - 1; i++) { - if (extrema[i + 1].val > extrema[i].val) { - threshold = (extrema[i].val + ((extrema[i + 1].val - extrema[i].val) / 3) * 2) | 0; - } else { - threshold = (extrema[i + 1].val + ((extrema[i].val - extrema[i + 1].val) / 3)) | 0; - } - - for ( j = extrema[i].pos; j < extrema[i + 1].pos; j++) { - line[j] = line[j] > threshold ? 0 : 1; - } - } - - return { - line : line, - threshold : threshold - }; - }; - - /** - * Used for development only - */ - Bresenham.debug = { - printFrequency: function(line, canvas) { - var i, - ctx = canvas.getContext("2d"); - canvas.width = line.length; - canvas.height = 256; - - ctx.beginPath(); - ctx.strokeStyle = "blue"; - for ( i = 0; i < line.length; i++) { - ctx.moveTo(i, 255); - ctx.lineTo(i, 255 - line[i]); - } - ctx.stroke(); - ctx.closePath(); - }, - - printPattern: function(line, canvas) { - var ctx = canvas.getContext("2d"), i; - - canvas.width = line.length; - ctx.fillColor = "black"; - for ( i = 0; i < line.length; i++) { - if (line[i] === 1) { - ctx.fillRect(i, 0, 1, 100); - } - } - } - }; - - return (Bresenham); -}); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'barcode_reader',[],function() { - "use strict"; - - function BarcodeReader(config) { - this._row = []; - this.config = config || {}; - return this; - } - - BarcodeReader.prototype._nextUnset = function(line, start) { - var i; - - if (start === undefined) { - start = 0; - } - for (i = start; i < line.length; i++) { - if (!line[i]) { - return i; - } - } - return line.length; - }; - - BarcodeReader.prototype._matchPattern = function(counter, code) { - var i, - error = 0, - singleError = 0, - modulo = this.MODULO, - maxSingleError = this.SINGLE_CODE_ERROR || 1; - - for (i = 0; i < counter.length; i++) { - singleError = Math.abs(code[i] - counter[i]); - if (singleError > maxSingleError) { - return Number.MAX_VALUE; - } - error += singleError; - } - return error/modulo; - }; - - BarcodeReader.prototype._nextSet = function(line, offset) { - var i; - - offset = offset || 0; - for (i = offset; i < line.length; i++) { - if (line[i]) { - return i; - } - } - return line.length; - }; - - BarcodeReader.prototype._normalize = function(counter, modulo) { - var i, - self = this, - sum = 0, - ratio, - numOnes = 0, - normalized = [], - norm = 0; - - if (!modulo) { - modulo = self.MODULO; - } - for (i = 0; i < counter.length; i++) { - if (counter[i] === 1) { - numOnes++; - } else { - sum += counter[i]; - } - } - ratio = sum / (modulo - numOnes); - if (ratio > 1.0) { - for (i = 0; i < counter.length; i++) { - norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; - normalized.push(norm); - } - } else { - ratio = (sum + numOnes)/modulo; - for (i = 0; i < counter.length; i++) { - norm = counter[i] / ratio; - normalized.push(norm); - } - } - return normalized; - }; - - BarcodeReader.prototype._matchTrace = function(cmpCounter, epsilon) { - var counter = [], - i, - self = this, - offset = self._nextSet(self._row), - isWhite = !self._row[offset], - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0 - }, - error; - - if (cmpCounter) { - for ( i = 0; i < cmpCounter.length; i++) { - counter.push(0); - } - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - error = self._matchPattern(counter, cmpCounter); - - if (error < epsilon) { - bestMatch.start = i - offset; - bestMatch.end = i; - bestMatch.counter = counter; - return bestMatch; - } else { - return null; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - } else { - counter.push(0); - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - counterPos++; - counter.push(0); - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - } - - // if cmpCounter was not given - bestMatch.start = offset; - bestMatch.end = self._row.length - 1; - bestMatch.counter = counter; - return bestMatch; - }; - - BarcodeReader.prototype.decodePattern = function(pattern) { - var self = this, - result; - - self._row = pattern; - result = self._decode(); - if (result === null) { - self._row.reverse(); - result = self._decode(); - if (result) { - result.direction = BarcodeReader.DIRECTION.REVERSE; - result.start = self._row.length - result.start; - result.end = self._row.length - result.end; - } - } else { - result.direction = BarcodeReader.DIRECTION.FORWARD; - } - if (result) { - result.format = self.FORMAT; - } - return result; - }; - - BarcodeReader.prototype._matchRange = function(start, end, value) { - var i; - - start = start < 0 ? 0 : start; - for (i = start; i < end; i++) { - if (this._row[i] !== value) { - return false; - } - } - return true; - }; - - BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) { - var self = this, - counterPos = 0, - i, - counters = []; - - isWhite = (typeof isWhite !== 'undefined') ? isWhite : true; - offset = (typeof offset !== 'undefined') ? offset : self._nextUnset(self._row); - end = end || self._row.length; - - counters[counterPos] = 0; - for (i = offset; i < end; i++) { - if (self._row[i] ^ isWhite) { - counters[counterPos]++; - } else { - counterPos++; - counters[counterPos] = 1; - isWhite = !isWhite; - } - } - return counters; - }; - - Object.defineProperty(BarcodeReader.prototype, "FORMAT", { - value: 'unknown', - writeable: false - }); - - BarcodeReader.DIRECTION = { - FORWARD : 1, - REVERSE : -1 - }; - - BarcodeReader.Exception = { - StartNotFoundException : "Start-Info was not found!", - CodeNotFoundException : "Code could not be found!", - PatternNotFoundException : "Pattern could not be found!" - }; - - BarcodeReader.CONFIG_KEYS = {}; - - return (BarcodeReader); - } -); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'code_128_reader',[ - "./barcode_reader" - ], - function(BarcodeReader) { - "use strict"; - - function Code128Reader() { - BarcodeReader.call(this); - } - - var properties = { - CODE_SHIFT : {value: 98}, - CODE_C : {value: 99}, - CODE_B : {value: 100}, - CODE_A : {value: 101}, - START_CODE_A : {value: 103}, - START_CODE_B : {value: 104}, - START_CODE_C : {value: 105}, - STOP_CODE : {value: 106}, - MODULO : {value: 11}, - CODE_PATTERN : {value: [ - [2, 1, 2, 2, 2, 2], - [2, 2, 2, 1, 2, 2], - [2, 2, 2, 2, 2, 1], - [1, 2, 1, 2, 2, 3], - [1, 2, 1, 3, 2, 2], - [1, 3, 1, 2, 2, 2], - [1, 2, 2, 2, 1, 3], - [1, 2, 2, 3, 1, 2], - [1, 3, 2, 2, 1, 2], - [2, 2, 1, 2, 1, 3], - [2, 2, 1, 3, 1, 2], - [2, 3, 1, 2, 1, 2], - [1, 1, 2, 2, 3, 2], - [1, 2, 2, 1, 3, 2], - [1, 2, 2, 2, 3, 1], - [1, 1, 3, 2, 2, 2], - [1, 2, 3, 1, 2, 2], - [1, 2, 3, 2, 2, 1], - [2, 2, 3, 2, 1, 1], - [2, 2, 1, 1, 3, 2], - [2, 2, 1, 2, 3, 1], - [2, 1, 3, 2, 1, 2], - [2, 2, 3, 1, 1, 2], - [3, 1, 2, 1, 3, 1], - [3, 1, 1, 2, 2, 2], - [3, 2, 1, 1, 2, 2], - [3, 2, 1, 2, 2, 1], - [3, 1, 2, 2, 1, 2], - [3, 2, 2, 1, 1, 2], - [3, 2, 2, 2, 1, 1], - [2, 1, 2, 1, 2, 3], - [2, 1, 2, 3, 2, 1], - [2, 3, 2, 1, 2, 1], - [1, 1, 1, 3, 2, 3], - [1, 3, 1, 1, 2, 3], - [1, 3, 1, 3, 2, 1], - [1, 1, 2, 3, 1, 3], - [1, 3, 2, 1, 1, 3], - [1, 3, 2, 3, 1, 1], - [2, 1, 1, 3, 1, 3], - [2, 3, 1, 1, 1, 3], - [2, 3, 1, 3, 1, 1], - [1, 1, 2, 1, 3, 3], - [1, 1, 2, 3, 3, 1], - [1, 3, 2, 1, 3, 1], - [1, 1, 3, 1, 2, 3], - [1, 1, 3, 3, 2, 1], - [1, 3, 3, 1, 2, 1], - [3, 1, 3, 1, 2, 1], - [2, 1, 1, 3, 3, 1], - [2, 3, 1, 1, 3, 1], - [2, 1, 3, 1, 1, 3], - [2, 1, 3, 3, 1, 1], - [2, 1, 3, 1, 3, 1], - [3, 1, 1, 1, 2, 3], - [3, 1, 1, 3, 2, 1], - [3, 3, 1, 1, 2, 1], - [3, 1, 2, 1, 1, 3], - [3, 1, 2, 3, 1, 1], - [3, 3, 2, 1, 1, 1], - [3, 1, 4, 1, 1, 1], - [2, 2, 1, 4, 1, 1], - [4, 3, 1, 1, 1, 1], - [1, 1, 1, 2, 2, 4], - [1, 1, 1, 4, 2, 2], - [1, 2, 1, 1, 2, 4], - [1, 2, 1, 4, 2, 1], - [1, 4, 1, 1, 2, 2], - [1, 4, 1, 2, 2, 1], - [1, 1, 2, 2, 1, 4], - [1, 1, 2, 4, 1, 2], - [1, 2, 2, 1, 1, 4], - [1, 2, 2, 4, 1, 1], - [1, 4, 2, 1, 1, 2], - [1, 4, 2, 2, 1, 1], - [2, 4, 1, 2, 1, 1], - [2, 2, 1, 1, 1, 4], - [4, 1, 3, 1, 1, 1], - [2, 4, 1, 1, 1, 2], - [1, 3, 4, 1, 1, 1], - [1, 1, 1, 2, 4, 2], - [1, 2, 1, 1, 4, 2], - [1, 2, 1, 2, 4, 1], - [1, 1, 4, 2, 1, 2], - [1, 2, 4, 1, 1, 2], - [1, 2, 4, 2, 1, 1], - [4, 1, 1, 2, 1, 2], - [4, 2, 1, 1, 1, 2], - [4, 2, 1, 2, 1, 1], - [2, 1, 2, 1, 4, 1], - [2, 1, 4, 1, 2, 1], - [4, 1, 2, 1, 2, 1], - [1, 1, 1, 1, 4, 3], - [1, 1, 1, 3, 4, 1], - [1, 3, 1, 1, 4, 1], - [1, 1, 4, 1, 1, 3], - [1, 1, 4, 3, 1, 1], - [4, 1, 1, 1, 1, 3], - [4, 1, 1, 3, 1, 1], - [1, 1, 3, 1, 4, 1], - [1, 1, 4, 1, 3, 1], - [3, 1, 1, 1, 4, 1], - [4, 1, 1, 1, 3, 1], - [2, 1, 1, 4, 1, 2], - [2, 1, 1, 2, 1, 4], - [2, 1, 1, 2, 3, 2], - [2, 3, 3, 1, 1, 1, 2] - ]}, - SINGLE_CODE_ERROR: {value: 1}, - AVG_CODE_ERROR: {value: 0.5}, - FORMAT: {value: "code_128", writeable: false} - }; - - Code128Reader.prototype = Object.create(BarcodeReader.prototype, properties); - Code128Reader.prototype.constructor = Code128Reader; - - Code128Reader.prototype._decodeCode = function(start) { - var counter = [0, 0, 0, 0, 0, 0], - i, - self = this, - offset = start, - isWhite = !self._row[offset], - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : start, - end : start - }, - code, - error, - normalized; - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < self.CODE_PATTERN.length; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - bestMatch.end = i; - return bestMatch; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; - - Code128Reader.prototype._findStart = function() { - var counter = [0, 0, 0, 0, 0, 0], - i, - self = this, - offset = self._nextSet(self._row), - isWhite = false, - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }, - code, - error, - j, - sum, - normalized; - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - sum = 0; - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - if (bestMatch.error < self.AVG_CODE_ERROR) { - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } - } - - for ( j = 0; j < 4; j++) { - counter[j] = counter[j + 2]; - } - counter[4] = 0; - counter[5] = 0; - counterPos--; - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; - - Code128Reader.prototype._decode = function() { - var self = this, - startInfo = self._findStart(), - code = null, - done = false, - result = [], - multiplier = 0, - checksum = 0, - codeset, - rawResult = [], - decodedCodes = [], - shiftNext = false, - unshift, - lastCharacterWasPrintable; - - if (startInfo === null) { - return null; - } - code = { - code : startInfo.code, - start : startInfo.start, - end : startInfo.end - }; - decodedCodes.push(code); - checksum = code.code; - switch(code.code) { - case self.START_CODE_A: - codeset = self.CODE_A; - break; - case self.START_CODE_B: - codeset = self.CODE_B; - break; - case self.START_CODE_C: - codeset = self.CODE_C; - break; - default: - return null; - } - - while (!done) { - unshift = shiftNext; - shiftNext = false; - code = self._decodeCode(code.end); - if (code !== null) { - if (code.code !== self.STOP_CODE) { - rawResult.push(code.code); - multiplier++; - checksum += multiplier * code.code; - } - decodedCodes.push(code); - - switch(codeset) { - case self.CODE_A: - if (code.code < 64) { - result.push(String.fromCharCode(32 + code.code)); - } else if (code.code < 96) { - result.push(String.fromCharCode(code.code - 64)); - } else { - switch (code.code) { - case self.CODE_SHIFT: - shiftNext = true; - codeset = self.CODE_B; - break; - case self.CODE_B: - codeset = self.CODE_B; - break; - case self.CODE_C: - codeset = self.CODE_C; - break; - case self.STOP_CODE: - done = true; - break; - } - } - break; - case self.CODE_B: - if (code.code < 96) { - result.push(String.fromCharCode(32 + code.code)); - } else { - if (code.code != self.STOP_CODE) { - lastCharacterWasPrintable = false; - } - switch (code.code) { - case self.CODE_SHIFT: - shiftNext = true; - codeset = self.CODE_A; - break; - case self.CODE_A: - codeset = self.CODE_A; - break; - case self.CODE_C: - codeset = self.CODE_C; - break; - case self.STOP_CODE: - done = true; - break; - } - } - break; - case self.CODE_C: - if (code.code < 100) { - result.push(code.code < 10 ? "0" + code.code : code.code); - } - switch (code.code) { - case self.CODE_A: - codeset = self.CODE_A; - break; - case self.CODE_B: - codeset = self.CODE_B; - break; - case self.STOP_CODE: - done = true; - break; - } - break; - } - } else { - done = true; - } - if (unshift) { - codeset = codeset == self.CODE_A ? self.CODE_B : self.CODE_A; - } - } - - if (code === null) { - return null; - } - - // find end bar - code.end = self._nextUnset(self._row, code.end); - if(!self._verifyTrailingWhitespace(code)){ - return null; - } - - // checksum - // Does not work correctly yet!!! startcode - endcode? - checksum -= multiplier * rawResult[rawResult.length - 1]; - if (checksum % 103 != rawResult[rawResult.length - 1]) { - return null; - } - - if (!result.length) { - return null; - } - - // remove last code from result (checksum) - result.splice(result.length - 1, 1); - - - - return { - code : result.join(""), - start : startInfo.start, - end : code.end, - codeset : codeset, - startInfo : startInfo, - decodedCodes : decodedCodes, - endInfo : code - }; - }; - - - BarcodeReader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; - - trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } - } - return null; - }; - - return (Code128Reader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'ean_reader',[ - "./barcode_reader" - ], - function(BarcodeReader) { - "use strict"; - - function EANReader(opts) { - BarcodeReader.call(this, opts); - } - - var properties = { - CODE_L_START : {value: 0}, - MODULO : {value: 7}, - CODE_G_START : {value: 10}, - START_PATTERN : {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, - STOP_PATTERN : {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, - MIDDLE_PATTERN : {value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7]}, - CODE_PATTERN : {value: [ - [3, 2, 1, 1], - [2, 2, 2, 1], - [2, 1, 2, 2], - [1, 4, 1, 1], - [1, 1, 3, 2], - [1, 2, 3, 1], - [1, 1, 1, 4], - [1, 3, 1, 2], - [1, 2, 1, 3], - [3, 1, 1, 2], - [1, 1, 2, 3], - [1, 2, 2, 2], - [2, 2, 1, 2], - [1, 1, 4, 1], - [2, 3, 1, 1], - [1, 3, 2, 1], - [4, 1, 1, 1], - [2, 1, 3, 1], - [3, 1, 2, 1], - [2, 1, 1, 3] - ]}, - CODE_FREQUENCY : {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]}, - SINGLE_CODE_ERROR: {value: 0.67}, - AVG_CODE_ERROR: {value: 0.27}, - FORMAT: {value: "ean_13", writeable: false} - }; - - EANReader.prototype = Object.create(BarcodeReader.prototype, properties); - EANReader.prototype.constructor = EANReader; - - EANReader.prototype._decodeCode = function(start, coderange) { - var counter = [0, 0, 0, 0], - i, - self = this, - offset = start, - isWhite = !self._row[offset], - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : start, - end : start - }, - code, - error, - normalized; - - if (!coderange) { - coderange = self.CODE_PATTERN.length; - } - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < coderange; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - bestMatch.end = i; - if (bestMatch.error > self.AVG_CODE_ERROR) { - return null; - } - return bestMatch; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; - - EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, epsilon) { - var counter = [], - self = this, - i, - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }, - error, - j, - sum, - normalized; - - if (!offset) { - offset = self._nextSet(self._row); - } - - if (isWhite === undefined) { - isWhite = false; - } - - if (tryHarder === undefined) { - tryHarder = true; - } - - if ( epsilon === undefined) { - epsilon = self.AVG_CODE_ERROR; - } - - for ( i = 0; i < pattern.length; i++) { - counter[i] = 0; - } - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - sum = 0; - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - error = self._matchPattern(normalized, pattern); - - if (error < epsilon) { - bestMatch.error = error; - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } - } - if (tryHarder) { - for ( j = 0; j < counter.length - 2; j++) { - counter[j] = counter[j + 2]; - } - counter[counter.length - 2] = 0; - counter[counter.length - 1] = 0; - counterPos--; - } else { - return null; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; - - EANReader.prototype._findStart = function() { - var self = this, - leadingWhitespaceStart, - offset = self._nextSet(self._row), - startInfo; - - while(!startInfo) { - startInfo = self._findPattern(self.START_PATTERN, offset); - if (!startInfo) { - return null; - } - leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start); - if (leadingWhitespaceStart >= 0) { - if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { - return startInfo; - } - } - offset = startInfo.end; - startInfo = null; - } - }; - - EANReader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; - - trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } - } - return null; - }; - - EANReader.prototype._findEnd = function(offset, isWhite) { - var self = this, - endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false); - - return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; - }; - - EANReader.prototype._calculateFirstDigit = function(codeFrequency) { - var i, - self = this; - - for ( i = 0; i < self.CODE_FREQUENCY.length; i++) { - if (codeFrequency === self.CODE_FREQUENCY[i]) { - return i; - } - } - return null; - }; - - EANReader.prototype._decodePayload = function(code, result, decodedCodes) { - var i, - self = this, - codeFrequency = 0x0, - firstDigit; - - for ( i = 0; i < 6; i++) { - code = self._decodeCode(code.end); - if (!code) { - return null; - } - if (code.code >= self.CODE_G_START) { - code.code = code.code - self.CODE_G_START; - codeFrequency |= 1 << (5 - i); - } else { - codeFrequency |= 0 << (5 - i); - } - result.push(code.code); - decodedCodes.push(code); - } - - firstDigit = self._calculateFirstDigit(codeFrequency); - if (firstDigit === null) { - return null; - } - result.unshift(firstDigit); - - code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); - if (code === null) { - return null; - } - decodedCodes.push(code); - - for ( i = 0; i < 6; i++) { - code = self._decodeCode(code.end, self.CODE_G_START); - if (!code) { - return null; - } - decodedCodes.push(code); - result.push(code.code); - } - - return code; - }; - - EANReader.prototype._decode = function() { - var startInfo, - self = this, - code, - result = [], - decodedCodes = []; - - startInfo = self._findStart(); - if (!startInfo) { - return null; - } - code = { - code : startInfo.code, - start : startInfo.start, - end : startInfo.end - }; - decodedCodes.push(code); - code = self._decodePayload(code, result, decodedCodes); - if (!code) { - return null; - } - code = self._findEnd(code.end, false); - if (!code){ - return null; - } - - decodedCodes.push(code); - - // Checksum - if (!self._checksum(result)) { - return null; - } - - return { - code : result.join(""), - start : startInfo.start, - end : code.end, - codeset : "", - startInfo : startInfo, - decodedCodes : decodedCodes - }; - }; - - EANReader.prototype._checksum = function(result) { - var sum = 0, i; - - for ( i = result.length - 2; i >= 0; i -= 2) { - sum += result[i]; - } - sum *= 3; - for ( i = result.length - 1; i >= 0; i -= 2) { - sum += result[i]; - } - return sum % 10 === 0; - }; - - return (EANReader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'code_39_reader',[ - "./barcode_reader", - "./array_helper" - ], - function(BarcodeReader, ArrayHelper) { - "use strict"; - - function Code39Reader() { - BarcodeReader.call(this); - } - - var properties = { - ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, - ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]}, - CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A]}, - ASTERISK: {value: 0x094}, - FORMAT: {value: "code_39", writeable: false} - }; - - Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); - Code39Reader.prototype.constructor = Code39Reader; - - Code39Reader.prototype._toCounters = function(start, counter) { - var self = this, - numCounters = counter.length, - end = self._row.length, - isWhite = !self._row[start], - i, - counterPos = 0; - - ArrayHelper.init(counter, 0); - - for ( i = start; i < end; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - counterPos++; - if (counterPos === numCounters) { - break; - } else { - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - } - - return counter; - }; - - Code39Reader.prototype._decode = function() { - var self = this, - counters = [0,0,0,0,0,0,0,0,0], - result = [], - start = self._findStart(), - decodedChar, - lastStart, - pattern, - nextStart; - - if (!start) { - return null; - } - nextStart = self._nextSet(self._row, start.end); - - do { - counters = self._toCounters(nextStart, counters); - pattern = self._toPattern(counters); - if (pattern < 0) { - return null; - } - decodedChar = self._patternToChar(pattern); - if (decodedChar < 0){ - return null; - } - result.push(decodedChar); - lastStart = nextStart; - nextStart += ArrayHelper.sum(counters); - nextStart = self._nextSet(self._row, nextStart); - } while(decodedChar !== '*'); - result.pop(); - - if (!result.length) { - return null; - } - - if(!self._verifyTrailingWhitespace(lastStart, nextStart, counters)) { - return null; - } - - return { - code : result.join(""), - start : start.start, - end : nextStart, - startInfo : start, - decodedCodes : result - }; - }; - - Code39Reader.prototype._verifyTrailingWhitespace = function(lastStart, nextStart, counters) { - var trailingWhitespaceEnd, - patternSize = ArrayHelper.sum(counters); - - trailingWhitespaceEnd = nextStart - lastStart - patternSize; - if ((trailingWhitespaceEnd * 3) >= patternSize) { - return true; - } - return false; - }; - - Code39Reader.prototype._patternToChar = function(pattern) { - var i, - self = this; - - for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { - if (self.CHARACTER_ENCODINGS[i] === pattern) { - return String.fromCharCode(self.ALPHABET[i]); - } - } - }; - - Code39Reader.prototype._findNextWidth = function(counters, current) { - var i, - minWidth = Number.MAX_VALUE; - - for (i = 0; i < counters.length; i++) { - if (counters[i] < minWidth && counters[i] > current) { - minWidth = counters[i]; - } - } - - return minWidth; - }; - - Code39Reader.prototype._toPattern = function(counters) { - var numCounters = counters.length, - maxNarrowWidth = 0, - numWideBars = numCounters, - wideBarWidth = 0, - self = this, - pattern, - i; - - while(numWideBars > 3) { - maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); - numWideBars = 0; - pattern = 0; - for (i = 0; i < numCounters; i++) { - if (counters[i] > maxNarrowWidth) { - pattern |= 1 << (numCounters - 1 - i); - numWideBars++; - wideBarWidth += counters[i]; - } - } - - if (numWideBars === 3) { - for (i = 0; i < numCounters && numWideBars > 0; i++) { - if (counters[i] > maxNarrowWidth) { - numWideBars--; - if ((counters[i] * 2) >= wideBarWidth) { - return -1; - } - } - } - return pattern; - } - } - return -1; - }; - - Code39Reader.prototype._findStart = function() { - var self = this, - offset = self._nextSet(self._row), - patternStart = offset, - counter = [0,0,0,0,0,0,0,0,0], - counterPos = 0, - isWhite = false, - i, - j, - whiteSpaceMustStart; - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - - // find start pattern - if (self._toPattern(counter) === self.ASTERISK) { - whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4))); - if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { - return { - start: patternStart, - end: i - }; - } - } - - patternStart += counter[0] + counter[1]; - for ( j = 0; j < 7; j++) { - counter[j] = counter[j + 2]; - } - counter[7] = 0; - counter[8] = 0; - counterPos--; - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; - - return (Code39Reader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'code_39_vin_reader',[ - "./code_39_reader" - ], - function(Code39Reader) { - "use strict"; - - function Code39VINReader() { - Code39Reader.call(this); - } - - var patterns = { - IOQ: /[IOQ]/g, - AZ09: /[A-Z0-9]{17}/ - }; - - Code39VINReader.prototype = Object.create(Code39Reader.prototype); - Code39VINReader.prototype.constructor = Code39VINReader; - - // Cribbed from: - // https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java - Code39VINReader.prototype._decode = function() { - var result = Code39Reader.prototype._decode.apply(this); - if (!result) { - return null; - } - - var code = result.code; - - if (!code) { - return; - } - - code = code.replace(patterns.IOQ, ''); - - if (!code.match(patterns.AZ09)) { - console.log('Failed AZ09 pattern code:', code); - return null; - } - - if (!this._checkChecksum(code)) { - return null; - } - - result.code = code; - return result; - }; - - Code39VINReader.prototype._checkChecksum = function(code) { - // TODO - return !!code; - }; - - return (Code39VINReader); - } -); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'codabar_reader',[ - "./barcode_reader" - ], - function(BarcodeReader) { - "use strict"; - - function CodabarReader() { - BarcodeReader.call(this); - this._counters = []; - } - - var properties = { - ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"}, - ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68]}, - CHARACTER_ENCODINGS: {value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E]}, - START_END: {value: [0x01A, 0x029, 0x00B, 0x00E]}, - MIN_ENCODED_CHARS: {value: 4}, - MAX_ACCEPTABLE: {value: 2.0}, - PADDING: {value: 1.5}, - FORMAT: {value: "codabar", writeable: false} - }; - - CodabarReader.prototype = Object.create(BarcodeReader.prototype, properties); - CodabarReader.prototype.constructor = CodabarReader; - - CodabarReader.prototype._decode = function() { - var self = this, - result = [], - start, - decodedChar, - pattern, - nextStart, - end; - - this._counters = self._fillCounters(); - start = self._findStart(); - if (!start) { - return null; - } - nextStart = start.startCounter; - - do { - pattern = self._toPattern(nextStart); - if (pattern < 0) { - return null; - } - decodedChar = self._patternToChar(pattern); - if (decodedChar < 0){ - return null; - } - result.push(decodedChar); - nextStart += 8; - if (result.length > 1 && self._isStartEnd(pattern)) { - break; - } - } while(nextStart < self._counters.length); - - // verify end - if ((result.length - 2) < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) { - return null; - } - - // verify end white space - if (!self._verifyWhitespace(start.startCounter, nextStart - 8)){ - return null; - } - - if (!self._validateResult(result, start.startCounter)){ - return null; - } - - nextStart = nextStart > self._counters.length ? self._counters.length : nextStart; - end = start.start + self._sumCounters(start.startCounter, nextStart - 8); - - return { - code : result.join(""), - start : start.start, - end : end, - startInfo : start, - decodedCodes : result - }; - }; - - CodabarReader.prototype._verifyWhitespace = function(startCounter, endCounter) { - if ((startCounter - 1 <= 0) || this._counters[startCounter-1] >= (this._calculatePatternLength(startCounter) / 2.0)) { - if ((endCounter + 8 >= this._counters.length) || this._counters[endCounter+7] >= (this._calculatePatternLength(endCounter) / 2.0)) { - return true; - } - } - return false; - }; - - CodabarReader.prototype._calculatePatternLength = function(offset) { - var i, - sum = 0; - - for (i = offset; i < offset + 7; i++) { - sum += this._counters[i]; - } - - return sum; - }; - - CodabarReader.prototype._thresholdResultPattern = function(result, startCounter){ - var self = this, - categorization = { - space: { - narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}, - wide: {size: 0, counts: 0, min: 0, max: Number.MAX_VALUE} - }, - bar: { - narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}, - wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE} - } - }, - kind, - cat, - i, - j, - pos = startCounter, - pattern; - - for (i = 0; i < result.length; i++){ - pattern = self._charToPattern(result[i]); - for (j = 6; j >= 0; j--) { - kind = (j & 1) === 2 ? categorization.bar : categorization.space; - cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; - cat.size += self._counters[pos + j]; - cat.counts++; - pattern >>= 1; - } - pos += 8; - } - - ["space", "bar"].forEach(function(key) { - var kind = categorization[key]; - kind.wide.min = Math.floor((kind.narrow.size/kind.narrow.counts + kind.wide.size / kind.wide.counts) / 2); - kind.narrow.max = Math.ceil(kind.wide.min); - kind.wide.max = Math.ceil((kind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / kind.wide.counts); - }); - - return categorization; - }; - - CodabarReader.prototype._charToPattern = function(char) { - var self = this, - charCode = char.charCodeAt(0), - i; - - for (i = 0; i < self.ALPHABET.length; i++) { - if (self.ALPHABET[i] === charCode){ - return self.CHARACTER_ENCODINGS[i]; - } - } - return 0x0; - }; - - CodabarReader.prototype._validateResult = function(result, startCounter) { - var self = this, - thresholds = self._thresholdResultPattern(result, startCounter), - i, - j, - kind, - cat, - size, - pos = startCounter, - pattern; - - for (i = 0; i < result.length; i++) { - pattern = self._charToPattern(result[i]); - for (j = 6; j >= 0; j--) { - kind = (j & 1) === 0 ? thresholds.bar : thresholds.space; - cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; - size = self._counters[pos + j]; - if (size < cat.min || size > cat.max) { - return false; - } - pattern >>= 1; - } - pos += 8; - } - return true; - }; - - CodabarReader.prototype._patternToChar = function(pattern) { - var i, - self = this; - - for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { - if (self.CHARACTER_ENCODINGS[i] === pattern) { - return String.fromCharCode(self.ALPHABET[i]); - } - } - return -1; - }; - - CodabarReader.prototype._computeAlternatingThreshold = function(offset, end) { - var i, - min = Number.MAX_VALUE, - max = 0, - counter; - - for (i = offset; i < end; i += 2){ - counter = this._counters[i]; - if (counter > max) { - max = counter; - } - if (counter < min) { - min = counter; - } - } - - return ((min + max) / 2.0) | 0; - }; - - CodabarReader.prototype._toPattern = function(offset) { - var numCounters = 7, - end = offset + numCounters, - barThreshold, - spaceThreshold, - bitmask = 1 << (numCounters - 1), - pattern = 0, - i, - threshold; - - if (end > this._counters.length) { - return -1; - } - - barThreshold = this._computeAlternatingThreshold(offset, end); - spaceThreshold = this._computeAlternatingThreshold(offset + 1, end); - - for (i = 0; i < numCounters; i++){ - threshold = (i & 1) === 0 ? barThreshold : spaceThreshold; - if (this._counters[offset + i] > threshold) { - pattern |= bitmask; - } - bitmask >>= 1; - } - - return pattern; - }; - - CodabarReader.prototype._isStartEnd = function(pattern) { - var i; - - for (i = 0; i < this.START_END.length; i++) { - if (this.START_END[i] === pattern) { - return true; - } - } - return false; - }; - - CodabarReader.prototype._sumCounters = function(start, end) { - var i, - sum = 0; - - for (i = start; i < end; i++) { - sum += this._counters[i]; - } - return sum; - }; - - CodabarReader.prototype._findStart = function() { - var self = this, - i, - pattern, - start = self._nextUnset(self._row), - end; - - for (i = 1; i < this._counters.length; i++) { - pattern = self._toPattern(i); - if (pattern !== -1 && self._isStartEnd(pattern)) { - // TODO: Look for whitespace ahead - start += self._sumCounters(0, i); - end = start + self._sumCounters(i, i + 8); - return { - start: start, - end: end, - startCounter: i, - endCounter: i + 8 - }; - } - } - }; - - return (CodabarReader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'upc_reader',[ - "./ean_reader" - ], - function(EANReader) { - "use strict"; - - function UPCReader() { - EANReader.call(this); - } - - var properties = { - FORMAT: {value: "upc_a", writeable: false} - }; - - UPCReader.prototype = Object.create(EANReader.prototype, properties); - UPCReader.prototype.constructor = UPCReader; - - UPCReader.prototype._decode = function() { - var result = EANReader.prototype._decode.call(this); - - if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") { - - result.code = result.code.substring(1); - return result; - } - return null; - }; - - return (UPCReader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'ean_8_reader',[ - "./ean_reader" - ], - function(EANReader) { - "use strict"; - - function EAN8Reader() { - EANReader.call(this); - } - - var properties = { - FORMAT: {value: "ean_8", writeable: false} - }; - - EAN8Reader.prototype = Object.create(EANReader.prototype, properties); - EAN8Reader.prototype.constructor = EAN8Reader; - - EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) { - var i, - self = this; - - for ( i = 0; i < 4; i++) { - code = self._decodeCode(code.end, self.CODE_G_START); - if (!code) { - return null; - } - result.push(code.code); - decodedCodes.push(code); - } - - code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); - if (code === null) { - return null; - } - decodedCodes.push(code); - - for ( i = 0; i < 4; i++) { - code = self._decodeCode(code.end, self.CODE_G_START); - if (!code) { - return null; - } - decodedCodes.push(code); - result.push(code.code); - } - - return code; - }; - - return (EAN8Reader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'upc_e_reader',[ - "./ean_reader" - ], - function(EANReader) { - "use strict"; - - function UPCEReader() { - EANReader.call(this); - } - - var properties = { - CODE_FREQUENCY : {value: [ - [ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ], - [7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]}, - STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]}, - FORMAT: {value: "upc_e", writeable: false} - }; - - UPCEReader.prototype = Object.create(EANReader.prototype, properties); - UPCEReader.prototype.constructor = UPCEReader; - - UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) { - var i, - self = this, - codeFrequency = 0x0; - - for ( i = 0; i < 6; i++) { - code = self._decodeCode(code.end); - if (!code) { - return null; - } - if (code.code >= self.CODE_G_START) { - code.code = code.code - self.CODE_G_START; - codeFrequency |= 1 << (5 - i); - } - result.push(code.code); - decodedCodes.push(code); - } - if (!self._determineParity(codeFrequency, result)) { - return null; - } - - return code; - }; - - UPCEReader.prototype._determineParity = function(codeFrequency, result) { - var self =this, - i, - nrSystem; - - for (nrSystem = 0; nrSystem < self.CODE_FREQUENCY.length; nrSystem++){ - for ( i = 0; i < self.CODE_FREQUENCY[nrSystem].length; i++) { - if (codeFrequency === self.CODE_FREQUENCY[nrSystem][i]) { - result.unshift(nrSystem); - result.push(i); - return true; - } - } - } - return false; - }; - - UPCEReader.prototype._convertToUPCA = function(result) { - var upca = [result[0]], - lastDigit = result[result.length - 2]; - - if (lastDigit <= 2) { - upca = upca.concat(result.slice(1, 3)) - .concat([lastDigit, 0, 0, 0, 0]) - .concat(result.slice(3, 6)); - } else if (lastDigit === 3) { - upca = upca.concat(result.slice(1, 4)) - .concat([0 ,0, 0, 0, 0]) - .concat(result.slice(4,6)); - } else if (lastDigit === 4) { - upca = upca.concat(result.slice(1, 5)) - .concat([0, 0, 0, 0, 0, result[5]]); - } else { - upca = upca.concat(result.slice(1, 6)) - .concat([0, 0, 0, 0, lastDigit]); - } - - upca.push(result[result.length - 1]); - return upca; - }; - - UPCEReader.prototype._checksum = function(result) { - return EANReader.prototype._checksum.call(this, this._convertToUPCA(result)); - }; - - UPCEReader.prototype._findEnd = function(offset, isWhite) { - isWhite = true; - return EANReader.prototype._findEnd.call(this, offset, isWhite); - }; - - UPCEReader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; - - trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start)/2); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } - } - }; - - return (UPCEReader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('html_utils',[], function() { - "use strict"; - - function createNode(htmlStr) { - var temp = document.createElement('div'); - - temp.innerHTML = htmlStr; - while (temp.firstChild) { - return temp.firstChild; - } - } - - function mergeObjects(obj1, obj2) { - for (var p in obj2) { - try { - if (obj2[p].constructor == Object) { - obj1[p] = mergeObjects(obj1[p], obj2[p]); - } else { - obj1[p] = obj2[p]; - } - } catch(e) { - obj1[p] = obj2[p]; - } - } - - return obj1; - } - - return { - createNode : function(htmlStr) { - return createNode(htmlStr); - }, - mergeObjects : function(obj1, obj2) { - return mergeObjects(obj1, obj2); - } - }; -}); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - 'i2of5_reader',[ - "./barcode_reader", - "./html_utils" - ], - function(BarcodeReader, HTMLUtils) { - "use strict"; - - function I2of5Reader(opts) { - opts = HTMLUtils.mergeObjects(getDefaulConfig(), opts); - BarcodeReader.call(this, opts); - this.barSpaceRatio = [1, 1]; - if (opts.normalizeBarSpaceWidth) { - this.SINGLE_CODE_ERROR = 0.38; - this.AVG_CODE_ERROR = 0.09; - } - } - - function getDefaulConfig() { - var config = {}; - - Object.keys(I2of5Reader.CONFIG_KEYS).forEach(function(key) { - config[key] = I2of5Reader.CONFIG_KEYS[key]['default']; - }); - return config; - } - - var N = 1, - W = 3, - properties = { - MODULO : {value: 10}, - START_PATTERN : {value: [N*2.5, N*2.5, N*2.5, N*2.5]}, - STOP_PATTERN : {value: [N*2, N*2, W*2]}, - CODE_PATTERN : {value: [ - [N, N, W, W, N], - [W, N, N, N, W], - [N, W, N, N, W], - [W, W, N, N, N], - [N, N, W, N, W], - [W, N, W, N, N], - [N, W, W, N, N], - [N, N, N, W, W], - [W, N, N, W, N], - [N, W, N, W, N] - ]}, - SINGLE_CODE_ERROR: {value: 0.78, writable: true}, - AVG_CODE_ERROR: {value: 0.38, writable: true}, - MAX_CORRECTION_FACTOR: {value: 5}, - FORMAT: {value: "i2of5"} - }; - - I2of5Reader.prototype = Object.create(BarcodeReader.prototype, properties); - I2of5Reader.prototype.constructor = I2of5Reader; - - I2of5Reader.prototype._matchPattern = function(counter, code) { - if (this.config.normalizeBarSpaceWidth) { - var i, - counterSum = [0, 0], - codeSum = [0, 0], - correction = [0, 0], - correctionRatio = this.MAX_CORRECTION_FACTOR, - correctionRatioInverse = 1 / correctionRatio; - - for (i = 0; i < counter.length; i++) { - counterSum[i % 2] += counter[i]; - codeSum[i % 2] += code[i]; - } - correction[0] = codeSum[0] / counterSum[0]; - correction[1] = codeSum[1] / counterSum[1]; - - correction[0] = Math.max(Math.min(correction[0], correctionRatio), correctionRatioInverse); - correction[1] = Math.max(Math.min(correction[1], correctionRatio), correctionRatioInverse); - this.barSpaceRatio = correction; - for (i = 0; i < counter.length; i++) { - counter[i] *= this.barSpaceRatio[i % 2]; - } - } - return BarcodeReader.prototype._matchPattern.call(this, counter, code); - }; - - I2of5Reader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) { - var counter = [], - self = this, - i, - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }, - error, - j, - sum, - normalized, - epsilon = self.AVG_CODE_ERROR; - - isWhite = isWhite || false; - tryHarder = tryHarder || false; - - if (!offset) { - offset = self._nextSet(self._row); - } - - for ( i = 0; i < pattern.length; i++) { - counter[i] = 0; - } - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - sum = 0; - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - error = self._matchPattern(normalized, pattern); - - if (error < epsilon) { - bestMatch.error = error; - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } - } - if (tryHarder) { - for (j = 0; j < counter.length - 2; j++) { - counter[j] = counter[j + 2]; - } - counter[counter.length - 2] = 0; - counter[counter.length - 1] = 0; - counterPos--; - } else { - return null; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; - - I2of5Reader.prototype._findStart = function() { - var self = this, - leadingWhitespaceStart, - offset = self._nextSet(self._row), - startInfo, - narrowBarWidth = 1; - - while(!startInfo) { - startInfo = self._findPattern(self.START_PATTERN, offset, false, true); - if (!startInfo) { - return null; - } - narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / 4); - leadingWhitespaceStart = startInfo.start - narrowBarWidth*10; - if (leadingWhitespaceStart >= 0) { - if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { - return startInfo; - } - } - offset = startInfo.end; - startInfo = null; - } - }; - - I2of5Reader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; - - trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } - } - return null; - }; - - I2of5Reader.prototype._findEnd = function() { - var self = this, - endInfo, - tmp; - - self._row.reverse(); - endInfo = self._findPattern(self.STOP_PATTERN); - self._row.reverse(); - - if (endInfo === null) { - return null; - } - - // reverse numbers - tmp = endInfo.start; - endInfo.start = self._row.length - endInfo.end; - endInfo.end = self._row.length - tmp; - - return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; - }; - - I2of5Reader.prototype._decodePair = function(counterPair) { - var i, - code, - codes = [], - self = this; - - for (i = 0; i < counterPair.length; i++) { - code = self._decodeCode(counterPair[i]); - if (!code) { - return null; - } - codes.push(code); - } - return codes; - }; - - I2of5Reader.prototype._decodeCode = function(counter) { - var j, - self = this, - sum = 0, - normalized, - error, - epsilon = self.AVG_CODE_ERROR, - code, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }; - - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < self.CODE_PATTERN.length; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - if (bestMatch.error < epsilon) { - return bestMatch; - } - } - return null; - }; - - I2of5Reader.prototype._decodePayload = function(counters, result, decodedCodes) { - var i, - self = this, - pos = 0, - counterLength = counters.length, - counterPair = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], - codes; - - while (pos < counterLength) { - for (i = 0; i < 5; i++) { - counterPair[0][i] = counters[pos]*this.barSpaceRatio[0]; - counterPair[1][i] = counters[pos + 1]*this.barSpaceRatio[1]; - pos += 2; - } - codes = self._decodePair(counterPair); - if (!codes) { - return null; - } - for (i = 0; i < codes.length; i++) { - result.push(codes[i].code + ""); - decodedCodes.push(codes[i]); - } - } - return codes; - }; - - I2of5Reader.prototype._verifyCounterLength = function(counters) { - return (counters.length % 10 === 0); - }; - - I2of5Reader.prototype._decode = function() { - var startInfo, - endInfo, - self = this, - code, - result = [], - decodedCodes = [], - counters; - - startInfo = self._findStart(); - if (!startInfo) { - return null; - } - decodedCodes.push(startInfo); - - endInfo = self._findEnd(); - if (!endInfo) { - return null; - } - - counters = self._fillCounters(startInfo.end, endInfo.start, false); - if (!self._verifyCounterLength(counters)) { - return null; - } - code = self._decodePayload(counters, result, decodedCodes); - if (!code) { - return null; - } - if (result.length % 2 !== 0 || - result.length < 6) { - return null; - } - - decodedCodes.push(endInfo); - return { - code : result.join(""), - start : startInfo.start, - end : endInfo.end, - startInfo : startInfo, - decodedCodes : decodedCodes - }; - }; - - I2of5Reader.CONFIG_KEYS = { - normalizeBarSpaceWidth: { - 'type': 'boolean', - 'default': false, - 'description': 'If true, the reader tries to normalize the' + - 'width-difference between bars and spaces' - } - }; - - return (I2of5Reader); - } -); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('barcode_decoder',[ - "bresenham", - "image_debug", - 'code_128_reader', - 'ean_reader', - 'code_39_reader', - 'code_39_vin_reader', - 'codabar_reader', - 'upc_reader', - 'ean_8_reader', - 'upc_e_reader', - 'i2of5_reader' -], function( - Bresenham, - ImageDebug, - Code128Reader, - EANReader, - Code39Reader, - Code39VINReader, - CodabarReader, - UPCReader, - EAN8Reader, - UPCEReader, - I2of5Reader) { - "use strict"; - - var readers = { - code_128_reader: Code128Reader, - ean_reader: EANReader, - ean_8_reader: EAN8Reader, - code_39_reader: Code39Reader, - code_39_vin_reader: Code39VINReader, - codabar_reader: CodabarReader, - upc_reader: UPCReader, - upc_e_reader: UPCEReader, - i2of5_reader: I2of5Reader - }; - var BarcodeDecoder = { - create : function(config, inputImageWrapper) { - var _canvas = { - ctx : { - frequency : null, - pattern : null, - overlay : null - }, - dom : { - frequency : null, - pattern : null, - overlay : null - } - }, - _barcodeReaders = []; - - initCanvas(); - initReaders(); - initConfig(); - - function initCanvas() { - if (typeof document !== 'undefined') { - var $debug = document.querySelector("#debug.detection"); - _canvas.dom.frequency = document.querySelector("canvas.frequency"); - if (!_canvas.dom.frequency) { - _canvas.dom.frequency = document.createElement("canvas"); - _canvas.dom.frequency.className = "frequency"; - if($debug) { - $debug.appendChild(_canvas.dom.frequency); - } - } - _canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d"); - - _canvas.dom.pattern = document.querySelector("canvas.patternBuffer"); - if (!_canvas.dom.pattern) { - _canvas.dom.pattern = document.createElement("canvas"); - _canvas.dom.pattern.className = "patternBuffer"; - if($debug) { - $debug.appendChild(_canvas.dom.pattern); - } - } - _canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d"); - - _canvas.dom.overlay = document.querySelector("canvas.drawingBuffer"); - if (_canvas.dom.overlay) { - _canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d"); - } - } - } - - function initReaders() { - config.readers.forEach(function(readerConfig) { - var reader, - config = {}; - - if (typeof readerConfig === 'object') { - reader = readerConfig.format; - config = readerConfig.config; - } else if (typeof readerConfig === 'string') { - reader = readerConfig; - } - _barcodeReaders.push(new readers[reader](config)); - }); - console.log("Registered Readers: " + _barcodeReaders - .map(function(reader) {return JSON.stringify({format: reader.FORMAT, config: reader.config});}) - .join(', ')); - } - - function initConfig() { - if (typeof document !== 'undefined') { - var i, - vis = [{ - node : _canvas.dom.frequency, - prop : config.showFrequency - }, { - node : _canvas.dom.pattern, - prop : config.showPattern - }]; - - for (i = 0; i < vis.length; i++) { - if (vis[i].prop === true) { - vis[i].node.style.display = "block"; - } else { - vis[i].node.style.display = "none"; - } - } - } - } - - /** - * extend the line on both ends - * @param {Array} line - * @param {Number} angle - */ - function getExtendedLine(line, angle, ext) { - function extendLine(amount) { - var extension = { - y : amount * Math.sin(angle), - x : amount * Math.cos(angle) - }; - - line[0].y -= extension.y; - line[0].x -= extension.x; - line[1].y += extension.y; - line[1].x += extension.x; - } - - // check if inside image - extendLine(ext); - while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0))) { - ext -= Math.ceil(ext/2); - extendLine(-ext); - } - return line; - } - - function getLine(box) { - return [{ - x : (box[1][0] - box[0][0]) / 2 + box[0][0], - y : (box[1][1] - box[0][1]) / 2 + box[0][1] - }, { - x : (box[3][0] - box[2][0]) / 2 + box[2][0], - y : (box[3][1] - box[2][1]) / 2 + box[2][1] - }]; - } - - function tryDecode(line) { - var result = null, - i, - barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, line[0], line[1]); - - if (config.showFrequency) { - ImageDebug.drawPath(line, {x: 'x', y: 'y'}, _canvas.ctx.overlay, {color: 'red', lineWidth: 3}); - Bresenham.debug.printFrequency(barcodeLine.line, _canvas.dom.frequency); - } - Bresenham.toBinaryLine(barcodeLine); - if (config.showPattern) { - Bresenham.debug.printPattern(barcodeLine.line, _canvas.dom.pattern); - } - - for ( i = 0; i < _barcodeReaders.length && result === null; i++) { - result = _barcodeReaders[i].decodePattern(barcodeLine.line); - } - if(result === null){ - return null; - } - return { - codeResult: result, - barcodeLine: barcodeLine - }; - - } - - /** - * This method slices the given area apart and tries to detect a barcode-pattern - * for each slice. It returns the decoded barcode, or null if nothing was found - * @param {Array} box - * @param {Array} line - * @param {Number} lineAngle - */ - function tryDecodeBruteForce(box, line, lineAngle) { - var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)), - i, - slices = 16, - result = null, - dir, - extension, - xdir = Math.sin(lineAngle), - ydir = Math.cos(lineAngle); - - for ( i = 1; i < slices && result === null; i++) { - // move line perpendicular to angle - dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1); - extension = { - y : dir * xdir, - x : dir * ydir - }; - line[0].y += extension.x; - line[0].x -= extension.y; - line[1].y += extension.x; - line[1].x -= extension.y; - - result = tryDecode(line); - } - return result; - } - - function getLineLength(line) { - return Math.sqrt( - Math.pow(Math.abs(line[1].y - line[0].y), 2) + - Math.pow(Math.abs(line[1].x - line[0].x), 2)); - } - - /** - * With the help of the configured readers (Code128 or EAN) this function tries to detect a - * valid barcode pattern within the given area. - * @param {Object} box The area to search in - * @returns {Object} the result {codeResult, line, angle, pattern, threshold} - */ - function decodeFromBoundingBox(box) { - var line, - lineAngle, - ctx = _canvas.ctx.overlay, - result, - lineLength; - - if (config.drawBoundingBox && ctx) { - ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2}); - } - - line = getLine(box); - lineLength = getLineLength(line); - lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); - line = getExtendedLine(line, lineAngle, Math.floor(lineLength*0.1)); - if(line === null){ - return null; - } - - result = tryDecode(line); - if(result === null) { - result = tryDecodeBruteForce(box, line, lineAngle); - } - - if(result === null) { - return null; - } - - if (result && config.drawScanline && ctx) { - ImageDebug.drawPath(line, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 3}); - } - - return { - codeResult : result.codeResult, - line : line, - angle : lineAngle, - pattern : result.barcodeLine.line, - threshold : result.barcodeLine.threshold - }; - } - - return { - decodeFromBoundingBox : function(box) { - return decodeFromBoundingBox(box); - }, - decodeFromBoundingBoxes : function(boxes) { - var i, result; - for ( i = 0; i < boxes.length; i++) { - result = decodeFromBoundingBox(boxes[i]); - if (result && result.codeResult) { - result.box = boxes[i]; - return result; - } - } - }, - setReaders: function(readers) { - config.readers = readers; - _barcodeReaders.length = 0; - initReaders(); - } - }; - } - }; - - return (BarcodeDecoder); -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('frame_grabber',["cv_utils"], function(CVUtils) { - "use strict"; - - var FrameGrabber = {}; - - FrameGrabber.create = function(inputStream, canvas) { - var _that = {}, - _streamConfig = inputStream.getConfig(), - _video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), - _canvasSize = inputStream.getCanvasSize(), - _size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), - topRight = inputStream.getTopRight(), - _sx = topRight.x, - _sy = topRight.y, - _canvas, - _ctx = null, - _data = null; - - _canvas = canvas ? canvas : document.createElement("canvas"); - _canvas.width = _canvasSize.x; - _canvas.height = _canvasSize.y; - _ctx = _canvas.getContext("2d"); - _data = new Uint8Array(_size.x * _size.y); - console.log("FrameGrabber", JSON.stringify({ - size: _size, - topRight: topRight, - videoSize: _video_size, - canvasSize: _canvasSize - })); - - /** - * Uses the given array as frame-buffer - */ - _that.attachData = function(data) { - _data = data; - }; - - /** - * Returns the used frame-buffer - */ - _that.getData = function() { - return _data; - }; - - /** - * Fetches a frame from the input-stream and puts into the frame-buffer. - * The image-data is converted to gray-scale and then half-sampled if configured. - */ - _that.grab = function() { - var doHalfSample = _streamConfig.halfSample, - frame = inputStream.getFrame(), - ctxData; - if (frame) { - _ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y); - ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data; - if(doHalfSample){ - CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data); - } else { - CVUtils.computeGray(ctxData, _data, _streamConfig); - } - return true; - } else { - return false; - } - }; - - _that.getSize = function() { - return _size; - }; - - return _that; - }; - - return (FrameGrabber); -}); - -/** - * The basic configuration - */ - -define('config',[],function(){ - var config = { - inputStream: { name: "Live", - type: "LiveStream", - constraints: { - width: 640, - height: 480, - minAspectRatio: 0, - maxAspectRatio: 100, - facing: "environment" // or user - }, - area: { - top: "0%", - right: "0%", - left: "0%", - bottom: "0%" - }, - singleChannel: false // true: only the red color-channel is read - }, - tracking: false, - debug: false, - controls: false, - locate: true, - numOfWorkers: 4, - visual: { - show: true - }, - decoder:{ - drawBoundingBox: false, - showFrequency: false, - drawScanline: false, - showPattern: false, - readers: [ - 'code_128_reader' - ] - }, - locator: { - halfSample: true, - patchSize: "medium", // x-small, small, medium, large, x-large - showCanvas: false, - showPatches: false, - showFoundPatches: false, - showSkeleton: false, - showLabels: false, - showPatchLabels: false, - showRemainingPatchLabels: false, - boxFromPatches: { - showTransformed: false, - showTransformedBox: false, - showBB: false - } - } - }; - - return config; -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('events',[],function() { - "use strict"; - - var _events = function() { - var events = {}; - - function getEvent(eventName) { - if (!events[eventName]) { - events[eventName] = { - subscribers : [] - }; - } - return events[eventName]; - } - - function clearEvents(){ - events = {}; - } - - function publishSubscription(subscription, data) { - if (subscription.async) { - setTimeout(function() { - subscription.callback(data); - }, 4); - } else { - subscription.callback(data); - } - } - - function subscribe(event, callback, async) { - var subscription; - - if ( typeof callback === "function") { - subscription = { - callback : callback, - async : async - }; - } else { - subscription = callback; - if (!subscription.callback) { - throw "Callback was not specified on options"; - } - } - - getEvent(event).subscribers.push(subscription); - } - - return { - subscribe : function(event, callback, async) { - return subscribe(event, callback, async); - }, - publish : function(eventName, data) { - var event = getEvent(eventName), - subscribers = event.subscribers; - - event.subscribers = subscribers.filter(function(subscriber) { - publishSubscription(subscriber, data); - return !subscriber.once; - }); - }, - once: function(event, callback, async) { - subscribe(event, { - callback: callback, - async: async, - once: true - }); - }, - unsubscribe: function(eventName, callback) { - var event; - - if (eventName) { - event = getEvent(eventName); - if (event && callback) { - event.subscribers = event.subscribers.filter(function(subscriber){ - return subscriber.callback !== callback; - }); - } else { - event.subscribers = []; - } - } else { - clearEvents(); - } - } - }; - }(); - - return _events; -}); -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define, MediaStreamTrack */ - -define('camera_access',["html_utils"], function(HtmlUtils) { - "use strict"; - var streamRef, - loadedDataHandler; - - /** - * Wraps browser-specific getUserMedia - * @param {Object} constraints - * @param {Object} success Callback - * @param {Object} failure Callback - */ - function getUserMedia(constraints, success, failure) { - if (typeof navigator.getUserMedia !== 'undefined') { - navigator.getUserMedia(constraints, function (stream) { - streamRef = stream; - var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream; - success.apply(null, [videoSrc]); - }, failure); - } else { - failure(new TypeError("getUserMedia not available")); - } - } - - function loadedData(video, callback) { - var attempts = 10; - - function checkVideo() { - if (attempts > 0) { - if (video.videoWidth > 0 && video.videoHeight > 0) { - console.log(video.videoWidth + "px x " + video.videoHeight + "px"); - callback(); - } else { - window.setTimeout(checkVideo, 500); - } - } else { - callback('Unable to play video stream. Is webcam working?'); - } - attempts--; - } - checkVideo(); - } - - /** - * Tries to attach the camera-stream to a given video-element - * and calls the callback function when the content is ready - * @param {Object} constraints - * @param {Object} video - * @param {Object} callback - */ - function initCamera(constraints, video, callback) { - getUserMedia(constraints, function(src) { - video.src = src; - if (loadedDataHandler) { - video.removeEventListener("loadeddata", loadedDataHandler, false); - } - loadedDataHandler = loadedData.bind(null, video, callback); - video.addEventListener('loadeddata', loadedDataHandler, false); - video.play(); - }, function(e) { - callback(e); - }); - } - - /** - * Normalizes the incoming constraints to satisfy the current browser - * @param config - * @param cb Callback which is called whenever constraints are created - * @returns {*} - */ - function normalizeConstraints(config, cb) { - var constraints = { - audio: false, - video: true - }, - videoConstraints = HtmlUtils.mergeObjects({ - width: 640, - height: 480, - minAspectRatio: 0, - maxAspectRatio: 100, - facing: "environment" - }, config); - - if ( typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') { - MediaStreamTrack.getSources(function(sourceInfos) { - var videoSourceId; - for (var i = 0; i != sourceInfos.length; ++i) { - var sourceInfo = sourceInfos[i]; - if (sourceInfo.kind == "video" && sourceInfo.facing == videoConstraints.facing) { - videoSourceId = sourceInfo.id; - } - } - constraints.video = { - mandatory: { - minWidth: videoConstraints.width, - minHeight: videoConstraints.height, - minAspectRatio: videoConstraints.minAspectRatio, - maxAspectRatio: videoConstraints.maxAspectRatio - }, - optional: [{ - sourceId: videoSourceId - }] - }; - return cb(constraints); - }); - } else { - constraints.video = { - mediaSource: "camera", - width: { min: videoConstraints.width, max: videoConstraints.width }, - height: { min: videoConstraints.height, max: videoConstraints.height }, - require: ["width", "height"] - }; - return cb(constraints); - } - } - - /** - * Requests the back-facing camera of the user. The callback is called - * whenever the stream is ready to be consumed, or if an error occures. - * @param {Object} video - * @param {Object} callback - */ - function request(video, videoConstraints, callback) { - normalizeConstraints(videoConstraints, function(constraints) { - initCamera(constraints, video, callback); - }); - } - - return { - request : function(video, constraints, callback) { - request(video, constraints, callback); - }, - release : function() { - var tracks = streamRef && streamRef.getVideoTracks(); - if (tracks.length) { - tracks[0].stop(); - } - streamRef = null; - } - }; -}); - -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define('result_collector',["image_debug"], function(ImageDebug) { - "use strict"; - - function contains(codeResult, list) { - if (list) { - return list.some(function (item) { - return Object.keys(item).every(function (key) { - return item[key] === codeResult[key]; - }); - }); - } - return false; - } - - function passesFilter(codeResult, filter) { - if (typeof filter === 'function') { - return filter(codeResult); - } - return true; - } - - return { - create: function(config) { - var canvas = document.createElement("canvas"), - ctx = canvas.getContext("2d"), - results = [], - capacity = config.capacity || 20, - capture = config.capture === true; - - function matchesConstraints(codeResult) { - return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter); - } - - return { - addResult: function(data, imageSize, codeResult) { - var result = {}; - - if (matchesConstraints(codeResult)) { - capacity--; - result.codeResult = codeResult; - if (capture) { - canvas.width = imageSize.x; - canvas.height = imageSize.y; - ImageDebug.drawImage(data, imageSize, ctx); - result.frame = canvas.toDataURL(); - } - results.push(result); - } - }, - getResults: function() { - return results; - } - }; - } - }; -}); - -/* jshint undef: true, unused: true, browser:true, devel: true, evil: true */ -/* global define */ -define('quagga',[ - "input_stream", - "image_wrapper", - "barcode_locator", - "barcode_decoder", - "frame_grabber", - "html_utils", - "config", - "events", - "camera_access", - "image_debug", - "gl-matrix", - "result_collector"], -function(InputStream, - ImageWrapper, - BarcodeLocator, - BarcodeDecoder, - FrameGrabber, - HtmlUtils, - _config, - Events, - CameraAccess, - ImageDebug, - glMatrix, - ResultCollector) { - "use strict"; - - var _inputStream, - _framegrabber, - _stopped, - _canvasContainer = { - ctx : { - image : null, - overlay : null - }, - dom : { - image : null, - overlay : null - } - }, - _inputImageWrapper, - _boxSize, - _decoder, - _workerPool = [], - _onUIThread = true, - vec2 = glMatrix.vec2, - _resultCollector; - - function initializeData(imageWrapper) { - initBuffers(imageWrapper); - _decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper); - } - - function initConfig() { - if (typeof document !== "undefined") { - var vis = [{ - node: document.querySelector("div[data-controls]"), - prop: _config.controls - }, { - node: _canvasContainer.dom.overlay, - prop: _config.visual.show - }]; - - for (var i = 0; i < vis.length; i++) { - if (vis[i].node) { - if (vis[i].prop === true) { - vis[i].node.style.display = "block"; - } else { - vis[i].node.style.display = "none"; - } - } - } - } - } - - function initInputStream(cb) { - var video; - if (_config.inputStream.type == "VideoStream") { - video = document.createElement("video"); - _inputStream = InputStream.createVideoStream(video); - } else if (_config.inputStream.type == "ImageStream") { - _inputStream = InputStream.createImageStream(); - } else if (_config.inputStream.type == "LiveStream") { - var $viewport = document.querySelector("#interactive.viewport"); - if ($viewport) { - video = $viewport.querySelector("video"); - if (!video) { - video = document.createElement("video"); - $viewport.appendChild(video); - } - } - _inputStream = InputStream.createLiveStream(video); - CameraAccess.request(video, _config.inputStream.constraints, function(err) { - if (!err) { - _inputStream.trigger("canrecord"); - } else { - return cb(err); - } - }); - } - - _inputStream.setAttribute("preload", "auto"); - _inputStream.setAttribute("autoplay", true); - _inputStream.setInputStream(_config.inputStream); - _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); - } - - function canRecord(cb) { - BarcodeLocator.checkImageConstraints(_inputStream, _config.locator); - initCanvas(); - _framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image); - initConfig(); - - if (_config.numOfWorkers > 0) { - initWorkers(function() { - console.log("Workers created"); - ready(cb); - }); - } else { - initializeData(); - ready(cb); - } - } - - function ready(cb){ - _inputStream.play(); - cb(); - } - - function initCanvas() { - if (typeof document !== "undefined") { - var $viewport = document.querySelector("#interactive.viewport"); - _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); - if (!_canvasContainer.dom.image) { - _canvasContainer.dom.image = document.createElement("canvas"); - _canvasContainer.dom.image.className = "imgBuffer"; - if ($viewport && _config.inputStream.type == "ImageStream") { - $viewport.appendChild(_canvasContainer.dom.image); - } - } - _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); - _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; - _canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; - - _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer"); - if (!_canvasContainer.dom.overlay) { - _canvasContainer.dom.overlay = document.createElement("canvas"); - _canvasContainer.dom.overlay.className = "drawingBuffer"; - if ($viewport) { - $viewport.appendChild(_canvasContainer.dom.overlay); - } - var clearFix = document.createElement("br"); - clearFix.setAttribute("clear", "all"); - if ($viewport) { - $viewport.appendChild(clearFix); - } - } - _canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); - _canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; - _canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; - } - } - - function initBuffers(imageWrapper) { - if (imageWrapper) { - _inputImageWrapper = imageWrapper; - } else { - _inputImageWrapper = new ImageWrapper({ - x : _inputStream.getWidth(), - y : _inputStream.getHeight() - }); - } - - console.log(_inputImageWrapper.size); - _boxSize = [ - vec2.clone([0, 0]), - vec2.clone([0, _inputImageWrapper.size.y]), - vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]), - vec2.clone([_inputImageWrapper.size.x, 0]) - ]; - BarcodeLocator.init(_inputImageWrapper, _config.locator); - } - - function getBoundingBoxes() { - if (_config.locate) { - return BarcodeLocator.locate(); - } else { - return [[ - vec2.clone(_boxSize[0]), - vec2.clone(_boxSize[1]), - vec2.clone(_boxSize[2]), - vec2.clone(_boxSize[3])]]; - } - } - - function transformResult(result) { - var topRight = _inputStream.getTopRight(), - xOffset = topRight.x, - yOffset = topRight.y, - i; - - if (!result || (xOffset === 0 && yOffset === 0)) { - return; - } - - - if (result.line && result.line.length === 2) { - moveLine(result.line); - } - if (result.boxes && result.boxes.length > 0) { - for (i = 0; i < result.boxes.length; i++) { - moveBox(result.boxes[i]); - } - } - - function moveBox(box) { - var corner = box.length; - - while(corner--) { - box[corner][0] += xOffset; - box[corner][1] += yOffset; - } - } - - function moveLine(line) { - line[0].x += xOffset; - line[0].y += yOffset; - line[1].x += xOffset; - line[1].y += yOffset; - } - } - - function publishResult(result, imageData) { - if (_onUIThread) { - transformResult(result); - if (imageData && result && result.codeResult) { - if (_resultCollector) { - _resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult); - } - } - } - - Events.publish("processed", result); - if (result && result.codeResult) { - Events.publish("detected", result); - } - } - - function locateAndDecode() { - var result, - boxes; - - boxes = getBoundingBoxes(); - if (boxes) { - result = _decoder.decodeFromBoundingBoxes(boxes); - result = result || {}; - result.boxes = boxes; - publishResult(result, _inputImageWrapper.data); - } else { - publishResult(); - } - } - - function update() { - var availableWorker; - - if (_onUIThread) { - if (_workerPool.length > 0) { - availableWorker = _workerPool.filter(function(workerThread) { - return !workerThread.busy; - })[0]; - if (availableWorker) { - _framegrabber.attachData(availableWorker.imageData); - } else { - return; // all workers are busy - } - } else { - _framegrabber.attachData(_inputImageWrapper.data); - } - if (_framegrabber.grab()) { - if (availableWorker) { - availableWorker.busy = true; - availableWorker.worker.postMessage({ - cmd: 'process', - imageData: availableWorker.imageData - }, [availableWorker.imageData.buffer]); - } else { - locateAndDecode(); - } - } - } else { - locateAndDecode(); - } - } - - function start() { - _stopped = false; - ( function frame() { - if (!_stopped) { - update(); - if (_onUIThread && _config.inputStream.type == "LiveStream") { - window.requestAnimFrame(frame); - } - } - }()); - } - - function initWorkers(cb) { - var i; - _workerPool = []; - - for (i = 0; i < _config.numOfWorkers; i++) { - initWorker(workerInitialized); - } - - function workerInitialized(workerThread) { - _workerPool.push(workerThread); - if (_workerPool.length >= _config.numOfWorkers){ - cb(); - } - } - } - - function initWorker(cb) { - var blobURL, - workerThread = { - worker: undefined, - imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), - busy: true - }; - - blobURL = generateWorkerBlob(); - workerThread.worker = new Worker(blobURL); - - workerThread.worker.onmessage = function(e) { - if (e.data.event === 'initialized') { - URL.revokeObjectURL(blobURL); - workerThread.busy = false; - workerThread.imageData = new Uint8Array(e.data.imageData); - console.log("Worker initialized"); - return cb(workerThread); - } else if (e.data.event === 'processed') { - workerThread.imageData = new Uint8Array(e.data.imageData); - workerThread.busy = false; - publishResult(e.data.result, workerThread.imageData); - } else if (e.data.event === 'error') { - console.log("Worker error: " + e.data.message); - } - }; - - workerThread.worker.postMessage({ - cmd: 'init', - size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()}, - imageData: workerThread.imageData, - config: _config - }, [workerThread.imageData.buffer]); - } - - - function workerInterface(factory) { - if (factory) { - /* jshint ignore:start */ - var Quagga = factory(); - if (!Quagga) { - self.postMessage({'event': 'error', message: 'Quagga could not be created'}); - return; - } - /* jshint ignore:end */ - } - /* jshint ignore:start */ - var imageWrapper; - - self.onmessage = function(e) { - if (e.data.cmd === 'init') { - var config = e.data.config; - config.numOfWorkers = 0; - imageWrapper = new Quagga.ImageWrapper({ - x : e.data.size.x, - y : e.data.size.y - }, new Uint8Array(e.data.imageData)); - Quagga.init(config, ready, imageWrapper); - Quagga.onProcessed(onProcessed); - } else if (e.data.cmd === 'process') { - imageWrapper.data = new Uint8Array(e.data.imageData); - Quagga.start(); - } else if (e.data.cmd === 'setReaders') { - Quagga.setReaders(e.data.readers); - } - }; - - function onProcessed(result) { - self.postMessage({'event': 'processed', imageData: imageWrapper.data, result: result}, [imageWrapper.data.buffer]); - } - - function ready() { - self.postMessage({'event': 'initialized', imageData: imageWrapper.data}, [imageWrapper.data.buffer]); - } - /* jshint ignore:end */ - } - - function generateWorkerBlob() { - var blob, - factorySource; - - /* jshint ignore:start */ - if (typeof __factorySource__ !== 'undefined') { - factorySource = __factorySource__; - } - /* jshint ignore:end */ - - blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'], - {type : 'text/javascript'}); - - return window.URL.createObjectURL(blob); - } - - function setReaders(readers) { - if (_decoder) { - _decoder.setReaders(readers); - } else if (_onUIThread && _workerPool.length > 0) { - _workerPool.forEach(function(workerThread) { - workerThread.worker.postMessage({cmd: 'setReaders', readers: readers}); - }); - } - } - - return { - init : function(config, cb, imageWrapper) { - _config = HtmlUtils.mergeObjects(_config, config); - if (imageWrapper) { - _onUIThread = false; - initializeData(imageWrapper); - return cb(); - } else { - initInputStream(cb); - } - }, - start : function() { - start(); - }, - stop : function() { - _stopped = true; - _workerPool.forEach(function(workerThread) { - workerThread.worker.terminate(); - console.log("Worker terminated!"); - }); - _workerPool.length = 0; - if (_config.inputStream.type === "LiveStream") { - CameraAccess.release(); - _inputStream.clearEventHandlers(); - } - }, - pause: function() { - _stopped = true; - }, - onDetected : function(callback) { - Events.subscribe("detected", callback); - }, - offDetected: function(callback) { - Events.unsubscribe("detected", callback); - }, - onProcessed: function(callback) { - Events.subscribe("processed", callback); - }, - offProcessed: function(callback) { - Events.unsubscribe("processed", callback); - }, - setReaders: function(readers) { - setReaders(readers); - }, - registerResultCollector: function(resultCollector) { - if (resultCollector && typeof resultCollector.addResult === 'function') { - _resultCollector = resultCollector; - } - }, - canvas : _canvasContainer, - decodeSingle : function(config, resultCallback) { - config = HtmlUtils.mergeObjects({ - inputStream: { - type : "ImageStream", - sequence : false, - size: 800, - src: config.src - }, - numOfWorkers: 1, - locator: { - halfSample: false - } - }, config); - this.init(config, function() { - Events.once("processed", function(result) { - _stopped = true; - resultCallback.call(null, result); - }, true); - start(); - }); - }, - ImageWrapper: ImageWrapper, - ImageDebug: ImageDebug, - ResultCollector: ResultCollector - }; -}); - - return require('quagga'); -})); \ No newline at end of file +; +//# sourceMappingURL=quagga.map \ No newline at end of file diff --git a/dist/quagga.min.js b/dist/quagga.min.js index 75431ff6..94f1c1d9 100644 --- a/dist/quagga.min.js +++ b/dist/quagga.min.js @@ -1,10 +1,3 @@ -/*! quagga 2015-09-15 */ -!function(a,b){var c=b.toString();"undefined"!=typeof module?module.exports=b(c):a.Quagga=b(c)}(this,function(a){/** - * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/almond for details - */ -var b,c,d;return function(a){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(b,c){return function(){return n.apply(a,v.call(arguments,0).concat([b,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(b){if(e(r,b)){var c=r[b];delete r[b],t[b]=!0,m.apply(a,c)}if(!e(q,b)&&!e(t,b))throw new Error("No "+b);return q[b]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(b,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||b,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n1?f.size:Math.floor(b/e*f.size):b,d=f.size?b/e>1?Math.floor(e/b*f.size):f.size:e,j.x=c,j.y=d}var c,d,e={},f=null,g=["canrecord","ended"],h={},i={x:0,y:0},j={x:0,y:0};return e.getRealWidth=function(){return a.videoWidth},e.getRealHeight=function(){return a.videoHeight},e.getWidth=function(){return c},e.getHeight=function(){return d},e.setWidth=function(a){c=a},e.setHeight=function(a){d=a},e.setInputStream=function(b){f=b,a.src="undefined"!=typeof b.src?b.src:""},e.ended=function(){return a.ended},e.getConfig=function(){return f},e.setAttribute=function(b,c){a.setAttribute(b,c)},e.pause=function(){a.pause()},e.play=function(){a.play()},e.setCurrentTime=function(b){"LiveStream"!==f.type&&(a.currentTime=b)},e.addEventListener=function(b,c,d){-1!==g.indexOf(b)?(h[b]||(h[b]=[]),h[b].push(c)):a.addEventListener(b,c,d)},e.clearEventHandlers=function(){g.forEach(function(b){var c=h[b];c&&c.length>0&&c.forEach(function(c){a.removeEventListener(b,c)})})},e.trigger=function(a,c){var d,f=h[a];if("canrecord"===a&&b(),f&&f.length>0)for(d=0;d1?g.size:Math.floor(h/i*g.size):h,e=g.size?h/i>1?Math.floor(i/h*g.size):g.size:i,u.x=d,u.y=e,l=!0,j=0,setTimeout(function(){c("canrecord",[])},0)},o,n,g.sequence)}function c(a,b){var c,d=s[a];if(d&&d.length>0)for(c=0;cj?j++:setTimeout(function(){q=!0,c("ended",[])},0)),a):null},f},b}),glMatrixArrayType=Float32Array,"undefined"!=typeof window&&(window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a,b){window.setTimeout(a,1e3/60)}}(),navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia,window.URL=window.URL||window.webkitURL||window.mozURL||window.msURL),Math.imul=Math.imul||function(a,b){var c=a>>>16&65535,d=65535&a,e=b>>>16&65535,f=65535&b;return d*f+(c*f+d*e<<16>>>0)|0},d("typedefs",function(a){return function(){var b;return b||a.typedefs}}(this)),d("subImage",["typedefs"],function(){"use strict";function a(a,b,c){c||(c={data:null,size:b}),this.data=c.data,this.originalSize=c.size,this.I=c,this.from=a,this.size=b}return a.prototype.show=function(a,b){var c,d,e,f,g,h,i;for(b||(b=1),c=a.getContext("2d"),a.width=this.size.x,a.height=this.size.y,d=c.getImageData(0,0,a.width,a.height),e=d.data,f=0,g=0;gi?(f.cross(a,b,e),f.length(a)<1e-6&&f.cross(a,c,e),f.normalize(a,a),h.setAxisAngle(d,a,Math.PI),d):i>.999999?(d[0]=0,d[1]=0,d[2]=0,d[3]=1,d):(f.cross(a,e,g),d[0]=a[0],d[1]=a[1],d[2]=a[2],d[3]=1+i,h.normalize(d,d))}}(),h.setAxes=function(){var a=e.create();return function(b,c,d,e){return a[0]=d[0],a[3]=d[1],a[6]=d[2],a[1]=e[0],a[4]=e[1],a[7]=e[2],a[2]=-c[0],a[5]=-c[1],a[8]=-c[2],h.normalize(b,h.fromMat3(b,a))}}(),h.clone=g.clone,h.fromValues=g.fromValues,h.copy=g.copy,h.set=g.set,h.identity=function(a){return a[0]=0,a[1]=0,a[2]=0,a[3]=1,a},h.setAxisAngle=function(a,b,c){c=.5*c;var d=Math.sin(c);return a[0]=d*b[0],a[1]=d*b[1],a[2]=d*b[2],a[3]=Math.cos(c),a},h.add=g.add,h.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1],j=c[2],k=c[3];return a[0]=d*k+g*h+e*j-f*i,a[1]=e*k+g*i+f*h-d*j,a[2]=f*k+g*j+d*i-e*h,a[3]=g*k-d*h-e*i-f*j,a},h.mul=h.multiply,h.scale=g.scale,h.rotateX=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+g*h,a[1]=e*i+f*h,a[2]=f*i-e*h,a[3]=g*i-d*h,a},h.rotateY=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i-f*h,a[1]=e*i+g*h,a[2]=f*i+d*h,a[3]=g*i-e*h,a},h.rotateZ=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+e*h,a[1]=e*i-d*h,a[2]=f*i+g*h,a[3]=g*i-f*h,a},h.calculateW=function(a,b){var c=b[0],d=b[1],e=b[2];return a[0]=c,a[1]=d,a[2]=e,a[3]=Math.sqrt(Math.abs(1-c*c-d*d-e*e)),a},h.dot=g.dot,h.lerp=g.lerp,h.slerp=function(a,b,c,d){var e,f,g,h,i,j=b[0],k=b[1],l=b[2],m=b[3],n=c[0],o=c[1],p=c[2],q=c[3];return f=j*n+k*o+l*p+m*q,0>f&&(f=-f,n=-n,o=-o,p=-p,q=-q),1-f>1e-6?(e=Math.acos(f),g=Math.sin(e),h=Math.sin((1-d)*e)/g,i=Math.sin(d*e)/g):(h=1-d,i=d),a[0]=h*j+i*n,a[1]=h*k+i*o,a[2]=h*l+i*p,a[3]=h*m+i*q,a},h.sqlerp=function(){var a=h.create(),b=h.create();return function(c,d,e,f,g,i){return h.slerp(a,d,g,i),h.slerp(b,e,f,i),h.slerp(c,a,b,2*i*(1-i)),c}}(),h.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*c+d*d+e*e+f*f,h=g?1/g:0;return a[0]=-c*h,a[1]=-d*h,a[2]=-e*h,a[3]=f*h,a},h.conjugate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a[3]=b[3],a},h.length=g.length,h.len=h.length,h.squaredLength=g.squaredLength,h.sqrLen=h.squaredLength,h.normalize=g.normalize,h.fromMat3=function(a,b){var c,d=b[0]+b[4]+b[8];if(d>0)c=Math.sqrt(d+1),a[3]=.5*c,c=.5/c,a[0]=(b[5]-b[7])*c,a[1]=(b[6]-b[2])*c,a[2]=(b[1]-b[3])*c;else{var e=0;b[4]>b[0]&&(e=1),b[8]>b[3*e+e]&&(e=2);var f=(e+1)%3,g=(e+2)%3;c=Math.sqrt(b[3*e+e]-b[3*f+f]-b[3*g+g]+1),a[e]=.5*c,c=.5/c,a[3]=(b[3*f+g]-b[3*g+f])*c,a[f]=(b[3*f+e]+b[3*e+f])*c,a[g]=(b[3*g+e]+b[3*e+g])*c}return a},h.str=function(a){return"quat("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},a.exports=h},function(a,b,c){var d=c(1),e={};e.create=function(){var a=new d.ARRAY_TYPE(3);return a[0]=0,a[1]=0,a[2]=0,a},e.clone=function(a){var b=new d.ARRAY_TYPE(3);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b},e.fromValues=function(a,b,c){var e=new d.ARRAY_TYPE(3);return e[0]=a,e[1]=b,e[2]=c,e},e.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a},e.set=function(a,b,c,d){return a[0]=b,a[1]=c,a[2]=d,a},e.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a[2]=b[2]+c[2],a},e.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a[2]=b[2]-c[2],a},e.sub=e.subtract,e.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a[2]=b[2]*c[2],a},e.mul=e.multiply,e.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a[2]=b[2]/c[2],a},e.div=e.divide,e.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a[2]=Math.min(b[2],c[2]),a},e.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a[2]=Math.max(b[2],c[2]),a},e.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a[2]=b[2]*c,a},e.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a[2]=b[2]+c[2]*d,a},e.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2];return Math.sqrt(c*c+d*d+e*e)},e.dist=e.distance,e.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2];return c*c+d*d+e*e},e.sqrDist=e.squaredDistance,e.length=function(a){var b=a[0],c=a[1],d=a[2];return Math.sqrt(b*b+c*c+d*d)},e.len=e.length,e.squaredLength=function(a){var b=a[0],c=a[1],d=a[2];return b*b+c*c+d*d},e.sqrLen=e.squaredLength,e.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a},e.inverse=function(a,b){return a[0]=1/b[0],a[1]=1/b[1],a[2]=1/b[2],a},e.normalize=function(a,b){var c=b[0],d=b[1],e=b[2],f=c*c+d*d+e*e;return f>0&&(f=1/Math.sqrt(f),a[0]=b[0]*f,a[1]=b[1]*f,a[2]=b[2]*f),a},e.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]},e.cross=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2];return a[0]=e*i-f*h,a[1]=f*g-d*i,a[2]=d*h-e*g,a},e.lerp=function(a,b,c,d){var e=b[0],f=b[1],g=b[2]; -return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a[2]=g+d*(c[2]-g),a},e.hermite=function(a,b,c,d,e,f){var g=f*f,h=g*(2*f-3)+1,i=g*(f-2)+f,j=g*(f-1),k=g*(3-2*f);return a[0]=b[0]*h+c[0]*i+d[0]*j+e[0]*k,a[1]=b[1]*h+c[1]*i+d[1]*j+e[1]*k,a[2]=b[2]*h+c[2]*i+d[2]*j+e[2]*k,a},e.bezier=function(a,b,c,d,e,f){var g=1-f,h=g*g,i=f*f,j=h*g,k=3*f*h,l=3*i*g,m=i*f;return a[0]=b[0]*j+c[0]*k+d[0]*l+e[0]*m,a[1]=b[1]*j+c[1]*k+d[1]*l+e[1]*m,a[2]=b[2]*j+c[2]*k+d[2]*l+e[2]*m,a},e.random=function(a,b){b=b||1;var c=2*d.RANDOM()*Math.PI,e=2*d.RANDOM()-1,f=Math.sqrt(1-e*e)*b;return a[0]=Math.cos(c)*f,a[1]=Math.sin(c)*f,a[2]=e*b,a},e.transformMat4=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[3]*d+c[7]*e+c[11]*f+c[15];return g=g||1,a[0]=(c[0]*d+c[4]*e+c[8]*f+c[12])/g,a[1]=(c[1]*d+c[5]*e+c[9]*f+c[13])/g,a[2]=(c[2]*d+c[6]*e+c[10]*f+c[14])/g,a},e.transformMat3=function(a,b,c){var d=b[0],e=b[1],f=b[2];return a[0]=d*c[0]+e*c[3]+f*c[6],a[1]=d*c[1]+e*c[4]+f*c[7],a[2]=d*c[2]+e*c[5]+f*c[8],a},e.transformQuat=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2],j=c[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return a[0]=k*j+n*-g+l*-i-m*-h,a[1]=l*j+n*-h+m*-g-k*-i,a[2]=m*j+n*-i+k*-h-l*-g,a},e.rotateX=function(a,b,c,d){var e=[],f=[];return e[0]=b[0]-c[0],e[1]=b[1]-c[1],e[2]=b[2]-c[2],f[0]=e[0],f[1]=e[1]*Math.cos(d)-e[2]*Math.sin(d),f[2]=e[1]*Math.sin(d)+e[2]*Math.cos(d),a[0]=f[0]+c[0],a[1]=f[1]+c[1],a[2]=f[2]+c[2],a},e.rotateY=function(a,b,c,d){var e=[],f=[];return e[0]=b[0]-c[0],e[1]=b[1]-c[1],e[2]=b[2]-c[2],f[0]=e[2]*Math.sin(d)+e[0]*Math.cos(d),f[1]=e[1],f[2]=e[2]*Math.cos(d)-e[0]*Math.sin(d),a[0]=f[0]+c[0],a[1]=f[1]+c[1],a[2]=f[2]+c[2],a},e.rotateZ=function(a,b,c,d){var e=[],f=[];return e[0]=b[0]-c[0],e[1]=b[1]-c[1],e[2]=b[2]-c[2],f[0]=e[0]*Math.cos(d)-e[1]*Math.sin(d),f[1]=e[0]*Math.sin(d)+e[1]*Math.cos(d),f[2]=e[2],a[0]=f[0]+c[0],a[1]=f[1]+c[1],a[2]=f[2]+c[2],a},e.forEach=function(){var a=e.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=3),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],a[2]=b[h+2],f(a,a,g),b[h]=a[0],b[h+1]=a[1],b[h+2]=a[2];return b}}(),e.angle=function(a,b){var c=e.fromValues(a[0],a[1],a[2]),d=e.fromValues(b[0],b[1],b[2]);e.normalize(c,c),e.normalize(d,d);var f=e.dot(c,d);return f>1?0:Math.acos(f)},e.str=function(a){return"vec3("+a[0]+", "+a[1]+", "+a[2]+")"},a.exports=e},function(a,b,c){var d=c(1),e={};e.create=function(){var a=new d.ARRAY_TYPE(4);return a[0]=0,a[1]=0,a[2]=0,a[3]=0,a},e.clone=function(a){var b=new d.ARRAY_TYPE(4);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b},e.fromValues=function(a,b,c,e){var f=new d.ARRAY_TYPE(4);return f[0]=a,f[1]=b,f[2]=c,f[3]=e,f},e.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a},e.set=function(a,b,c,d,e){return a[0]=b,a[1]=c,a[2]=d,a[3]=e,a},e.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a[2]=b[2]+c[2],a[3]=b[3]+c[3],a},e.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a[2]=b[2]-c[2],a[3]=b[3]-c[3],a},e.sub=e.subtract,e.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a[2]=b[2]*c[2],a[3]=b[3]*c[3],a},e.mul=e.multiply,e.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a[2]=b[2]/c[2],a[3]=b[3]/c[3],a},e.div=e.divide,e.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a[2]=Math.min(b[2],c[2]),a[3]=Math.min(b[3],c[3]),a},e.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a[2]=Math.max(b[2],c[2]),a[3]=Math.max(b[3],c[3]),a},e.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a[2]=b[2]*c,a[3]=b[3]*c,a},e.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a[2]=b[2]+c[2]*d,a[3]=b[3]+c[3]*d,a},e.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2],f=b[3]-a[3];return Math.sqrt(c*c+d*d+e*e+f*f)},e.dist=e.distance,e.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2],f=b[3]-a[3];return c*c+d*d+e*e+f*f},e.sqrDist=e.squaredDistance,e.length=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return Math.sqrt(b*b+c*c+d*d+e*e)},e.len=e.length,e.squaredLength=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return b*b+c*c+d*d+e*e},e.sqrLen=e.squaredLength,e.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a[3]=-b[3],a},e.inverse=function(a,b){return a[0]=1/b[0],a[1]=1/b[1],a[2]=1/b[2],a[3]=1/b[3],a},e.normalize=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*c+d*d+e*e+f*f;return g>0&&(g=1/Math.sqrt(g),a[0]=c*g,a[1]=d*g,a[2]=e*g,a[3]=f*g),a},e.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]},e.lerp=function(a,b,c,d){var e=b[0],f=b[1],g=b[2],h=b[3];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a[2]=g+d*(c[2]-g),a[3]=h+d*(c[3]-h),a},e.random=function(a,b){return b=b||1,a[0]=d.RANDOM(),a[1]=d.RANDOM(),a[2]=d.RANDOM(),a[3]=d.RANDOM(),e.normalize(a,a),e.scale(a,a,b),a},e.transformMat4=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3];return a[0]=c[0]*d+c[4]*e+c[8]*f+c[12]*g,a[1]=c[1]*d+c[5]*e+c[9]*f+c[13]*g,a[2]=c[2]*d+c[6]*e+c[10]*f+c[14]*g,a[3]=c[3]*d+c[7]*e+c[11]*f+c[15]*g,a},e.transformQuat=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2],j=c[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return a[0]=k*j+n*-g+l*-i-m*-h,a[1]=l*j+n*-h+m*-g-k*-i,a[2]=m*j+n*-i+k*-h-l*-g,a[3]=b[3],a},e.forEach=function(){var a=e.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=4),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],a[2]=b[h+2],a[3]=b[h+3],f(a,a,g),b[h]=a[0],b[h+1]=a[1],b[h+2]=a[2],b[h+3]=a[3];return b}}(),e.str=function(a){return"vec4("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},a.exports=e},function(a,b,c){var d=c(1),e={};e.create=function(){var a=new d.ARRAY_TYPE(2);return a[0]=0,a[1]=0,a},e.clone=function(a){var b=new d.ARRAY_TYPE(2);return b[0]=a[0],b[1]=a[1],b},e.fromValues=function(a,b){var c=new d.ARRAY_TYPE(2);return c[0]=a,c[1]=b,c},e.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a},e.set=function(a,b,c){return a[0]=b,a[1]=c,a},e.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a},e.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a},e.sub=e.subtract,e.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a},e.mul=e.multiply,e.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a},e.div=e.divide,e.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a},e.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a},e.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a},e.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a},e.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1];return Math.sqrt(c*c+d*d)},e.dist=e.distance,e.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1];return c*c+d*d},e.sqrDist=e.squaredDistance,e.length=function(a){var b=a[0],c=a[1];return Math.sqrt(b*b+c*c)},e.len=e.length,e.squaredLength=function(a){var b=a[0],c=a[1];return b*b+c*c},e.sqrLen=e.squaredLength,e.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a},e.inverse=function(a,b){return a[0]=1/b[0],a[1]=1/b[1],a},e.normalize=function(a,b){var c=b[0],d=b[1],e=c*c+d*d;return e>0&&(e=1/Math.sqrt(e),a[0]=b[0]*e,a[1]=b[1]*e),a},e.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]},e.cross=function(a,b,c){var d=b[0]*c[1]-b[1]*c[0];return a[0]=a[1]=0,a[2]=d,a},e.lerp=function(a,b,c,d){var e=b[0],f=b[1];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a},e.random=function(a,b){b=b||1;var c=2*d.RANDOM()*Math.PI;return a[0]=Math.cos(c)*b,a[1]=Math.sin(c)*b,a},e.transformMat2=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[2]*e,a[1]=c[1]*d+c[3]*e,a},e.transformMat2d=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[2]*e+c[4],a[1]=c[1]*d+c[3]*e+c[5],a},e.transformMat3=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[3]*e+c[6],a[1]=c[1]*d+c[4]*e+c[7],a},e.transformMat4=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[4]*e+c[12],a[1]=c[1]*d+c[5]*e+c[13],a},e.forEach=function(){var a=e.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=2),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],f(a,a,g),b[h]=a[0],b[h+1]=a[1];return b}}(),e.str=function(a){return"vec2("+a[0]+", "+a[1]+")"},a.exports=e}])}),d("cluster",["gl-matrix"],function(a){"use strict";var b=a.vec2,c={create:function(a,c){function d(){e(a),f()}function e(a){i[a.id]=a,g.push(a)}function f(){var a,c=0;for(a=0;ac?!0:!1},getPoints:function(){return g},getCenter:function(){return h}}},createPoint:function(a,b,c){return{rad:a[c],point:a,id:b}}};return c}),d("array_helper",[],function(){"use strict";return{init:function(a,b){for(var c=a.length;c--;)a[c]=b},shuffle:function(a){var b,c,d=a.length-1;for(d;d>=0;d--)b=Math.floor(Math.random()*d),c=a[d],a[d]=a[b],a[b]=c;return a},toPointList:function(a){var b,c,d=[],e=[];for(b=0;b=b&&e.push(a[d]);return e},maxIndex:function(a){var b,c=0;for(b=0;ba[c]&&(c=b);return c},max:function(a){var b,c=0;for(b=0;bc&&(c=a[b]);return c},sum:function(a){for(var b=a.length,c=0;b--;)c+=a[b];return c}}}),d("cv_utils",["cluster","array_helper","gl-matrix"],function(a,b,c){"use strict";var d={},e=c.vec2,f=c.vec3;return d.imageRef=function(a,b){var c={x:a,y:b,toVec2:function(){return e.clone([this.x,this.y])},toVec3:function(){return f.clone([this.x,this.y,1])},round:function(){return this.x=this.x>0?Math.floor(this.x+.5):Math.floor(this.x-.5),this.y=this.y>0?Math.floor(this.y+.5):Math.floor(this.y-.5),this}};return c},d.computeIntegralImage2=function(a,b){var c,d,e=a.data,f=a.size.x,g=a.size.y,h=b.data,i=0,j=0,k=0,l=0,m=0;for(k=f,i=0,d=1;g>d;d++)i+=e[j],h[k]+=i,j+=f,k+=f;for(j=0,k=1,i=0,c=1;f>c;c++)i+=e[j],h[k]+=i,j++,k++;for(d=1;g>d;d++)for(j=d*f+1,k=(d-1)*f+1,l=d*f,m=(d-1)*f,c=1;f>c;c++)h[j]+=e[j]+h[k]+h[l]-h[m],j++,k++,l++,m++},d.computeIntegralImage=function(a,b){for(var c=a.data,d=a.size.x,e=a.size.y,f=b.data,g=0,h=0;d>h;h++)g+=c[h],f[h]=g;for(var i=1;e>i;i++){g=0;for(var j=0;d>j;j++)g+=c[i*d+j],f[i*d+j]=g+f[(i-1)*d+j]}},d.thresholdImage=function(a,b,c){c||(c=a);for(var d=a.data,e=d.length,f=c.data;e--;)f[e]=d[e]>e]++;return g},d.sharpenLine=function(a){var b,c,d=a.length,e=a[0],f=a[1];for(b=1;d-1>b;b++)c=a[b+1],a[b-1]=2*f-e-c&255,e=f,f=c;return a},d.determineOtsuThreshold=function(a,c){function e(a,b){var c,d=0;for(c=a;b>=c;c++)d+=h[c];return d}function f(a,b){var c,d=0;for(c=a;b>=c;c++)d+=c*h[c];return d}function g(){var g,i,j,k,l,m,n,o=[0],p=(1<k;k++)g=e(0,k),i=e(k+1,p),j=g*i,0===j&&(j=1),l=f(0,k)*i,m=f(k+1,p)*g,n=l-m,o[k]=n*n/j;return b.maxIndex(o)}c||(c=8);var h,i,j=8-c;return i=g(),i<=e;e++)for(f=0;n>f;f++)m[e*n+f]=0,m[(o-1-e)*n+f]=0;for(e=r;o-r>e;e++)for(f=0;r>=f;f++)m[e*n+f]=0,m[e*n+(n-1-f)]=0;for(e=r+1;o-r-1>e;e++)for(f=r+1;n-r>f;f++)g=p[(e-r-1)*n+(f-r-1)],h=p[(e-r-1)*n+(f+r)],i=p[(e+r)*n+(f-r-1)],j=p[(e+r)*n+(f+r)],q=j-i-h+g,k=q/s,m[e*n+f]=l[e*n+f]>k+5?0:1},d.cluster=function(b,c,d){function e(a){var b=!1;for(g=0;gb.x-j&&a.xb.y-k&&a.yd;d++){for(h=Math.floor(Math.random()*a.length),f=[],i=h,f.push(a[i]);null!==(i=c(i,!0));)f.push(a[i]);if(h>0)for(i=h;null!==(i=c(i,!1));)f.push(a[i]);f.length>g.length&&(g=f)}return g}},d.DILATE=1,d.ERODE=2,d.dilate=function(a,b){var c,d,e,f,g,h,i,j=a.data,k=b.data,l=a.size.y,m=a.size.x;for(c=1;l-1>c;c++)for(d=1;m-1>d;d++)f=c-1,g=c+1,h=d-1,i=d+1,e=j[f*m+h]+j[f*m+i]+j[c*m+d]+j[g*m+h]+j[g*m+i],k[c*m+d]=e>0?1:0},d.erode=function(a,b){var c,d,e,f,g,h,i,j=a.data,k=b.data,l=a.size.y,m=a.size.x;for(c=1;l-1>c;c++)for(d=1;m-1>d;d++)f=c-1,g=c+1,h=d-1,i=d+1,e=j[f*m+h]+j[f*m+i]+j[c*m+d]+j[g*m+h]+j[g*m+i],k[c*m+d]=5===e?1:0},d.subtract=function(a,b,c){c||(c=a);for(var d=a.data.length,e=a.data,f=b.data,g=c.data;d--;)g[d]=e[d]-f[d]},d.bitwiseOr=function(a,b,c){c||(c=a);for(var d=a.data.length,e=a.data,f=b.data,g=c.data;d--;)g[d]=e[d]||f[d]},d.countNonZero=function(a){for(var b=a.data.length,c=a.data,d=0;b--;)d+=c[b];return d},d.topGeneric=function(a,b,c){var d,e,f,g,h=0,i=0,j=[];for(d=0;b>d;d++)j[d]={score:0,item:null};for(d=0;di)for(f=j[h],f.score=e,f.item=a[d],i=Number.MAX_VALUE,g=0;b>g;g++)j[g].scoref;){for(d=0;h>d;d++)c[i]=Math.floor((.299*a[4*e+0]+.587*a[4*e+1]+.114*a[4*e+2]+(.299*a[4*(e+1)+0]+.587*a[4*(e+1)+1]+.114*a[4*(e+1)+2])+(.299*a[4*f+0]+.587*a[4*f+1]+.114*a[4*f+2])+(.299*a[4*(f+1)+0]+.587*a[4*(f+1)+1]+.114*a[4*(f+1)+2]))/4),i++,e+=2,f+=2;e+=j,f+=j}},d.computeGray=function(a,b,c){var d,e=a.length/4|0,f=c&&c.singleChannel===!0;if(f)for(d=0;e>d;d++)b[d]=a[4*d+0];else for(d=0;e>d;d++)b[d]=Math.floor(.299*a[4*d+0]+.587*a[4*d+1]+.114*a[4*d+2])},d.loadImageArray=function(a,b,c){c||(c=document.createElement("canvas"));var e=new Image;e.callback=b,e.onload=function(){c.width=this.width,c.height=this.height;var a=c.getContext("2d");a.drawImage(this,0,0);var b=new Uint8Array(this.width*this.height);a.drawImage(this,0,0);var e=a.getImageData(0,0,this.width,this.height).data;d.computeGray(e,b),this.callback(b,{x:this.width,y:this.height},this)},e.src=a},d.halfSample=function(a,b){for(var c=a.data,d=a.size.x,e=b.data,f=0,g=d,h=c.length,i=d/2,j=0;h>g;){for(var k=0;i>k;k++)e[j]=Math.floor((c[f]+c[f+1]+c[g]+c[g+1])/4),j++,f+=2,g+=2;f+=d,g+=d}},d.hsv2rgb=function(a,b){var c=a[0],d=a[1],e=a[2],f=e*d,g=f*(1-Math.abs(c/60%2-1)),h=e-f,i=0,j=0,k=0;return b=b||[0,0,0],60>c?(i=f,j=g):120>c?(i=g,j=f):180>c?(j=f,k=g):240>c?(j=g,k=f):300>c?(i=g,k=f):360>c&&(i=f,k=g),b[0]=255*(i+h)|0,b[1]=255*(j+h)|0,b[2]=255*(k+h)|0,b},d._computeDivisors=function(a){var b,c=[],d=[];for(b=1;bb[d]?d++:c++;return e},d.calculatePatchSize=function(a,b){function c(a){for(var b=0,c=a[Math.floor(a.length/2)];b0&&(c=Math.abs(a[b]-m)>Math.abs(a[b-1]-m)?a[b-1]:a[b]),m/ci[k-1]/i[k]?{x:c,y:c}:null}var d,e=this._computeDivisors(b.x),f=this._computeDivisors(b.y),g=Math.max(b.x,b.y),h=this._computeIntersection(e,f),i=[8,10,15,20,32,60,80],j={"x-small":5,small:4,medium:3,large:2,"x-large":1},k=j[a]||j.medium,l=i[k],m=Math.floor(g/l);return d=c(h),d||(d=c(this._computeDivisors(g)),d||(d=c(this._computeDivisors(m*l)))),d},d._parseCSSDimensionValues=function(a){var b={value:parseFloat(a),unit:(a.indexOf("%")===a.length-1,"%")};return b},d._dimensionsConverters={top:function(a,b){return"%"===a.unit?Math.floor(b.height*(a.value/100)):void 0},right:function(a,b){return"%"===a.unit?Math.floor(b.width-b.width*(a.value/100)):void 0},bottom:function(a,b){return"%"===a.unit?Math.floor(b.height-b.height*(a.value/100)):void 0},left:function(a,b){return"%"===a.unit?Math.floor(b.width*(a.value/100)):void 0}},d.computeImageArea=function(a,b,c){var e={width:a,height:b},f=Object.keys(c).reduce(function(a,b){var f=c[b],g=d._parseCSSDimensionValues(f),h=d._dimensionsConverters[b](g,e);return a[b]=h,a},{});return{sx:f.left,sy:f.top,sw:f.right-f.left,sh:f.bottom-f.top}},d}),d("image_wrapper",["subImage","cv_utils","array_helper","gl-matrix"],function(a,b,c,d){"use strict";function e(a,b,d,e){b?this.data=b:d?(this.data=new d(a.x*a.y),d===Array&&e&&c.init(this.data,0)):(this.data=new Uint8Array(a.x*a.y),Uint8Array===Array&&e&&c.init(this.data,0)),this.size=a}var f=d.vec2,g=d.mat2;return e.prototype.inImageWithBorder=function(a,b){return a.x>=b&&a.y>=b&&a.x=0&&u>=0&&n-1>v&&o-1>w){for(i=s,j=0;m>j;++j,f.add(i,y))for(k=0;l>k;++k,f.add(i,p))b.set(k,j,x(a,i[0],i[1]));return 0}var z=n-1,A=o-1,B=0;for(i=s,j=0;m>j;++j,f.add(i,y))for(k=0;l>k;++k,f.add(i,p))0<=i[0]&&0<=i[1]&&i[0]c;c++)for(d=0;e>d;d++)a.data[d*f+c]=this.data[(b.y+d)*this.size.x+b.x+c]},e.prototype.copyTo=function(a){for(var b=this.data.length,c=this.data,d=a.data;b--;)d[b]=c[b]},e.prototype.get=function(a,b){return this.data[b*this.size.x+a]},e.prototype.getSafe=function(a,b){var c;if(!this.indexMapping){for(this.indexMapping={x:[],y:[]},c=0;ca;a++)d[a]=d[(c-1)*b+a]=0;for(a=1;c-1>a;a++)d[a*b]=d[a*b+(b-1)]=0},e.prototype.invert=function(){for(var a=this.data,b=a.length;b--;)a[b]=a[b]?0:1},e.prototype.convolve=function(a){var b,c,d,e,f=a.length/2|0,g=0;for(c=0;c=e;e++)for(d=-f;f>=d;d++)g+=a[e+f][d+f]*this.getSafe(b+d,c+e);this.data[c*this.size.x+b]=g}},e.prototype.moments=function(a){var b,c,d,e,g,h,i,j,k,l,m,n,o=this.data,p=this.size.y,q=this.size.x,r=[],s=[],t=Math.PI,u=t/4;if(0>=a)return s;for(g=0;a>g;g++)r[g]={m00:0,m01:0,m10:0,m11:0,m02:0,m20:0,theta:0,rad:0};for(c=0;p>c;c++)for(e=c*c,b=0;q>b;b++)d=o[c*q+b],d>0&&(h=r[d-1],h.m00+=1,h.m01+=c,h.m10+=b,h.m11+=b*c,h.m02+=e,h.m20+=b*b);for(g=0;a>g;g++)h=r[g],isNaN(h.m00)||0===h.m00||(l=h.m10/h.m00,m=h.m01/h.m00,i=h.m11/h.m00-l*m,j=h.m02/h.m00-m*m,k=h.m20/h.m00-l*l,n=(j-k)/(2*i),n=.5*Math.atan(n)+(i>=0?u:-u)+t,h.theta=(180*n/t+90)%180-90,h.theta<0&&(h.theta+=180),h.rad=n>t?n-t:n,h.vec=f.clone([Math.cos(n),Math.sin(n)]),s.push(h));return s},e.prototype.show=function(a,b){var c,d,e,f,g,h,i;for(b||(b=1),c=a.getContext("2d"),a.width=this.size.x,a.height=this.size.y,d=c.getImageData(0,0,a.width,a.height),e=d.data,f=0,i=0;ic||c>360)&&(c=360);for(var e=[0,1,1],f=[0,0,0],g=[255,255,255],h=[0,0,0],i=[],j=a.getContext("2d"),k=j.getImageData(d.x,d.y,this.size.x,this.size.y),l=k.data,m=this.data.length;m--;)e[0]=this.data[m]*c,i=e[0]<=0?g:e[0]>=360?h:b.hsv2rgb(e,f),l[4*m+0]=i[0],l[4*m+1]=i[1],l[4*m+2]=i[2],l[4*m+3]=255;j.putImageData(k,d.x,d.y)},e}),d("tracer",[],function(){"use strict";var a={searchDirections:[[0,1],[1,1],[1,0],[1,-1],[0,-1],[-1,-1],[-1,0],[-1,1]],create:function(a,b){function c(a,b,c,d){var e,k,l;for(e=0;7>e;e++){if(k=a.cy+i[a.dir][0],l=a.cx+i[a.dir][1],f=k*j+l,g[f]===b&&(0===h[f]||h[f]===c))return h[f]=c,a.cy=k,a.cx=l,!0;0===h[f]&&(h[f]=d),a.dir=(a.dir+1)%8}return!1}function d(a,b,c){return{dir:c,x:a,y:b,next:null,prev:null}}function e(a,b,e,f,g){var h,i,j,k=null,l={cx:b,cy:a,dir:0};if(c(l,f,e,g)){k=d(b,a,l.dir),h=k,j=l.dir,i=d(l.cx,l.cy,0),i.prev=h,h.next=i,i.next=null,h=i;do l.dir=(l.dir+6)%8,c(l,f,e,g),j!=l.dir?(h.dir=l.dir,i=d(l.cx,l.cy,0),i.prev=h,h.next=i,i.next=null,h=i):(h.dir=j,h.x=l.cx,h.y=l.cy),j=l.dir;while(l.cx!=b||l.cy!=a);k.prev=h.prev,h.prev.next=k}return k}var f,g=a.data,h=b.data,i=this.searchDirections,j=a.size.x;return{trace:function(a,b,d,e){return c(a,b,d,e)},contourTracing:function(a,b,c,d,f){return e(a,b,c,d,f)}}}};return a}),d("rasterizer",["tracer"],function(a){"use strict";var b={createContour2D:function(){return{dir:null,index:null,firstVertex:null,insideContours:null,nextpeer:null,prevpeer:null}},CONTOUR_DIR:{CW_DIR:0,CCW_DIR:1,UNKNOWN_DIR:2},DIR:{OUTSIDE_EDGE:-32767,INSIDE_EDGE:-32766},create:function(c,d){var e=c.data,f=d.data,g=c.size.x,h=c.size.y,i=a.create(c,d);return{rasterize:function(a){var c,d,j,k,l,m,n,o,p,q,r,s,t=[],u=0;for(s=0;400>s;s++)t[s]=0;for(t[0]=e[0],p=null,m=1;h-1>m;m++)for(k=0,d=t[0],l=1;g-1>l;l++)if(r=m*g+l,0===f[r])if(c=e[r],c!==d){if(0===k)j=u+1,t[j]=c,d=c,n=i.contourTracing(m,l,j,c,b.DIR.OUTSIDE_EDGE),null!==n&&(u++,k=j,o=b.createContour2D(),o.dir=b.CONTOUR_DIR.CW_DIR,o.index=k,o.firstVertex=n,o.nextpeer=p,o.insideContours=null,null!==p&&(p.prevpeer=o),p=o);else if(n=i.contourTracing(m,l,b.DIR.INSIDE_EDGE,c,k),null!==n){for(o=b.createContour2D(),o.firstVertex=n,o.insideContours=null,0===a?o.dir=b.CONTOUR_DIR.CCW_DIR:o.dir=b.CONTOUR_DIR.CW_DIR,o.index=a,q=p;null!==q&&q.index!==k;)q=q.nextpeer;null!==q&&(o.nextpeer=q.insideContours,null!==q.insideContours&&(q.insideContours.prevpeer=o),q.insideContours=o)}}else f[r]=k;else f[r]===b.DIR.OUTSIDE_EDGE||f[r]===b.DIR.INSIDE_EDGE?(k=0,d=f[r]===b.DIR.INSIDE_EDGE?e[r]:t[0]):(k=f[r],d=t[k]);for(q=p;null!==q;)q.index=a,q=q.nextpeer;return{cc:p,count:u}},debug:{drawContour:function(a,c){var d,e,f,g=a.getContext("2d"),h=c;for(g.strokeStyle="red",g.fillStyle="red",g.lineWidth=1,d=null!==h?h.insideContours:null;null!==h;){switch(null!==d?(e=d,d=d.nextpeer):(e=h,h=h.nextpeer,d=null!==h?h.insideContours:null),e.dir){case b.CONTOUR_DIR.CW_DIR:g.strokeStyle="red";break;case b.CONTOUR_DIR.CCW_DIR:g.strokeStyle="blue";break;case b.CONTOUR_DIR.UNKNOWN_DIR:g.strokeStyle="green"}f=e.firstVertex,g.beginPath(),g.moveTo(f.x,f.y);do f=f.next,g.lineTo(f.x,f.y);while(f!==e.firstVertex);g.stroke()}}}}}};return b}),d("skeletonizer",[],function(){"use strict";function a(stdlib, foreign, buffer) {"use asm";var images=new stdlib.Uint8Array(buffer),size=foreign.size|0,imul=stdlib.Math.imul;function erode(inImagePtr, outImagePtr) {inImagePtr=inImagePtr|0;outImagePtr=outImagePtr|0;var v=0,u=0,sum=0,yStart1=0,yStart2=0,xStart1=0,xStart2=0,offset=0;for ( v=1; (v|0)<((size - 1)|0); v=(v+1)|0) {offset=(offset+size)|0;for ( u=1; (u|0)<((size - 1)|0); u=(u+1)|0) {yStart1=(offset - size)|0;yStart2=(offset+size)|0;xStart1=(u - 1)|0;xStart2=(u+1)|0;sum=((images[(inImagePtr+yStart1+xStart1)|0]|0)+(images[(inImagePtr+yStart1+xStart2)|0]|0)+(images[(inImagePtr+offset+u)|0]|0)+(images[(inImagePtr+yStart2+xStart1)|0]|0)+(images[(inImagePtr+yStart2+xStart2)|0]|0))|0;if ((sum|0) == (5|0)) {images[(outImagePtr+offset+u)|0]=1;} else {images[(outImagePtr+offset+u)|0]=0;}}}return;}function subtract(aImagePtr, bImagePtr, outImagePtr) {aImagePtr=aImagePtr|0;bImagePtr=bImagePtr|0;outImagePtr=outImagePtr|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=(length - 1)|0;images[(outImagePtr+length)|0]=((images[(aImagePtr+length)|0]|0) - (images[(bImagePtr+length)|0]|0))|0;}}function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) {aImagePtr=aImagePtr|0;bImagePtr=bImagePtr|0;outImagePtr=outImagePtr|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=(length - 1)|0;images[(outImagePtr+length)|0]=((images[(aImagePtr+length)|0]|0)|(images[(bImagePtr+length)|0]|0))|0;}}function countNonZero(imagePtr) {imagePtr=imagePtr|0;var sum=0,length=0;length=imul(size, size)|0;while ((length|0)>0) {length=(length - 1)|0;sum=((sum|0)+(images[(imagePtr+length)|0]|0))|0;}return (sum|0);}function init(imagePtr, value) {imagePtr=imagePtr|0;value=value|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=(length - 1)|0;images[(imagePtr+length)|0]=value;}}function dilate(inImagePtr, outImagePtr) {inImagePtr=inImagePtr|0;outImagePtr=outImagePtr|0;var v=0,u=0,sum=0,yStart1=0,yStart2=0,xStart1=0,xStart2=0,offset=0;for ( v=1; (v|0)<((size - 1)|0); v=(v+1)|0) {offset=(offset+size)|0;for ( u=1; (u|0)<((size - 1)|0); u=(u+1)|0) {yStart1=(offset - size)|0;yStart2=(offset+size)|0;xStart1=(u - 1)|0;xStart2=(u+1)|0;sum=((images[(inImagePtr+yStart1+xStart1)|0]|0)+(images[(inImagePtr+yStart1+xStart2)|0]|0)+(images[(inImagePtr+offset+u)|0]|0)+(images[(inImagePtr+yStart2+xStart1)|0]|0)+(images[(inImagePtr+yStart2+xStart2)|0]|0))|0;if ((sum|0)>(0|0)) {images[(outImagePtr+offset+u)|0]=1;} else {images[(outImagePtr+offset+u)|0]=0;}}}return;}function memcpy(srcImagePtr, dstImagePtr) {srcImagePtr=srcImagePtr|0;dstImagePtr=dstImagePtr|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=(length - 1)|0;images[(dstImagePtr+length)|0]=(images[(srcImagePtr+length)|0]|0);}}function zeroBorder(imagePtr) {imagePtr=imagePtr|0;var x=0,y=0;for ( x=0; (x|0)<((size - 1)|0); x=(x+1)|0) {images[(imagePtr+x)|0]=0;images[(imagePtr+y)|0]=0;y=((y+size) - 1)|0;images[(imagePtr+y)|0]=0;y=(y+1)|0;}for ( x=0; (x|0)<(size|0); x=(x+1)|0) {images[(imagePtr+y)|0]=0;y=(y+1)|0;}}function skeletonize() {var subImagePtr=0,erodedImagePtr=0,tempImagePtr=0,skelImagePtr=0,sum=0,done=0;erodedImagePtr=imul(size, size)|0;tempImagePtr=(erodedImagePtr+erodedImagePtr)|0;skelImagePtr=(tempImagePtr+erodedImagePtr)|0;init(skelImagePtr, 0);zeroBorder(subImagePtr);do {erode(subImagePtr, erodedImagePtr);dilate(erodedImagePtr, tempImagePtr);subtract(subImagePtr, tempImagePtr, tempImagePtr);bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr);memcpy(erodedImagePtr, subImagePtr);sum=countNonZero(subImagePtr)|0;done=((sum|0) == 0|0);} while(!done);}return {skeletonize : skeletonize};} -return a}),d("image_debug",[],function(){"use strict";return{drawRect:function(a,b,c,d){c.strokeStyle=d.color,c.fillStyle=d.color,c.lineWidth=1,c.beginPath(),c.strokeRect(a.x,a.y,b.x,b.y)},drawPath:function(a,b,c,d){c.strokeStyle=d.color,c.fillStyle=d.color,c.lineWidth=d.lineWidth,c.beginPath(),c.moveTo(a[0][b.x],a[0][b.y]);for(var e=1;eb&&(b+=180),b=(180-b)*Math.PI/180,f=I.clone([Math.cos(b),Math.sin(b),-Math.sin(b),Math.cos(b)]),c=0;cd;d++)H.transformMat2(e.box[d],e.box[d],f);t.boxFromPatches.showTransformed&&g.drawPath(e.box,{x:0,y:1},F.ctx.binary,{color:"#99ff00",lineWidth:2})}for(c=0;cd;d++)e.box[d][0]l&&(l=e.box[d][0]),e.box[d][1]m&&(m=e.box[d][1]);for(h=[[j,k],[l,k],[l,m],[j,m]],t.boxFromPatches.showTransformedBox&&g.drawPath(h,{x:0,y:1},F.ctx.binary,{color:"#ff0000",lineWidth:2}),i=t.halfSample?2:1,f=I.invert(f,f),d=0;4>d;d++)H.transformMat2(h[d],h[d],f);for(t.boxFromPatches.showBB&&g.drawPath(h,{x:0,y:1},F.ctx.binary,{color:"#ff0000",lineWidth:2}),d=0;4>d;d++)H.scale(h[d],h[d],i);return h}function l(){b.otsuThreshold(u,B),B.zeroBorder(),t.showCanvas&&B.show(F.dom.binary,255)}function m(){var a,b,d,e,h,i,j,k,l=[];for(a=0;ab;b++)d.push(0);for(c=z.data.length;c--;)z.data[c]>0&&d[z.data[c]-1]++;return d=d.map(function(a,b){return{val:a,label:b+1}}),d.sort(function(a,b){return b.val-a.val}),e=d.filter(function(a){return a.val>=5})}function o(a,c){var d,e,f,h,i,j=[],l=[],m=[0,1,1],n=[0,0,0];for(d=0;d=2){for(e=0;el&&j.push(a[e]);if(j.length>=2){for(i=j.length,g=p(j),f=0,e=0;e1&&g.length>=j.length/4*3&&g.length>a.length/4&&(f/=g.length,h={index:b[1]*G.x+b[0],pos:{x:c,y:d},box:[H.clone([c,d]),H.clone([c+w.size.x,d]),H.clone([c+w.size.x,d+w.size.y]),H.clone([c,d+w.size.y])],moments:g,rad:f,vec:H.clone([Math.cos(f),Math.sin(f)])},k.push(h))}}return k}function s(a){function c(){var a;for(a=0;ak&&e(h))):z.data[h]=Number.MAX_VALUE}var h,i,j=0,k=.95,l=0,m=[0,1,1],n=[0,0,0];for(f.init(y.data,0),f.init(z.data,0),f.init(A.data,null),h=0;h0&&z.data[h]<=j&&(i=A.data[h],m[0]=z.data[h]/(j+1)*360,b.hsv2rgb(m,n),g.drawRect(i.pos,w.size,F.ctx.binary,{color:"rgb("+n.join(",")+")",lineWidth:2}));return j}var t,u,v,w,x,y,z,A,B,C,D,E,F={ctx:{binary:null},dom:{binary:null}},G={x:0,y:0},H=h.vec2,I=h.mat2,J=this;return{init:function(a,b){t=b,D=a,i(),j()},locate:function(){var a,c,d;if(t.halfSample&&b.halfSample(D,u),l(),a=m(),a.lengthe?null:(c=n(e),0===c.length?null:d=o(c,e))},checkImageConstraints:function(a,c){var d,e,f,g=a.getWidth(),h=a.getHeight(),i=c.halfSample?.5:1;if(a.getConfig().area&&(f=b.computeImageArea(g,h,a.getConfig().area),a.setTopRight({x:f.sx,y:f.sy}),a.setCanvasSize({x:g,y:h}),g=f.sw,h=f.sh),e={x:Math.floor(g*i),y:Math.floor(h*i)},d=b.calculatePatchSize(c.patchSize,e),console.log("Patch-Size: "+JSON.stringify(d)),a.setWidth(Math.floor(Math.floor(e.x/d.x)*(1/i)*d.x)),a.setHeight(Math.floor(Math.floor(e.y/d.y)*(1/i)*d.y)),a.getWidth()%d.x===0&&a.getHeight()%d.y===0)return!0;throw new Error("Image dimensions do not comply with the current settings: Width ("+g+" )and height ("+h+") must a multiple of "+d.x)}}}),d("bresenham",["cv_utils","image_wrapper"],function(a,b){"use strict";var c={},d={DIR:{UP:1,DOWN:-1}};return c.getBarcodeLine=function(a,b,c){function d(a,b){l=s[b*t+a],u+=l,v=v>l?l:v,w=l>w?l:w,r.push(l)}var e,f,g,h,i,j,k,l,m=0|b.x,n=0|b.y,o=0|c.x,p=0|c.y,q=Math.abs(p-n)>Math.abs(o-m),r=[],s=a.data,t=a.size.x,u=0,v=255,w=0;for(q&&(j=m,m=n,n=j,j=o,o=p,p=j),m>o&&(j=m,m=o,o=j,j=n,n=p,p=j),e=o-m,f=Math.abs(p-n),g=e/2|0,i=n,h=p>n?1:-1,k=m;o>k;k++)q?d(i,k):d(k,i),g-=f,0>g&&(i+=h,g+=e);return{line:r,min:v,max:w}},c.toOtsuBinaryLine=function(c){var d=c.line,e=new b({x:d.length-1,y:1},d),f=a.determineOtsuThreshold(e,5);return d=a.sharpenLine(d),a.thresholdImage(e,f),{line:d,threshold:f}},c.toBinaryLine=function(a){var b,c,e,f,g,h,i=a.min,j=a.max,k=a.line,l=i+(j-i)/2,m=[],n=(j-i)/12,o=-n;for(e=k[0]>l?d.DIR.UP:d.DIR.DOWN,m.push({pos:0,val:k[0]}),g=0;gb+c&&k[g+1]<1.5*l?d.DIR.DOWN:b+c>n&&k[g+1]>.5*l?d.DIR.UP:e,e!==f&&(m.push({pos:g,val:k[g]}),e=f);for(m.push({pos:k.length,val:k[k.length-1]}),h=m[0].pos;hl?0:1;for(g=1;gm[g].val?m[g].val+(m[g+1].val-m[g].val)/3*2|0:m[g+1].val+(m[g].val-m[g+1].val)/3|0,h=m[g].pos;hn?0:1;return{line:k,threshold:n}},c.debug={printFrequency:function(a,b){var c,d=b.getContext("2d");for(b.width=a.length,b.height=256,d.beginPath(),d.strokeStyle="blue",c=0;cg)return Number.MAX_VALUE;d+=e}return d/f},a.prototype._nextSet=function(a,b){var c;for(b=b||0,c=b;c1)for(c=0;cd?(j.start=c-g,j.end=c,j.counter=e,j):null;i++,e[i]=1,h=!h}}else for(e.push(0),c=g;ca?0:a,d=a;b>d;d++)if(this._row[d]!==c)return!1;return!0},a.prototype._fillCounters=function(a,b,c){var d,e=this,f=0,g=[];for(c="undefined"!=typeof c?c:!0,a="undefined"!=typeof a?a:e._nextUnset(e._row),b=b||e._row.length,g[f]=0,d=a;b>d;d++)e._row[d]^c?g[f]++:(f++,g[f]=1,c=!c);return g},Object.defineProperty(a.prototype,"FORMAT",{value:"unknown",writeable:!1}),a.DIRECTION={FORWARD:1,REVERSE:-1},a.Exception={StartNotFoundException:"Start-Info was not found!",CodeNotFoundException:"Code could not be found!",PatternNotFoundException:"Pattern could not be found!"},a.CONFIG_KEYS={},a}),d("code_128_reader",["./barcode_reader"],function(a){"use strict";function b(){a.call(this)}var c={CODE_SHIFT:{value:98},CODE_C:{value:99},CODE_B:{value:100},CODE_A:{value:101},START_CODE_A:{value:103},START_CODE_B:{value:104},START_CODE_C:{value:105},STOP_CODE:{value:106},MODULO:{value:11},CODE_PATTERN:{value:[[2,1,2,2,2,2],[2,2,2,1,2,2],[2,2,2,2,2,1],[1,2,1,2,2,3],[1,2,1,3,2,2],[1,3,1,2,2,2],[1,2,2,2,1,3],[1,2,2,3,1,2],[1,3,2,2,1,2],[2,2,1,2,1,3],[2,2,1,3,1,2],[2,3,1,2,1,2],[1,1,2,2,3,2],[1,2,2,1,3,2],[1,2,2,2,3,1],[1,1,3,2,2,2],[1,2,3,1,2,2],[1,2,3,2,2,1],[2,2,3,2,1,1],[2,2,1,1,3,2],[2,2,1,2,3,1],[2,1,3,2,1,2],[2,2,3,1,1,2],[3,1,2,1,3,1],[3,1,1,2,2,2],[3,2,1,1,2,2],[3,2,1,2,2,1],[3,1,2,2,1,2],[3,2,2,1,1,2],[3,2,2,2,1,1],[2,1,2,1,2,3],[2,1,2,3,2,1],[2,3,2,1,2,1],[1,1,1,3,2,3],[1,3,1,1,2,3],[1,3,1,3,2,1],[1,1,2,3,1,3],[1,3,2,1,1,3],[1,3,2,3,1,1],[2,1,1,3,1,3],[2,3,1,1,1,3],[2,3,1,3,1,1],[1,1,2,1,3,3],[1,1,2,3,3,1],[1,3,2,1,3,1],[1,1,3,1,2,3],[1,1,3,3,2,1],[1,3,3,1,2,1],[3,1,3,1,2,1],[2,1,1,3,3,1],[2,3,1,1,3,1],[2,1,3,1,1,3],[2,1,3,3,1,1],[2,1,3,1,3,1],[3,1,1,1,2,3],[3,1,1,3,2,1],[3,3,1,1,2,1],[3,1,2,1,1,3],[3,1,2,3,1,1],[3,3,2,1,1,1],[3,1,4,1,1,1],[2,2,1,4,1,1],[4,3,1,1,1,1],[1,1,1,2,2,4],[1,1,1,4,2,2],[1,2,1,1,2,4],[1,2,1,4,2,1],[1,4,1,1,2,2],[1,4,1,2,2,1],[1,1,2,2,1,4],[1,1,2,4,1,2],[1,2,2,1,1,4],[1,2,2,4,1,1],[1,4,2,1,1,2],[1,4,2,2,1,1],[2,4,1,2,1,1],[2,2,1,1,1,4],[4,1,3,1,1,1],[2,4,1,1,1,2],[1,3,4,1,1,1],[1,1,1,2,4,2],[1,2,1,1,4,2],[1,2,1,2,4,1],[1,1,4,2,1,2],[1,2,4,1,1,2],[1,2,4,2,1,1],[4,1,1,2,1,2],[4,2,1,1,1,2],[4,2,1,2,1,1],[2,1,2,1,4,1],[2,1,4,1,2,1],[4,1,2,1,2,1],[1,1,1,1,4,3],[1,1,1,3,4,1],[1,3,1,1,4,1],[1,1,4,1,1,3],[1,1,4,3,1,1],[4,1,1,1,1,3],[4,1,1,3,1,1],[1,1,3,1,4,1],[1,1,4,1,3,1],[3,1,1,1,4,1],[4,1,1,1,3,1],[2,1,1,4,1,2],[2,1,1,2,1,4],[2,1,1,2,3,2],[2,3,3,1,1,1,2]]},SINGLE_CODE_ERROR:{value:1},AVG_CODE_ERROR:{value:.5},FORMAT:{value:"code_128",writeable:!1}};return b.prototype=Object.create(a.prototype,c),b.prototype.constructor=b,b.prototype._decodeCode=function(a){var b,c,d,e,f=[0,0,0,0,0,0],g=this,h=a,i=!g._row[h],j=0,k={error:Number.MAX_VALUE,code:-1,start:a,end:a};for(b=h;bd;d++)g[d]=g[d+2];g[4]=0,g[5]=0,k--}else k++;g[k]=1,j=!j}return null},b.prototype._decode=function(){var a,b,c,d=this,e=d._findStart(),f=null,g=!1,h=[],i=0,j=0,k=[],l=[],m=!1;if(null===e)return null;switch(f={code:e.code,start:e.start,end:e.end},l.push(f),j=f.code,f.code){case d.START_CODE_A:a=d.CODE_A;break;case d.START_CODE_B:a=d.CODE_B;break;case d.START_CODE_C:a=d.CODE_C;break;default:return null}for(;!g;){if(b=m,m=!1,f=d._decodeCode(f.end),null!==f)switch(f.code!==d.STOP_CODE&&(k.push(f.code),i++,j+=i*f.code),l.push(f),a){case d.CODE_A:if(f.code<64)h.push(String.fromCharCode(32+f.code));else if(f.code<96)h.push(String.fromCharCode(f.code-64));else switch(f.code){case d.CODE_SHIFT:m=!0,a=d.CODE_B;break;case d.CODE_B:a=d.CODE_B;break;case d.CODE_C:a=d.CODE_C;break;case d.STOP_CODE:g=!0}break;case d.CODE_B:if(f.code<96)h.push(String.fromCharCode(32+f.code));else switch(f.code!=d.STOP_CODE&&(c=!1),f.code){case d.CODE_SHIFT:m=!0,a=d.CODE_A;break;case d.CODE_A:a=d.CODE_A;break;case d.CODE_C:a=d.CODE_C;break;case d.STOP_CODE:g=!0}break;case d.CODE_C:switch(f.code<100&&h.push(f.code<10?"0"+f.code:f.code),f.code){case d.CODE_A:a=d.CODE_A;break;case d.CODE_B:a=d.CODE_B;break;case d.STOP_CODE:g=!0}}else g=!0;b&&(a=a==d.CODE_A?d.CODE_B:d.CODE_A)}return null===f?null:(f.end=d._nextUnset(d._row,f.end),d._verifyTrailingWhitespace(f)?(j-=i*k[k.length-1],j%103!=k[k.length-1]?null:h.length?(h.splice(h.length-1,1),{code:h.join(""),start:e.start,end:f.end,codeset:a,startInfo:e,decodedCodes:l,endInfo:f}):null):null)},a.prototype._verifyTrailingWhitespace=function(a){var b,c=this;return b=a.end+(a.end-a.start)/2,bd;d++)e=h._matchPattern(f,h.CODE_PATTERN[d]),eh.AVG_CODE_ERROR?null:l}}else k++;g[k]=1,j=!j}return null},b.prototype._findPattern=function(a,b,c,d,e){var f,g,h,i,j,k=[],l=this,m=0,n={error:Number.MAX_VALUE,code:-1,start:0,end:0};for(b||(b=l._nextSet(l._row)),void 0===c&&(c=!1),void 0===d&&(d=!0),void 0===e&&(e=l.AVG_CODE_ERROR),f=0;fg))return n.error=g,n.start=f-i,n.end=f,n;if(!d)return null;for(h=0;h=0&&c._matchRange(a,b.start,0))return b;d=b.end,b=null}},b.prototype._verifyTrailingWhitespace=function(a){var b,c=this;return b=a.end+(a.end-a.start),bd;d++){if(a=f._decodeCode(a.end),!a)return null;a.code>=f.CODE_G_START?(a.code=a.code-f.CODE_G_START,g|=1<<5-d):g|=0<<5-d,b.push(a.code),c.push(a)}if(e=f._calculateFirstDigit(g),null===e)return null;if(b.unshift(e),a=f._findPattern(f.MIDDLE_PATTERN,a.end,!0,!1),null===a)return null;for(c.push(a),d=0;6>d;d++){if(a=f._decodeCode(a.end,f.CODE_G_START),!a)return null;c.push(a),b.push(a.code)}return a},b.prototype._decode=function(){var a,b,c=this,d=[],e=[];return(a=c._findStart())?(b={code:a.code,start:a.start,end:a.end},e.push(b),(b=c._decodePayload(b,d,e))&&(b=c._findEnd(b.end,!1))?(e.push(b),c._checksum(d)?{code:d.join(""),start:a.start,end:b.end,codeset:"",startInfo:a,decodedCodes:e}:null):null):null},b.prototype._checksum=function(a){var b,c=0;for(b=a.length-2;b>=0;b-=2)c+=a[b];for(c*=3,b=a.length-1;b>=0;b-=2)c+=a[b];return c%10===0},b}),d("code_39_reader",["./barcode_reader","./array_helper"],function(a,b){"use strict";function c(){a.call(this)}var d={ALPHABETH_STRING:{value:"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"},ALPHABET:{value:[48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,45,46,32,42,36,47,43,37]},CHARACTER_ENCODINGS:{value:[52,289,97,352,49,304,112,37,292,100,265,73,328,25,280,88,13,268,76,28,259,67,322,19,274,82,7,262,70,22,385,193,448,145,400,208,133,388,196,148,168,162,138,42]},ASTERISK:{value:148},FORMAT:{value:"code_39",writeable:!1}};return c.prototype=Object.create(a.prototype,d),c.prototype.constructor=c,c.prototype._toCounters=function(a,c){var d,e=this,f=c.length,g=e._row.length,h=!e._row[a],i=0;for(b.init(c,0),d=a;g>d;d++)if(e._row[d]^h)c[i]++;else{if(i++,i===f)break;c[i]=1,h=!h}return c},c.prototype._decode=function(){var a,c,d,e,f=this,g=[0,0,0,0,0,0,0,0,0],h=[],i=f._findStart();if(!i)return null;e=f._nextSet(f._row,i.end);do{if(g=f._toCounters(e,g),d=f._toPattern(g),0>d)return null;if(a=f._patternToChar(d),0>a)return null;h.push(a),c=e,e+=b.sum(g),e=f._nextSet(f._row,e)}while("*"!==a);return h.pop(),h.length&&f._verifyTrailingWhitespace(c,e,g)?{code:h.join(""),start:i.start,end:e,startInfo:i,decodedCodes:h}:null},c.prototype._verifyTrailingWhitespace=function(a,c,d){var e,f=b.sum(d);return e=c-a-f,3*e>=f?!0:!1},c.prototype._patternToChar=function(a){var b,c=this;for(b=0;bb&&(d=a[c]);return d},c.prototype._toPattern=function(a){for(var b,c,d=a.length,e=0,f=d,g=0,h=this;f>3;){for(e=h._findNextWidth(a,e),f=0,b=0,c=0;d>c;c++)a[c]>e&&(b|=1<c&&f>0;c++)if(a[c]>e&&(f--,2*a[c]>=g))return-1;return b}}return-1},c.prototype._findStart=function(){var a,b,c,d=this,e=d._nextSet(d._row),f=e,g=[0,0,0,0,0,0,0,0,0],h=0,i=!1;for(a=e;ab;b++)g[b]=g[b+2];g[7]=0,g[8]=0,h--}else h++;g[h]=1,i=!i}return null},c}),d("code_39_vin_reader",["./code_39_reader"],function(a){"use strict";function b(){a.call(this)}var c={IOQ:/[IOQ]/g,AZ09:/[A-Z0-9]{17}/};return b.prototype=Object.create(a.prototype),b.prototype.constructor=b,b.prototype._decode=function(){var b=a.prototype._decode.apply(this);if(!b)return null;var d=b.code;if(d)return d=d.replace(c.IOQ,""),d.match(c.AZ09)?this._checkChecksum(d)?(b.code=d,b):null:(console.log("Failed AZ09 pattern code:",d),null)},b.prototype._checkChecksum=function(a){return!!a},b}),d("codabar_reader",["./barcode_reader"],function(a){"use strict";function b(){a.call(this),this._counters=[]}var c={ALPHABETH_STRING:{value:"0123456789-$:/.+ABCD"},ALPHABET:{value:[48,49,50,51,52,53,54,55,56,57,45,36,58,47,46,43,65,66,67,68]},CHARACTER_ENCODINGS:{value:[3,6,9,96,18,66,33,36,48,72,12,24,69,81,84,21,26,41,11,14]},START_END:{value:[26,41,11,14]},MIN_ENCODED_CHARS:{value:4},MAX_ACCEPTABLE:{value:2},PADDING:{value:1.5},FORMAT:{value:"codabar",writeable:!1}};return b.prototype=Object.create(a.prototype,c),b.prototype.constructor=b,b.prototype._decode=function(){var a,b,c,d,e,f=this,g=[];if(this._counters=f._fillCounters(),a=f._findStart(),!a)return null;d=a.startCounter;do{if(c=f._toPattern(d),0>c)return null;if(b=f._patternToChar(c),0>b)return null;if(g.push(b),d+=8,g.length>1&&f._isStartEnd(c))break}while(df._counters.length?f._counters.length:d,e=a.start+f._sumCounters(a.startCounter,d-8),{code:g.join(""),start:a.start,end:e,startInfo:a,decodedCodes:g}):null},b.prototype._verifyWhitespace=function(a,b){return(0>=a-1||this._counters[a-1]>=this._calculatePatternLength(a)/2)&&(b+8>=this._counters.length||this._counters[b+7]>=this._calculatePatternLength(b)/2)?!0:!1},b.prototype._calculatePatternLength=function(a){var b,c=0;for(b=a;a+7>b;b++)c+=this._counters[b];return c},b.prototype._thresholdResultPattern=function(a,b){var c,d,e,f,g,h=this,i={space:{narrow:{size:0,counts:0,min:0,max:Number.MAX_VALUE},wide:{size:0,counts:0,min:0,max:Number.MAX_VALUE}},bar:{narrow:{size:0,counts:0,min:0,max:Number.MAX_VALUE},wide:{size:0,counts:0,min:0,max:Number.MAX_VALUE}}},j=b;for(e=0;e=0;f--)c=2===(1&f)?i.bar:i.space,d=1===(1&g)?c.wide:c.narrow,d.size+=h._counters[j+f],d.counts++,g>>=1;j+=8}return["space","bar"].forEach(function(a){var b=i[a];b.wide.min=Math.floor((b.narrow.size/b.narrow.counts+b.wide.size/b.wide.counts)/2),b.narrow.max=Math.ceil(b.wide.min),b.wide.max=Math.ceil((b.wide.size*h.MAX_ACCEPTABLE+h.PADDING)/b.wide.counts)}),i},b.prototype._charToPattern=function(a){var b,c=this,d=a.charCodeAt(0);for(b=0;b=0;d--){if(e=0===(1&d)?j.bar:j.space,f=1===(1&h)?e.wide:e.narrow,g=i._counters[k+d],gf.max)return!1;h>>=1}k+=8}return!0},b.prototype._patternToChar=function(a){var b,c=this;for(b=0;bc;c+=2)d=this._counters[c],d>f&&(f=d),e>d&&(e=d);return(e+f)/2|0},b.prototype._toPattern=function(a){var b,c,d,e,f=7,g=a+f,h=1<this._counters.length)return-1;for(b=this._computeAlternatingThreshold(a,g),c=this._computeAlternatingThreshold(a+1,g),d=0;f>d;d++)e=0===(1&d)?b:c,this._counters[a+d]>e&&(i|=h),h>>=1;return i},b.prototype._isStartEnd=function(a){var b;for(b=0;bc;c++)d+=this._counters[c];return d},b.prototype._findStart=function(){var a,b,c,d=this,e=d._nextUnset(d._row);for(a=1;ad;d++){if(a=e._decodeCode(a.end,e.CODE_G_START),!a)return null;b.push(a.code),c.push(a)}if(a=e._findPattern(e.MIDDLE_PATTERN,a.end,!0,!1),null===a)return null;for(c.push(a),d=0;4>d;d++){if(a=e._decodeCode(a.end,e.CODE_G_START),!a)return null;c.push(a),b.push(a.code)}return a},b}),d("upc_e_reader",["./ean_reader"],function(a){"use strict";function b(){a.call(this)}var c={CODE_FREQUENCY:{value:[[56,52,50,49,44,38,35,42,41,37],[7,11,13,14,19,25,28,21,22,26]]},STOP_PATTERN:{value:[1/6*7,1/6*7,1/6*7,1/6*7,1/6*7,1/6*7]},FORMAT:{value:"upc_e",writeable:!1}};return b.prototype=Object.create(a.prototype,c),b.prototype.constructor=b,b.prototype._decodePayload=function(a,b,c){var d,e=this,f=0;for(d=0;6>d;d++){if(a=e._decodeCode(a.end),!a)return null;a.code>=e.CODE_G_START&&(a.code=a.code-e.CODE_G_START,f|=1<<5-d),b.push(a.code),c.push(a)}return e._determineParity(f,b)?a:null},b.prototype._determineParity=function(a,b){var c,d,e=this;for(d=0;d=c?b.concat(a.slice(1,3)).concat([c,0,0,0,0]).concat(a.slice(3,6)):3===c?b.concat(a.slice(1,4)).concat([0,0,0,0,0]).concat(a.slice(4,6)):4===c?b.concat(a.slice(1,5)).concat([0,0,0,0,0,a[5]]):b.concat(a.slice(1,6)).concat([0,0,0,0,c]),b.push(a[a.length-1]),b},b.prototype._checksum=function(b){return a.prototype._checksum.call(this,this._convertToUPCA(b))},b.prototype._findEnd=function(b,c){return c=!0,a.prototype._findEnd.call(this,b,c)},b.prototype._verifyTrailingWhitespace=function(a){var b,c=this;return b=a.end+(a.end-a.start)/2,bf))return m.error=f,m.start=e-h,m.end=e,m;if(!d)return null;for(g=0;g=0&&c._matchRange(a,b.start,0))return b;d=b.end,b=null}},c.prototype._verifyTrailingWhitespace=function(a){var b,c=this;return b=a.end+(a.end-a.start)/2,bg;){for(d=0;5>d;d++)i[0][d]=a[g]*this.barSpaceRatio[0],i[1][d]=a[g+1]*this.barSpaceRatio[1],g+=2;if(e=f._decodePair(i),!e)return null;for(d=0;d1&&(!d.inImageWithBorder(a[0],0)||!d.inImageWithBorder(a[1],0));)c-=Math.ceil(c/2),e(-c);return a}function i(a){return[{x:(a[1][0]-a[0][0])/2+a[0][0],y:(a[1][1]-a[0][1])/2+a[0][1]},{x:(a[3][0]-a[2][0])/2+a[2][0],y:(a[3][1]-a[2][1])/2+a[2][1]}]}function j(e){var f,g=null,h=a.getBarcodeLine(d,e[0],e[1]);for(c.showFrequency&&(b.drawPath(e,{x:"x",y:"y"},o.ctx.overlay,{color:"red",lineWidth:3}),a.debug.printFrequency(h.line,o.dom.frequency)),a.toBinaryLine(h),c.showPattern&&a.debug.printPattern(h.line,o.dom.pattern),f=0;fd&&null===i;d++)e=g/h*d*(d%2===0?-1:1),f={y:e*k,x:e*l},b[0].y+=f.x,b[0].x-=f.y,b[1].y+=f.x,b[1].x-=f.y,i=j(b);return i}function m(a){return Math.sqrt(Math.pow(Math.abs(a[1].y-a[0].y),2)+Math.pow(Math.abs(a[1].x-a[0].x),2))}function n(a){var d,e,f,g,l=o.ctx.overlay;return c.drawBoundingBox&&l&&b.drawPath(a,{x:0,y:1},l,{color:"blue",lineWidth:2}),d=i(a),g=m(d),e=Math.atan2(d[1].y-d[0].y,d[1].x-d[0].x),d=h(d,e,Math.floor(.1*g)),null===d?null:(f=j(d),null===f&&(f=k(a,d,e)),null===f?null:(f&&c.drawScanline&&l&&b.drawPath(d,{x:"x",y:"y"},l,{color:"red",lineWidth:3}),{codeResult:f.codeResult,line:d,angle:e,pattern:f.barcodeLine.line,threshold:f.barcodeLine.threshold}))}var o={ctx:{frequency:null,pattern:null,overlay:null},dom:{frequency:null,pattern:null,overlay:null}},p=[];return e(),f(),g(),{decodeFromBoundingBox:function(a){return n(a)},decodeFromBoundingBoxes:function(a){var b,c;for(b=0;b0?a.videoWidth>0&&a.videoHeight>0?(console.log(a.videoWidth+"px x "+a.videoHeight+"px"),b()):window.setTimeout(c,500):b("Unable to play video stream. Is webcam working?"),d--}var d=10;c()}function d(a,d,e){b(a,function(a){d.src=a,h&&d.removeEventListener("loadeddata",h,!1),h=c.bind(null,d,e),d.addEventListener("loadeddata",h,!1),d.play()},function(a){e(a)})}function e(b,c){var d={audio:!1,video:!0},e=a.mergeObjects({width:640,height:480,minAspectRatio:0,maxAspectRatio:100,facing:"environment"},b);return"undefined"==typeof MediaStreamTrack||"undefined"==typeof MediaStreamTrack.getSources?(d.video={mediaSource:"camera",width:{min:e.width,max:e.width},height:{min:e.height,max:e.height},require:["width","height"]},c(d)):void MediaStreamTrack.getSources(function(a){for(var b,f=0;f!=a.length;++f){var g=a[f];"video"==g.kind&&g.facing==e.facing&&(b=g.id)}return d.video={mandatory:{minWidth:e.width,minHeight:e.height,minAspectRatio:e.minAspectRatio,maxAspectRatio:e.maxAspectRatio},optional:[{sourceId:b}]},c(d)})}function f(a,b,c){e(b,function(b){d(b,a,c)})}var g,h;return{request:function(a,b,c){f(a,b,c)},release:function(){var a=g&&g.getVideoTracks();a.length&&a[0].stop(),g=null}}}),d("result_collector",["image_debug"],function(a){"use strict";function b(a,b){return b?b.some(function(b){return Object.keys(b).every(function(c){return b[c]===a[c]})}):!1}function c(a,b){return"function"==typeof b?b(a):!0}return{create:function(d){function e(a){return i&&a&&!b(a,d.blacklist)&&c(a,d.filter)}var f=document.createElement("canvas"),g=f.getContext("2d"),h=[],i=d.capacity||20,j=d.capture===!0;return{addResult:function(b,c,d){var k={};e(d)&&(i--,k.codeResult=d,j&&(f.width=c.x,f.height=c.y,a.drawImage(b,c,g),k.frame=f.toDataURL()),h.push(k))},getResults:function(){return h}}}}}),d("quagga",["input_stream","image_wrapper","barcode_locator","barcode_decoder","frame_grabber","html_utils","config","events","camera_access","image_debug","gl-matrix","result_collector"],function(b,c,d,e,f,g,h,i,j,k,l,m){"use strict";function n(a){t(a),K=e.create(h.decoder,I)}function o(){if("undefined"!=typeof document)for(var a=[{node:document.querySelector("div[data-controls]"),prop:h.controls},{node:M.dom.overlay,prop:h.visual.show}],b=0;b0?A(function(){console.log("Workers created"),r(a)}):(n(),r(a))}function r(a){F.play(),a()}function s(){if("undefined"!=typeof document){var a=document.querySelector("#interactive.viewport");if(M.dom.image=document.querySelector("canvas.imgBuffer"),M.dom.image||(M.dom.image=document.createElement("canvas"),M.dom.image.className="imgBuffer",a&&"ImageStream"==h.inputStream.type&&a.appendChild(M.dom.image)),M.ctx.image=M.dom.image.getContext("2d"),M.dom.image.width=F.getCanvasSize().x,M.dom.image.height=F.getCanvasSize().y,M.dom.overlay=document.querySelector("canvas.drawingBuffer"),!M.dom.overlay){M.dom.overlay=document.createElement("canvas"),M.dom.overlay.className="drawingBuffer",a&&a.appendChild(M.dom.overlay);var b=document.createElement("br");b.setAttribute("clear","all"),a&&a.appendChild(b)}M.ctx.overlay=M.dom.overlay.getContext("2d"),M.dom.overlay.width=F.getCanvasSize().x,M.dom.overlay.height=F.getCanvasSize().y}}function t(a){I=a?a:new c({x:F.getWidth(),y:F.getHeight()}),console.log(I.size),J=[P.clone([0,0]),P.clone([0,I.size.y]),P.clone([I.size.x,I.size.y]),P.clone([I.size.x,0])],d.init(I,h.locator)}function u(){return h.locate?d.locate():[[P.clone(J[0]),P.clone(J[1]),P.clone(J[2]),P.clone(J[3])]]}function v(a){function b(a){for(var b=a.length;b--;)a[b][0]+=f,a[b][1]+=g}function c(a){a[0].x+=f,a[0].y+=g,a[1].x+=f,a[1].y+=g}var d,e=F.getTopRight(),f=e.x,g=e.y;if(a&&(0!==f||0!==g)&&(a.line&&2===a.line.length&&c(a.line),a.boxes&&a.boxes.length>0))for(d=0;d0){if(a=N.filter(function(a){return!a.busy})[0],!a)return;G.attachData(a.imageData)}else G.attachData(I.data);G.grab()&&(a?(a.busy=!0,a.worker.postMessage({cmd:"process",imageData:a.imageData},[a.imageData.buffer])):x())}else x()}function z(){H=!1,function a(){H||(y(),O&&"LiveStream"==h.inputStream.type&&window.requestAnimFrame(a))}()}function A(a){function b(b){N.push(b),N.length>=h.numOfWorkers&&a()}var c;for(N=[],c=0;c0&&N.forEach(function(b){b.worker.postMessage({cmd:"setReaders",readers:a})})}var F,G,H,I,J,K,L,M={ctx:{image:null,overlay:null},dom:{image:null,overlay:null}},N=[],O=!0,P=l.vec2;return{init:function(a,b,c){return h=g.mergeObjects(h,a),c?(O=!1,n(c),b()):void p(b)},start:function(){z()},stop:function(){H=!0,N.forEach(function(a){a.worker.terminate(),console.log("Worker terminated!")}),N.length=0,"LiveStream"===h.inputStream.type&&(j.release(),F.clearEventHandlers())},pause:function(){H=!0},onDetected:function(a){i.subscribe("detected",a)},offDetected:function(a){i.unsubscribe("detected",a)},onProcessed:function(a){i.subscribe("processed",a)},offProcessed:function(a){i.unsubscribe("processed",a)},setReaders:function(a){E(a)},registerResultCollector:function(a){a&&"function"==typeof a.addResult&&(L=a)},canvas:M,decodeSingle:function(a,b){a=g.mergeObjects({inputStream:{type:"ImageStream",sequence:!1,size:800,src:a.src},numOfWorkers:1,locator:{halfSample:!1}},a),this.init(a,function(){i.once("processed",function(a){H=!0,b.call(null,a)},!0),z()})},ImageWrapper:c,ImageDebug:k,ResultCollector:m}}),c("quagga")}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(e.toString()):"object"==typeof exports?exports.Quagga=e(e.toString()):t.Quagga=e(e.toString())}(this,function(t){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="/",e(0)}([function(t,e,n){t.exports=n(1)},function(e,n,r){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function a(t){s(t),O=L["default"].create(tt.decoder,E)}function i(){if("undefined"!=typeof document)for(var t=[{node:document.querySelector("div[data-controls]"),prop:tt.controls},{node:K.dom.overlay,prop:tt.visual.show}],e=0;e0?y(function(){console.log("Workers created"),f(t)}):(a(),f(t))}function f(t){w.play(),t()}function l(){if("undefined"!=typeof document){var t=document.querySelector("#interactive.viewport");if(K.dom.image=document.querySelector("canvas.imgBuffer"),K.dom.image||(K.dom.image=document.createElement("canvas"),K.dom.image.className="imgBuffer",t&&"ImageStream"===tt.inputStream.type&&t.appendChild(K.dom.image)),K.ctx.image=K.dom.image.getContext("2d"),K.dom.image.width=w.getCanvasSize().x,K.dom.image.height=w.getCanvasSize().y,K.dom.overlay=document.querySelector("canvas.drawingBuffer"),!K.dom.overlay){K.dom.overlay=document.createElement("canvas"),K.dom.overlay.className="drawingBuffer",t&&t.appendChild(K.dom.overlay);var e=document.createElement("br");e.setAttribute("clear","all"),t&&t.appendChild(e)}K.ctx.overlay=K.dom.overlay.getContext("2d"),K.dom.overlay.width=w.getCanvasSize().x,K.dom.overlay.height=w.getCanvasSize().y}}function s(t){E=t?t:new P["default"]({x:w.getWidth(),y:w.getHeight()}),console.log(E.size),C=[Y.vec2.clone([0,0]),Y.vec2.clone([0,E.size.y]),Y.vec2.clone([E.size.x,E.size.y]),Y.vec2.clone([E.size.x,0])],I["default"].init(E,tt.locator)}function d(){return tt.locate?I["default"].locate():[[Y.vec2.clone(C[0]),Y.vec2.clone(C[1]),Y.vec2.clone(C[2]),Y.vec2.clone(C[3])]]}function h(t){function e(t){for(var e=t.length;e--;)t[e][0]+=a,t[e][1]+=i}function n(t){t[0].x+=a,t[0].y+=i,t[1].x+=a,t[1].y+=i}var r,o=w.getTopRight(),a=o.x,i=o.y;if(t&&(0!==a||0!==i)&&(t.line&&2===t.line.length&&n(t.line),t.boxes&&t.boxes.length>0))for(r=0;r0){if(t=$.filter(function(t){return!t.busy})[0],!t)return;R.attachData(t.imageData)}else R.attachData(E.data);R.grab()&&(t?(t.busy=!0,t.worker.postMessage({cmd:"process",imageData:t.imageData},[t.imageData.buffer])):v())}else v()}function m(){A=!1,function t(){A||(g(),J&&"LiveStream"===tt.inputStream.type&&window.requestAnimFrame(t))}()}function y(t){function e(e){$.push(e),$.length>=tt.numOfWorkers&&t()}var n;for($=[],n=0;n0&&$.forEach(function(e){e.worker.postMessage({cmd:"setReaders",readers:t})})}Object.defineProperty(n,"__esModule",{value:!0});var w,R,A,E,C,O,T,D=r(2),S=(o(D),r(3)),P=o(S),z=r(18),I=o(z),N=r(23),L=o(N),j=r(68),k=o(j),q=r(69),U=o(q),W=r(70),F=o(W),B=r(22),G=o(B),Y=r(7),V=r(71),H=o(V),X=r(35),Q=r(72),Z=r(74),K={ctx:{image:null,overlay:null},dom:{image:null,overlay:null}},$=[],J=!0,tt={};n["default"]={init:function(t,e,n){return tt=X({},k["default"],t),n?(J=!1,a(n),e()):void u(e)},start:function(){m()},stop:function(){A=!0,$.forEach(function(t){t.worker.terminate(),console.log("Worker terminated!")}),$.length=0,"LiveStream"===tt.inputStream.type&&(F["default"].release(),w.clearEventHandlers())},pause:function(){A=!0},onDetected:function(t){U["default"].subscribe("detected",t)},offDetected:function(t){U["default"].unsubscribe("detected",t)},onProcessed:function(t){U["default"].subscribe("processed",t)},offProcessed:function(t){U["default"].unsubscribe("processed",t)},setReaders:function(t){b(t)},registerResultCollector:function(t){t&&"function"==typeof t.addResult&&(T=t)},canvas:K,decodeSingle:function(t,e){t=X({inputStream:{type:"ImageStream",sequence:!1,size:800,src:t.src},numOfWorkers:1,locator:{halfSample:!1}},t),this.init(t,function(){U["default"].once("processed",function(t){A=!0,e.call(null,t)},!0),m()})},ImageWrapper:P["default"],ImageDebug:G["default"],ResultCollector:H["default"]},e.exports=n["default"]},function(t,e){"use strict";"undefined"!=typeof window&&(window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia,window.URL=window.URL||window.webkitURL||window.mozURL||window.msURL),Math.imul=Math.imul||function(t,e){var n=t>>>16&65535,r=65535&t,o=e>>>16&65535,a=65535&e;return r*a+(n*a+r*o<<16>>>0)|0}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t,e,n,r){e?this.data=e:n?(this.data=new n(t.x*t.y),n===Array&&r&&l["default"].init(this.data,0)):(this.data=new Uint8Array(t.x*t.y),Uint8Array===Array&&r&&l["default"].init(this.data,0)),this.size=t}Object.defineProperty(e,"__esModule",{value:!0});var a=n(4),i=r(a),u=n(5),c=r(u),f=n(17),l=r(f),s=n(7);o.prototype.inImageWithBorder=function(t,e){return t.x>=e&&t.y>=e&&t.xn;n++)for(r=0;o>r;r++)t.data[r*a+n]=this.data[(e.y+r)*this.size.x+e.x+n]},o.prototype.copyTo=function(t){for(var e=this.data.length,n=this.data,r=t.data;e--;)r[e]=n[e]},o.prototype.get=function(t,e){return this.data[e*this.size.x+t]},o.prototype.getSafe=function(t,e){var n;if(!this.indexMapping){for(this.indexMapping={x:[],y:[]},n=0;nt;t++)r[t]=r[(n-1)*e+t]=0;for(t=1;n-1>t;t++)r[t*e]=r[t*e+(e-1)]=0},o.prototype.invert=function(){for(var t=this.data,e=t.length;e--;)t[e]=t[e]?0:1},o.prototype.convolve=function(t){var e,n,r,o,a=t.length/2|0,i=0;for(n=0;n=o;o++)for(r=-a;a>=r;r++)i+=t[o+a][r+a]*this.getSafe(e+r,n+o);this.data[n*this.size.x+e]=i}},o.prototype.moments=function(t){var e,n,r,o,a,i,u,c,f,l,d,h,p=this.data,v=this.size.y,g=this.size.x,m=[],y=[],_=Math.PI,x=_/4;if(0>=t)return y;for(a=0;t>a;a++)m[a]={m00:0,m01:0,m10:0,m11:0,m02:0,m20:0,theta:0,rad:0};for(n=0;v>n;n++)for(o=n*n,e=0;g>e;e++)r=p[n*g+e],r>0&&(i=m[r-1],i.m00+=1,i.m01+=n,i.m10+=e,i.m11+=e*n,i.m02+=o,i.m20+=e*e);for(a=0;t>a;a++)i=m[a],isNaN(i.m00)||0===i.m00||(l=i.m10/i.m00,d=i.m01/i.m00,u=i.m11/i.m00-l*d,c=i.m02/i.m00-d*d,f=i.m20/i.m00-l*l,h=(c-f)/(2*u),h=.5*Math.atan(h)+(u>=0?x:-x)+_,i.theta=(180*h/_+90)%180-90,i.theta<0&&(i.theta+=180),i.rad=h>_?h-_:h,i.vec=s.vec2.clone([Math.cos(h),Math.sin(h)]),y.push(i));return y},o.prototype.show=function(t,e){var n,r,o,a,i,u,c;for(e||(e=1),n=t.getContext("2d"),t.width=this.size.x,t.height=this.size.y,r=n.getImageData(0,0,t.width,t.height),o=r.data,a=0,c=0;ce||e>360)&&(e=360);for(var r=[0,1,1],o=[0,0,0],a=[255,255,255],i=[0,0,0],u=[],f=t.getContext("2d"),l=f.getImageData(n.x,n.y,this.size.x,this.size.y),s=l.data,d=this.data.length;d--;)r[0]=this.data[d]*e,u=r[0]<=0?a:r[0]>=360?i:c["default"].hsv2rgb(r,o),s[4*d+0]=u[0],s[4*d+1]=u[1],s[4*d+2]=u[2],s[4*d+3]=255;f.putImageData(l,n.x,n.y)},e["default"]=o,t.exports=e["default"]},function(t,e){"use strict";function n(t,e,n){n||(n={data:null,size:e}),this.data=n.data,this.originalSize=n.size,this.I=n,this.from=t,this.size=e}Object.defineProperty(e,"__esModule",{value:!0}),n.prototype.show=function(t,e){var n,r,o,a,i,u,c;for(e||(e=1),n=t.getContext("2d"),t.width=this.size.x,t.height=this.size.y,r=n.getImageData(0,0,t.width,t.height),o=r.data,a=0,i=0;i0?Math.floor(this.x+.5):Math.floor(this.x-.5),this.y=this.y>0?Math.floor(this.y+.5):Math.floor(this.y-.5),this}};return n},f.computeIntegralImage2=function(t,e){var n,r,o=t.data,a=t.size.x,i=t.size.y,u=e.data,c=0,f=0,l=0,s=0,d=0;for(l=a,c=0,r=1;i>r;r++)c+=o[f],u[l]+=c,f+=a,l+=a;for(f=0,l=1,c=0,n=1;a>n;n++)c+=o[f],u[l]+=c,f++,l++;for(r=1;i>r;r++)for(f=r*a+1,l=(r-1)*a+1,s=r*a,d=(r-1)*a,n=1;a>n;n++)u[f]+=o[f]+u[l]+u[s]-u[d],f++,l++,s++,d++},f.computeIntegralImage=function(t,e){for(var n=t.data,r=t.size.x,o=t.size.y,a=e.data,i=0,u=0;r>u;u++)i+=n[u],a[u]=i;for(var c=1;o>c;c++){i=0;for(var f=0;r>f;f++)i+=n[c*r+f],a[c*r+f]=i+a[(c-1)*r+f]}},f.thresholdImage=function(t,e,n){n||(n=t);for(var r=t.data,o=r.length,a=n.data;o--;)a[o]=r[o]>o]++;return i},f.sharpenLine=function(t){var e,n,r=t.length,o=t[0],a=t[1];for(e=1;r-1>e;e++)n=t[e+1],t[e-1]=2*a-o-n&255,o=a,a=n;return t},f.determineOtsuThreshold=function(t,e){function n(t,e){var n,r=0;for(n=t;e>=n;n++)r+=a[n];return r}function r(t,e){var n,r=0;for(n=t;e>=n;n++)r+=n*a[n];return r}function o(){var o,i,c,l,s,d,h,p=[0],v=(1<l;l++)o=n(0,l),i=n(l+1,v),c=o*i,0===c&&(c=1),s=r(0,l)*i,d=r(l+1,v)*o,h=s-d,p[l]=h*h/c;return u["default"].maxIndex(p)}e||(e=8);var a,i,c=8-e;return i=o(),i<=r;r++)for(o=0;h>o;o++)d[r*h+o]=0,d[(p-1-r)*h+o]=0;for(r=m;p-m>r;r++)for(o=0;m>=o;o++)d[r*h+o]=0,d[r*h+(h-1-o)]=0;for(r=m+1;p-m-1>r;r++)for(o=m+1;h-m>o;o++)a=v[(r-m-1)*h+(o-m-1)],i=v[(r-m-1)*h+(o+m)],u=v[(r+m)*h+(o-m-1)],c=v[(r+m)*h+(o+m)],g=c-u-i+a,l=g/y,d[r*h+o]=s[r*h+o]>l+5?0:1},f.cluster=function(t,e,n){function r(t){var e=!1;for(i=0;ie.x-f&&t.xe.y-l&&t.yn;n++){for(i=Math.floor(Math.random()*t.length),o=[],u=i,o.push(t[u]);null!==(u=l(u,!0));)o.push(t[u]);if(i>0)for(u=i;null!==(u=l(u,!1));)o.push(t[u]);o.length>a.length&&(a=o)}return a}},f.DILATE=1,f.ERODE=2,f.dilate=function(t,e){var n,r,o,a,i,u,c,f=t.data,l=e.data,s=t.size.y,d=t.size.x;for(n=1;s-1>n;n++)for(r=1;d-1>r;r++)a=n-1,i=n+1,u=r-1,c=r+1,o=f[a*d+u]+f[a*d+c]+f[n*d+r]+f[i*d+u]+f[i*d+c],l[n*d+r]=o>0?1:0},f.erode=function(t,e){var n,r,o,a,i,u,c,f=t.data,l=e.data,s=t.size.y,d=t.size.x;for(n=1;s-1>n;n++)for(r=1;d-1>r;r++)a=n-1,i=n+1,u=r-1,c=r+1,o=f[a*d+u]+f[a*d+c]+f[n*d+r]+f[i*d+u]+f[i*d+c],l[n*d+r]=5===o?1:0},f.subtract=function(t,e,n){n||(n=t);for(var r=t.data.length,o=t.data,a=e.data,i=n.data;r--;)i[r]=o[r]-a[r]},f.bitwiseOr=function(t,e,n){n||(n=t);for(var r=t.data.length,o=t.data,a=e.data,i=n.data;r--;)i[r]=o[r]||a[r]},f.countNonZero=function(t){for(var e=t.data.length,n=t.data,r=0;e--;)r+=n[e];return r},f.topGeneric=function(t,e,n){var r,o,a,i,u=0,c=0,f=[];for(r=0;e>r;r++)f[r]={score:0,item:null};for(r=0;rc)for(a=f[u],a.score=o,a.item=t[r],c=Number.MAX_VALUE,i=0;e>i;i++)f[i].scorea;){for(r=0;u>r;r++)n[c]=Math.floor((.299*t[4*o+0]+.587*t[4*o+1]+.114*t[4*o+2]+(.299*t[4*(o+1)+0]+.587*t[4*(o+1)+1]+.114*t[4*(o+1)+2])+(.299*t[4*a+0]+.587*t[4*a+1]+.114*t[4*a+2])+(.299*t[4*(a+1)+0]+.587*t[4*(a+1)+1]+.114*t[4*(a+1)+2]))/4),c++,o+=2,a+=2;o+=f,a+=f}},f.computeGray=function(t,e,n){var r,o=t.length/4|0,a=n&&n.singleChannel===!0;if(a)for(r=0;o>r;r++)e[r]=t[4*r+0];else for(r=0;o>r;r++)e[r]=Math.floor(.299*t[4*r+0]+.587*t[4*r+1]+.114*t[4*r+2])},f.loadImageArray=function(t,e,n){n||(n=document.createElement("canvas"));var r=new Image;r.callback=e,r.onload=function(){n.width=this.width,n.height=this.height;var t=n.getContext("2d");t.drawImage(this,0,0);var e=new Uint8Array(this.width*this.height);t.drawImage(this,0,0);var r=t.getImageData(0,0,this.width,this.height).data;f.computeGray(r,e),this.callback(e,{x:this.width,y:this.height},this)},r.src=t},f.halfSample=function(t,e){for(var n=t.data,r=t.size.x,o=e.data,a=0,i=r,u=n.length,c=r/2,f=0;u>i;){for(var l=0;c>l;l++)o[f]=Math.floor((n[a]+n[a+1]+n[i]+n[i+1])/4),f++,a+=2,i+=2;a+=r,i+=r}},f.hsv2rgb=function(t,e){var n=t[0],r=t[1],o=t[2],a=o*r,i=a*(1-Math.abs(n/60%2-1)),u=o-a,c=0,f=0,l=0;return e=e||[0,0,0],60>n?(c=a,f=i):120>n?(c=i,f=a):180>n?(f=a,l=i):240>n?(f=i,l=a):300>n?(c=i,l=a):360>n&&(c=a,l=i),e[0]=255*(c+u)|0,e[1]=255*(f+u)|0,e[2]=255*(l+u)|0,e},f._computeDivisors=function(t){var e,n=[],r=[];for(e=1;ee[r]?r++:n++;return o},f.calculatePatchSize=function(t,e){function n(t){for(var e=0,n=t[Math.floor(t.length/2)];e0&&(n=Math.abs(t[e]-d)>Math.abs(t[e-1]-d)?t[e-1]:t[e]),d/nc[l-1]/c[l]?{x:n,y:n}:null}var r,o=this._computeDivisors(e.x),a=this._computeDivisors(e.y),i=Math.max(e.x,e.y),u=this._computeIntersection(o,a),c=[8,10,15,20,32,60,80],f={"x-small":5,small:4,medium:3,large:2,"x-large":1},l=f[t]||f.medium,s=c[l],d=Math.floor(i/s);return r=n(u),r||(r=n(this._computeDivisors(i)),r||(r=n(this._computeDivisors(d*s)))),r},f._parseCSSDimensionValues=function(t){var e={value:parseFloat(t),unit:(t.indexOf("%")===t.length-1,"%")};return e},f._dimensionsConverters={top:function(t,e){return"%"===t.unit?Math.floor(e.height*(t.value/100)):void 0},right:function(t,e){return"%"===t.unit?Math.floor(e.width-e.width*(t.value/100)):void 0},bottom:function(t,e){return"%"===t.unit?Math.floor(e.height-e.height*(t.value/100)):void 0},left:function(t,e){return"%"===t.unit?Math.floor(e.width*(t.value/100)):void 0}},f.computeImageArea=function(t,e,n){var r={width:t,height:e},o=Object.keys(n).reduce(function(t,e){var o=n[e],a=f._parseCSSDimensionValues(o),i=f._dimensionsConverters[e](a,r);return t[e]=i,t},{});return{sx:o.left,sy:o.top,sw:o.right-o.left,sh:o.bottom-o.top}},e["default"]=f,t.exports=e["default"]},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(7);e["default"]={create:function(t,e){function n(){o(t),a()}function o(t){c[t.id]=t,i.push(t)}function a(){var t,e=0;for(t=0;te?!0:!1},getPoints:function(){return i},getCenter:function(){return u}}},createPoint:function(t,e,n){return{rad:t[n],point:t,id:e}}},t.exports=e["default"]},function(t,e,n){e.glMatrix=n(8),e.mat2=n(9),e.mat2d=n(10),e.mat3=n(11),e.mat4=n(12),e.quat=n(13),e.vec2=n(16),e.vec3=n(14),e.vec4=n(15)},function(t,e){var n={};n.EPSILON=1e-6,n.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,n.RANDOM=Math.random,n.setMatrixArrayType=function(t){GLMAT_ARRAY_TYPE=t};var r=Math.PI/180;n.toRadian=function(t){return t*r},t.exports=n},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(4);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},o.clone=function(t){var e=new r.ARRAY_TYPE(4);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},o.transpose=function(t,e){if(t===e){var n=e[1];t[1]=e[2],t[2]=n}else t[0]=e[0],t[1]=e[2],t[2]=e[1],t[3]=e[3];return t},o.invert=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=n*a-o*r;return i?(i=1/i,t[0]=a*i,t[1]=-r*i,t[2]=-o*i,t[3]=n*i,t):null},o.adjoint=function(t,e){var n=e[0];return t[0]=e[3],t[1]=-e[1],t[2]=-e[2],t[3]=n,t},o.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},o.multiply=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=n[0],c=n[1],f=n[2],l=n[3];return t[0]=r*u+a*c,t[1]=o*u+i*c,t[2]=r*f+a*l,t[3]=o*f+i*l,t},o.mul=o.multiply,o.rotate=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=Math.sin(n),c=Math.cos(n);return t[0]=r*c+a*u,t[1]=o*c+i*u,t[2]=r*-u+a*c,t[3]=o*-u+i*c,t},o.scale=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=n[0],c=n[1];return t[0]=r*u,t[1]=o*u,t[2]=a*c,t[3]=i*c,t},o.fromRotation=function(t,e){var n=Math.sin(e),r=Math.cos(e);return t[0]=r,t[1]=n,t[2]=-n,t[3]=r,t},o.fromScaling=function(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=e[1],t},o.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},o.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},o.LDU=function(t,e,n,r){return t[2]=r[2]/r[0],n[0]=r[0],n[1]=r[1],n[3]=r[3]-t[2]*n[1],[t,e,n]},t.exports=o},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(6);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},o.clone=function(t){var e=new r.ARRAY_TYPE(6);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},o.invert=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=e[4],u=e[5],c=n*a-r*o;return c?(c=1/c,t[0]=a*c,t[1]=-r*c,t[2]=-o*c,t[3]=n*c,t[4]=(o*u-a*i)*c,t[5]=(r*i-n*u)*c,t):null},o.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},o.multiply=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=n[0],l=n[1],s=n[2],d=n[3],h=n[4],p=n[5];return t[0]=r*f+a*l,t[1]=o*f+i*l,t[2]=r*s+a*d,t[3]=o*s+i*d,t[4]=r*h+a*p+u,t[5]=o*h+i*p+c,t},o.mul=o.multiply,o.rotate=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=Math.sin(n),l=Math.cos(n);return t[0]=r*l+a*f,t[1]=o*l+i*f,t[2]=r*-f+a*l,t[3]=o*-f+i*l,t[4]=u,t[5]=c,t},o.scale=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=n[0],l=n[1];return t[0]=r*f,t[1]=o*f,t[2]=a*l,t[3]=i*l,t[4]=u,t[5]=c,t},o.translate=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=n[0],l=n[1];return t[0]=r,t[1]=o,t[2]=a,t[3]=i,t[4]=r*f+a*l+u,t[5]=o*f+i*l+c,t},o.fromRotation=function(t,e){var n=Math.sin(e),r=Math.cos(e);return t[0]=r,t[1]=n,t[2]=-n,t[3]=r,t[4]=0,t[5]=0,t},o.fromScaling=function(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=e[1],t[4]=0,t[5]=0,t},o.fromTranslation=function(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=e[0],t[5]=e[1],t},o.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},o.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},t.exports=o},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.fromMat4=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[4],t[4]=e[5],t[5]=e[6],t[6]=e[8],t[7]=e[9],t[8]=e[10],t},o.clone=function(t){var e=new r.ARRAY_TYPE(9);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.transpose=function(t,e){if(t===e){var n=e[1],r=e[2],o=e[5];t[1]=e[3],t[2]=e[6],t[3]=n,t[5]=e[7],t[6]=r,t[7]=o}else t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8];return t},o.invert=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=e[4],u=e[5],c=e[6],f=e[7],l=e[8],s=l*i-u*f,d=-l*a+u*c,h=f*a-i*c,p=n*s+r*d+o*h;return p?(p=1/p,t[0]=s*p,t[1]=(-l*r+o*f)*p,t[2]=(u*r-o*i)*p,t[3]=d*p,t[4]=(l*n-o*c)*p,t[5]=(-u*n+o*a)*p,t[6]=h*p,t[7]=(-f*n+r*c)*p,t[8]=(i*n-r*a)*p,t):null},o.adjoint=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=e[4],u=e[5],c=e[6],f=e[7],l=e[8];return t[0]=i*l-u*f,t[1]=o*f-r*l,t[2]=r*u-o*i,t[3]=u*c-a*l,t[4]=n*l-o*c,t[5]=o*a-n*u,t[6]=a*f-i*c,t[7]=r*c-n*f,t[8]=n*i-r*a,t},o.determinant=function(t){var e=t[0],n=t[1],r=t[2],o=t[3],a=t[4],i=t[5],u=t[6],c=t[7],f=t[8];return e*(f*a-i*c)+n*(-f*o+i*u)+r*(c*o-a*u)},o.multiply=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=e[6],l=e[7],s=e[8],d=n[0],h=n[1],p=n[2],v=n[3],g=n[4],m=n[5],y=n[6],_=n[7],x=n[8];return t[0]=d*r+h*i+p*f,t[1]=d*o+h*u+p*l,t[2]=d*a+h*c+p*s,t[3]=v*r+g*i+m*f,t[4]=v*o+g*u+m*l,t[5]=v*a+g*c+m*s,t[6]=y*r+_*i+x*f,t[7]=y*o+_*u+x*l,t[8]=y*a+_*c+x*s,t},o.mul=o.multiply,o.translate=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=e[6],l=e[7],s=e[8],d=n[0],h=n[1];return t[0]=r,t[1]=o,t[2]=a,t[3]=i,t[4]=u,t[5]=c,t[6]=d*r+h*i+f,t[7]=d*o+h*u+l,t[8]=d*a+h*c+s,t},o.rotate=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=e[6],l=e[7],s=e[8],d=Math.sin(n),h=Math.cos(n);return t[0]=h*r+d*i,t[1]=h*o+d*u,t[2]=h*a+d*c,t[3]=h*i-d*r,t[4]=h*u-d*o,t[5]=h*c-d*a,t[6]=f,t[7]=l,t[8]=s,t},o.scale=function(t,e,n){var r=n[0],o=n[1];return t[0]=r*e[0],t[1]=r*e[1],t[2]=r*e[2],t[3]=o*e[3],t[4]=o*e[4],t[5]=o*e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t},o.fromTranslation=function(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=e[0],t[7]=e[1],t[8]=1,t},o.fromRotation=function(t,e){var n=Math.sin(e),r=Math.cos(e);return t[0]=r,t[1]=n,t[2]=0,t[3]=-n,t[4]=r,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.fromScaling=function(t,e){return t[0]=e[0],t[1]=0,t[2]=0,t[3]=0,t[4]=e[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.fromMat2d=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=0,t[3]=e[2],t[4]=e[3],t[5]=0,t[6]=e[4],t[7]=e[5],t[8]=1,t},o.fromQuat=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=n+n,u=r+r,c=o+o,f=n*i,l=r*i,s=r*u,d=o*i,h=o*u,p=o*c,v=a*i,g=a*u,m=a*c;return t[0]=1-s-p,t[3]=l-m,t[6]=d+g,t[1]=l+m,t[4]=1-f-p,t[7]=h-v,t[2]=d-g,t[5]=h+v,t[8]=1-f-s,t},o.normalFromMat4=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=e[4],u=e[5],c=e[6],f=e[7],l=e[8],s=e[9],d=e[10],h=e[11],p=e[12],v=e[13],g=e[14],m=e[15],y=n*u-r*i,_=n*c-o*i,x=n*f-a*i,M=r*c-o*u,b=r*f-a*u,w=o*f-a*c,R=l*v-s*p,A=l*g-d*p,E=l*m-h*p,C=s*g-d*v,O=s*m-h*v,T=d*m-h*g,D=y*T-_*O+x*C+M*E-b*A+w*R;return D?(D=1/D,t[0]=(u*T-c*O+f*C)*D,t[1]=(c*E-i*T-f*A)*D,t[2]=(i*O-u*E+f*R)*D,t[3]=(o*O-r*T-a*C)*D,t[4]=(n*T-o*E+a*A)*D,t[5]=(r*E-n*O-a*R)*D,t[6]=(v*w-g*b+m*M)*D,t[7]=(g*x-p*w-m*_)*D,t[8]=(p*b-v*x+m*y)*D,t):null},o.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},o.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},t.exports=o},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},o.clone=function(t){var e=new r.ARRAY_TYPE(16);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e[9]=t[9],e[10]=t[10],e[11]=t[11],e[12]=t[12],e[13]=t[13],e[14]=t[14],e[15]=t[15],e},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t[4]=e[4],t[5]=e[5],t[6]=e[6],t[7]=e[7],t[8]=e[8],t[9]=e[9],t[10]=e[10],t[11]=e[11],t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},o.transpose=function(t,e){if(t===e){var n=e[1],r=e[2],o=e[3],a=e[6],i=e[7],u=e[11];t[1]=e[4],t[2]=e[8],t[3]=e[12],t[4]=n,t[6]=e[9],t[7]=e[13],t[8]=r,t[9]=a,t[11]=e[14],t[12]=o,t[13]=i,t[14]=u}else t[0]=e[0],t[1]=e[4],t[2]=e[8],t[3]=e[12],t[4]=e[1],t[5]=e[5],t[6]=e[9],t[7]=e[13],t[8]=e[2],t[9]=e[6],t[10]=e[10],t[11]=e[14],t[12]=e[3],t[13]=e[7],t[14]=e[11],t[15]=e[15];return t},o.invert=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=e[4],u=e[5],c=e[6],f=e[7],l=e[8],s=e[9],d=e[10],h=e[11],p=e[12],v=e[13],g=e[14],m=e[15],y=n*u-r*i,_=n*c-o*i,x=n*f-a*i,M=r*c-o*u,b=r*f-a*u,w=o*f-a*c,R=l*v-s*p,A=l*g-d*p,E=l*m-h*p,C=s*g-d*v,O=s*m-h*v,T=d*m-h*g,D=y*T-_*O+x*C+M*E-b*A+w*R;return D?(D=1/D,t[0]=(u*T-c*O+f*C)*D,t[1]=(o*O-r*T-a*C)*D,t[2]=(v*w-g*b+m*M)*D,t[3]=(d*b-s*w-h*M)*D,t[4]=(c*E-i*T-f*A)*D,t[5]=(n*T-o*E+a*A)*D,t[6]=(g*x-p*w-m*_)*D,t[7]=(l*w-d*x+h*_)*D,t[8]=(i*O-u*E+f*R)*D,t[9]=(r*E-n*O-a*R)*D,t[10]=(p*b-v*x+m*y)*D,t[11]=(s*x-l*b-h*y)*D,t[12]=(u*A-i*C-c*R)*D,t[13]=(n*C-r*A+o*R)*D,t[14]=(v*_-p*M-g*y)*D,t[15]=(l*M-s*_+d*y)*D,t):null},o.adjoint=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=e[4],u=e[5],c=e[6],f=e[7],l=e[8],s=e[9],d=e[10],h=e[11],p=e[12],v=e[13],g=e[14],m=e[15];return t[0]=u*(d*m-h*g)-s*(c*m-f*g)+v*(c*h-f*d),t[1]=-(r*(d*m-h*g)-s*(o*m-a*g)+v*(o*h-a*d)),t[2]=r*(c*m-f*g)-u*(o*m-a*g)+v*(o*f-a*c),t[3]=-(r*(c*h-f*d)-u*(o*h-a*d)+s*(o*f-a*c)),t[4]=-(i*(d*m-h*g)-l*(c*m-f*g)+p*(c*h-f*d)),t[5]=n*(d*m-h*g)-l*(o*m-a*g)+p*(o*h-a*d),t[6]=-(n*(c*m-f*g)-i*(o*m-a*g)+p*(o*f-a*c)),t[7]=n*(c*h-f*d)-i*(o*h-a*d)+l*(o*f-a*c),t[8]=i*(s*m-h*v)-l*(u*m-f*v)+p*(u*h-f*s),t[9]=-(n*(s*m-h*v)-l*(r*m-a*v)+p*(r*h-a*s)),t[10]=n*(u*m-f*v)-i*(r*m-a*v)+p*(r*f-a*u),t[11]=-(n*(u*h-f*s)-i*(r*h-a*s)+l*(r*f-a*u)),t[12]=-(i*(s*g-d*v)-l*(u*g-c*v)+p*(u*d-c*s)),t[13]=n*(s*g-d*v)-l*(r*g-o*v)+p*(r*d-o*s),t[14]=-(n*(u*g-c*v)-i*(r*g-o*v)+p*(r*c-o*u)),t[15]=n*(u*d-c*s)-i*(r*d-o*s)+l*(r*c-o*u),t},o.determinant=function(t){var e=t[0],n=t[1],r=t[2],o=t[3],a=t[4],i=t[5],u=t[6],c=t[7],f=t[8],l=t[9],s=t[10],d=t[11],h=t[12],p=t[13],v=t[14],g=t[15],m=e*i-n*a,y=e*u-r*a,_=e*c-o*a,x=n*u-r*i,M=n*c-o*i,b=r*c-o*u,w=f*p-l*h,R=f*v-s*h,A=f*g-d*h,E=l*v-s*p,C=l*g-d*p,O=s*g-d*v;return m*O-y*C+_*E+x*A-M*R+b*w},o.multiply=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=e[6],l=e[7],s=e[8],d=e[9],h=e[10],p=e[11],v=e[12],g=e[13],m=e[14],y=e[15],_=n[0],x=n[1],M=n[2],b=n[3];return t[0]=_*r+x*u+M*s+b*v,t[1]=_*o+x*c+M*d+b*g,t[2]=_*a+x*f+M*h+b*m,t[3]=_*i+x*l+M*p+b*y,_=n[4],x=n[5],M=n[6],b=n[7],t[4]=_*r+x*u+M*s+b*v,t[5]=_*o+x*c+M*d+b*g,t[6]=_*a+x*f+M*h+b*m,t[7]=_*i+x*l+M*p+b*y,_=n[8],x=n[9],M=n[10],b=n[11],t[8]=_*r+x*u+M*s+b*v,t[9]=_*o+x*c+M*d+b*g,t[10]=_*a+x*f+M*h+b*m,t[11]=_*i+x*l+M*p+b*y,_=n[12],x=n[13],M=n[14],b=n[15],t[12]=_*r+x*u+M*s+b*v,t[13]=_*o+x*c+M*d+b*g,t[14]=_*a+x*f+M*h+b*m,t[15]=_*i+x*l+M*p+b*y,t},o.mul=o.multiply,o.translate=function(t,e,n){var r,o,a,i,u,c,f,l,s,d,h,p,v=n[0],g=n[1],m=n[2];return e===t?(t[12]=e[0]*v+e[4]*g+e[8]*m+e[12],t[13]=e[1]*v+e[5]*g+e[9]*m+e[13],t[14]=e[2]*v+e[6]*g+e[10]*m+e[14],t[15]=e[3]*v+e[7]*g+e[11]*m+e[15]):(r=e[0],o=e[1],a=e[2],i=e[3],u=e[4],c=e[5],f=e[6],l=e[7],s=e[8],d=e[9],h=e[10],p=e[11],t[0]=r,t[1]=o,t[2]=a,t[3]=i,t[4]=u,t[5]=c,t[6]=f,t[7]=l,t[8]=s,t[9]=d,t[10]=h,t[11]=p,t[12]=r*v+u*g+s*m+e[12],t[13]=o*v+c*g+d*m+e[13],t[14]=a*v+f*g+h*m+e[14],t[15]=i*v+l*g+p*m+e[15]),t},o.scale=function(t,e,n){var r=n[0],o=n[1],a=n[2];return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t[4]=e[4]*o,t[5]=e[5]*o,t[6]=e[6]*o,t[7]=e[7]*o,t[8]=e[8]*a,t[9]=e[9]*a,t[10]=e[10]*a, +t[11]=e[11]*a,t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15],t},o.rotate=function(t,e,n,o){var a,i,u,c,f,l,s,d,h,p,v,g,m,y,_,x,M,b,w,R,A,E,C,O,T=o[0],D=o[1],S=o[2],P=Math.sqrt(T*T+D*D+S*S);return Math.abs(P)c?(a.cross(t,e,o),a.length(t)<1e-6&&a.cross(t,n,o),a.normalize(t,t),u.setAxisAngle(r,t,Math.PI),r):c>.999999?(r[0]=0,r[1]=0,r[2]=0,r[3]=1,r):(a.cross(t,o,i),r[0]=t[0],r[1]=t[1],r[2]=t[2],r[3]=1+c,u.normalize(r,r))}}(),u.setAxes=function(){var t=o.create();return function(e,n,r,o){return t[0]=r[0],t[3]=r[1],t[6]=r[2],t[1]=o[0],t[4]=o[1],t[7]=o[2],t[2]=-n[0],t[5]=-n[1],t[8]=-n[2],u.normalize(e,u.fromMat3(e,t))}}(),u.clone=i.clone,u.fromValues=i.fromValues,u.copy=i.copy,u.set=i.set,u.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},u.setAxisAngle=function(t,e,n){n=.5*n;var r=Math.sin(n);return t[0]=r*e[0],t[1]=r*e[1],t[2]=r*e[2],t[3]=Math.cos(n),t},u.add=i.add,u.multiply=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3],u=n[0],c=n[1],f=n[2],l=n[3];return t[0]=r*l+i*u+o*f-a*c,t[1]=o*l+i*c+a*u-r*f,t[2]=a*l+i*f+r*c-o*u,t[3]=i*l-r*u-o*c-a*f,t},u.mul=u.multiply,u.scale=i.scale,u.rotateX=function(t,e,n){n*=.5;var r=e[0],o=e[1],a=e[2],i=e[3],u=Math.sin(n),c=Math.cos(n);return t[0]=r*c+i*u,t[1]=o*c+a*u,t[2]=a*c-o*u,t[3]=i*c-r*u,t},u.rotateY=function(t,e,n){n*=.5;var r=e[0],o=e[1],a=e[2],i=e[3],u=Math.sin(n),c=Math.cos(n);return t[0]=r*c-a*u,t[1]=o*c+i*u,t[2]=a*c+r*u,t[3]=i*c-o*u,t},u.rotateZ=function(t,e,n){n*=.5;var r=e[0],o=e[1],a=e[2],i=e[3],u=Math.sin(n),c=Math.cos(n);return t[0]=r*c+o*u,t[1]=o*c-r*u,t[2]=a*c+i*u,t[3]=i*c-a*u,t},u.calculateW=function(t,e){var n=e[0],r=e[1],o=e[2];return t[0]=n,t[1]=r,t[2]=o,t[3]=Math.sqrt(Math.abs(1-n*n-r*r-o*o)),t},u.dot=i.dot,u.lerp=i.lerp,u.slerp=function(t,e,n,r){var o,a,i,u,c,f=e[0],l=e[1],s=e[2],d=e[3],h=n[0],p=n[1],v=n[2],g=n[3];return a=f*h+l*p+s*v+d*g,0>a&&(a=-a,h=-h,p=-p,v=-v,g=-g),1-a>1e-6?(o=Math.acos(a),i=Math.sin(o),u=Math.sin((1-r)*o)/i,c=Math.sin(r*o)/i):(u=1-r,c=r),t[0]=u*f+c*h,t[1]=u*l+c*p,t[2]=u*s+c*v,t[3]=u*d+c*g,t},u.sqlerp=function(){var t=u.create(),e=u.create();return function(n,r,o,a,i,c){return u.slerp(t,r,i,c),u.slerp(e,o,a,c),u.slerp(n,t,e,2*c*(1-c)),n}}(),u.invert=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=n*n+r*r+o*o+a*a,u=i?1/i:0;return t[0]=-n*u,t[1]=-r*u,t[2]=-o*u,t[3]=a*u,t},u.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},u.length=i.length,u.len=u.length,u.squaredLength=i.squaredLength,u.sqrLen=u.squaredLength,u.normalize=i.normalize,u.fromMat3=function(t,e){var n,r=e[0]+e[4]+e[8];if(r>0)n=Math.sqrt(r+1),t[3]=.5*n,n=.5/n,t[0]=(e[5]-e[7])*n,t[1]=(e[6]-e[2])*n,t[2]=(e[1]-e[3])*n;else{var o=0;e[4]>e[0]&&(o=1),e[8]>e[3*o+o]&&(o=2);var a=(o+1)%3,i=(o+2)%3;n=Math.sqrt(e[3*o+o]-e[3*a+a]-e[3*i+i]+1),t[o]=.5*n,n=.5/n,t[3]=(e[3*a+i]-e[3*i+a])*n,t[a]=(e[3*a+o]+e[3*o+a])*n,t[i]=(e[3*i+o]+e[3*o+i])*n}return t},u.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=u},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t},o.clone=function(t){var e=new r.ARRAY_TYPE(3);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e},o.fromValues=function(t,e,n){var o=new r.ARRAY_TYPE(3);return o[0]=t,o[1]=e,o[2]=n,o},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t},o.set=function(t,e,n,r){return t[0]=e,t[1]=n,t[2]=r,t},o.add=function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t[2]=e[2]+n[2],t},o.subtract=function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t[2]=e[2]-n[2],t},o.sub=o.subtract,o.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t[2]=e[2]*n[2],t},o.mul=o.multiply,o.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t[2]=e[2]/n[2],t},o.div=o.divide,o.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t[2]=Math.min(e[2],n[2]),t},o.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t[2]=Math.max(e[2],n[2]),t},o.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t[2]=e[2]*n,t},o.scaleAndAdd=function(t,e,n,r){return t[0]=e[0]+n[0]*r,t[1]=e[1]+n[1]*r,t[2]=e[2]+n[2]*r,t},o.distance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1],o=e[2]-t[2];return Math.sqrt(n*n+r*r+o*o)},o.dist=o.distance,o.squaredDistance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1],o=e[2]-t[2];return n*n+r*r+o*o},o.sqrDist=o.squaredDistance,o.length=function(t){var e=t[0],n=t[1],r=t[2];return Math.sqrt(e*e+n*n+r*r)},o.len=o.length,o.squaredLength=function(t){var e=t[0],n=t[1],r=t[2];return e*e+n*n+r*r},o.sqrLen=o.squaredLength,o.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t},o.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t[2]=1/e[2],t},o.normalize=function(t,e){var n=e[0],r=e[1],o=e[2],a=n*n+r*r+o*o;return a>0&&(a=1/Math.sqrt(a),t[0]=e[0]*a,t[1]=e[1]*a,t[2]=e[2]*a),t},o.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},o.cross=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=n[0],u=n[1],c=n[2];return t[0]=o*c-a*u,t[1]=a*i-r*c,t[2]=r*u-o*i,t},o.lerp=function(t,e,n,r){var o=e[0],a=e[1],i=e[2];return t[0]=o+r*(n[0]-o),t[1]=a+r*(n[1]-a),t[2]=i+r*(n[2]-i),t},o.hermite=function(t,e,n,r,o,a){var i=a*a,u=i*(2*a-3)+1,c=i*(a-2)+a,f=i*(a-1),l=i*(3-2*a);return t[0]=e[0]*u+n[0]*c+r[0]*f+o[0]*l,t[1]=e[1]*u+n[1]*c+r[1]*f+o[1]*l,t[2]=e[2]*u+n[2]*c+r[2]*f+o[2]*l,t},o.bezier=function(t,e,n,r,o,a){var i=1-a,u=i*i,c=a*a,f=u*i,l=3*a*u,s=3*c*i,d=c*a;return t[0]=e[0]*f+n[0]*l+r[0]*s+o[0]*d,t[1]=e[1]*f+n[1]*l+r[1]*s+o[1]*d,t[2]=e[2]*f+n[2]*l+r[2]*s+o[2]*d,t},o.random=function(t,e){e=e||1;var n=2*r.RANDOM()*Math.PI,o=2*r.RANDOM()-1,a=Math.sqrt(1-o*o)*e;return t[0]=Math.cos(n)*a,t[1]=Math.sin(n)*a,t[2]=o*e,t},o.transformMat4=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=n[3]*r+n[7]*o+n[11]*a+n[15];return i=i||1,t[0]=(n[0]*r+n[4]*o+n[8]*a+n[12])/i,t[1]=(n[1]*r+n[5]*o+n[9]*a+n[13])/i,t[2]=(n[2]*r+n[6]*o+n[10]*a+n[14])/i,t},o.transformMat3=function(t,e,n){var r=e[0],o=e[1],a=e[2];return t[0]=r*n[0]+o*n[3]+a*n[6],t[1]=r*n[1]+o*n[4]+a*n[7],t[2]=r*n[2]+o*n[5]+a*n[8],t},o.transformQuat=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=n[0],u=n[1],c=n[2],f=n[3],l=f*r+u*a-c*o,s=f*o+c*r-i*a,d=f*a+i*o-u*r,h=-i*r-u*o-c*a;return t[0]=l*f+h*-i+s*-c-d*-u,t[1]=s*f+h*-u+d*-i-l*-c,t[2]=d*f+h*-c+l*-u-s*-i,t},o.rotateX=function(t,e,n,r){var o=[],a=[];return o[0]=e[0]-n[0],o[1]=e[1]-n[1],o[2]=e[2]-n[2],a[0]=o[0],a[1]=o[1]*Math.cos(r)-o[2]*Math.sin(r),a[2]=o[1]*Math.sin(r)+o[2]*Math.cos(r),t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},o.rotateY=function(t,e,n,r){var o=[],a=[];return o[0]=e[0]-n[0],o[1]=e[1]-n[1],o[2]=e[2]-n[2],a[0]=o[2]*Math.sin(r)+o[0]*Math.cos(r),a[1]=o[1],a[2]=o[2]*Math.cos(r)-o[0]*Math.sin(r),t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},o.rotateZ=function(t,e,n,r){var o=[],a=[];return o[0]=e[0]-n[0],o[1]=e[1]-n[1],o[2]=e[2]-n[2],a[0]=o[0]*Math.cos(r)-o[1]*Math.sin(r),a[1]=o[0]*Math.sin(r)+o[1]*Math.cos(r),a[2]=o[2],t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},o.forEach=function(){var t=o.create();return function(e,n,r,o,a,i){var u,c;for(n||(n=3),r||(r=0),c=o?Math.min(o*n+r,e.length):e.length,u=r;c>u;u+=n)t[0]=e[u],t[1]=e[u+1],t[2]=e[u+2],a(t,t,i),e[u]=t[0],e[u+1]=t[1],e[u+2]=t[2];return e}}(),o.angle=function(t,e){var n=o.fromValues(t[0],t[1],t[2]),r=o.fromValues(e[0],e[1],e[2]);o.normalize(n,n),o.normalize(r,r);var a=o.dot(n,r);return a>1?0:Math.acos(a)},o.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},t.exports=o},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},o.clone=function(t){var e=new r.ARRAY_TYPE(4);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e},o.fromValues=function(t,e,n,o){var a=new r.ARRAY_TYPE(4);return a[0]=t,a[1]=e,a[2]=n,a[3]=o,a},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},o.set=function(t,e,n,r,o){return t[0]=e,t[1]=n,t[2]=r,t[3]=o,t},o.add=function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t[2]=e[2]+n[2],t[3]=e[3]+n[3],t},o.subtract=function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t[2]=e[2]-n[2],t[3]=e[3]-n[3],t},o.sub=o.subtract,o.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t[2]=e[2]*n[2],t[3]=e[3]*n[3],t},o.mul=o.multiply,o.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t[2]=e[2]/n[2],t[3]=e[3]/n[3],t},o.div=o.divide,o.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t[2]=Math.min(e[2],n[2]),t[3]=Math.min(e[3],n[3]),t},o.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t[2]=Math.max(e[2],n[2]),t[3]=Math.max(e[3],n[3]),t},o.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t[2]=e[2]*n,t[3]=e[3]*n,t},o.scaleAndAdd=function(t,e,n,r){return t[0]=e[0]+n[0]*r,t[1]=e[1]+n[1]*r,t[2]=e[2]+n[2]*r,t[3]=e[3]+n[3]*r,t},o.distance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1],o=e[2]-t[2],a=e[3]-t[3];return Math.sqrt(n*n+r*r+o*o+a*a)},o.dist=o.distance,o.squaredDistance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1],o=e[2]-t[2],a=e[3]-t[3];return n*n+r*r+o*o+a*a},o.sqrDist=o.squaredDistance,o.length=function(t){var e=t[0],n=t[1],r=t[2],o=t[3];return Math.sqrt(e*e+n*n+r*r+o*o)},o.len=o.length,o.squaredLength=function(t){var e=t[0],n=t[1],r=t[2],o=t[3];return e*e+n*n+r*r+o*o},o.sqrLen=o.squaredLength,o.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=-e[3],t},o.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t[2]=1/e[2],t[3]=1/e[3],t},o.normalize=function(t,e){var n=e[0],r=e[1],o=e[2],a=e[3],i=n*n+r*r+o*o+a*a;return i>0&&(i=1/Math.sqrt(i),t[0]=n*i,t[1]=r*i,t[2]=o*i,t[3]=a*i),t},o.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},o.lerp=function(t,e,n,r){var o=e[0],a=e[1],i=e[2],u=e[3];return t[0]=o+r*(n[0]-o),t[1]=a+r*(n[1]-a),t[2]=i+r*(n[2]-i),t[3]=u+r*(n[3]-u),t},o.random=function(t,e){return e=e||1,t[0]=r.RANDOM(),t[1]=r.RANDOM(),t[2]=r.RANDOM(),t[3]=r.RANDOM(),o.normalize(t,t),o.scale(t,t,e),t},o.transformMat4=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=e[3];return t[0]=n[0]*r+n[4]*o+n[8]*a+n[12]*i,t[1]=n[1]*r+n[5]*o+n[9]*a+n[13]*i,t[2]=n[2]*r+n[6]*o+n[10]*a+n[14]*i,t[3]=n[3]*r+n[7]*o+n[11]*a+n[15]*i,t},o.transformQuat=function(t,e,n){var r=e[0],o=e[1],a=e[2],i=n[0],u=n[1],c=n[2],f=n[3],l=f*r+u*a-c*o,s=f*o+c*r-i*a,d=f*a+i*o-u*r,h=-i*r-u*o-c*a;return t[0]=l*f+h*-i+s*-c-d*-u,t[1]=s*f+h*-u+d*-i-l*-c,t[2]=d*f+h*-c+l*-u-s*-i,t[3]=e[3],t},o.forEach=function(){var t=o.create();return function(e,n,r,o,a,i){var u,c;for(n||(n=4),r||(r=0),c=o?Math.min(o*n+r,e.length):e.length,u=r;c>u;u+=n)t[0]=e[u],t[1]=e[u+1],t[2]=e[u+2],t[3]=e[u+3],a(t,t,i),e[u]=t[0],e[u+1]=t[1],e[u+2]=t[2],e[u+3]=t[3];return e}}(),o.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=o},function(t,e,n){var r=n(8),o={};o.create=function(){var t=new r.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t},o.clone=function(t){var e=new r.ARRAY_TYPE(2);return e[0]=t[0],e[1]=t[1],e},o.fromValues=function(t,e){var n=new r.ARRAY_TYPE(2);return n[0]=t,n[1]=e,n},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t},o.set=function(t,e,n){return t[0]=e,t[1]=n,t},o.add=function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t},o.subtract=function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t},o.sub=o.subtract,o.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t},o.mul=o.multiply,o.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t},o.div=o.divide,o.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t},o.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t},o.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t},o.scaleAndAdd=function(t,e,n,r){return t[0]=e[0]+n[0]*r,t[1]=e[1]+n[1]*r,t},o.distance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1];return Math.sqrt(n*n+r*r)},o.dist=o.distance,o.squaredDistance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1];return n*n+r*r},o.sqrDist=o.squaredDistance,o.length=function(t){var e=t[0],n=t[1];return Math.sqrt(e*e+n*n)},o.len=o.length,o.squaredLength=function(t){var e=t[0],n=t[1];return e*e+n*n},o.sqrLen=o.squaredLength,o.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t},o.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t},o.normalize=function(t,e){var n=e[0],r=e[1],o=n*n+r*r;return o>0&&(o=1/Math.sqrt(o),t[0]=e[0]*o,t[1]=e[1]*o),t},o.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]},o.cross=function(t,e,n){var r=e[0]*n[1]-e[1]*n[0];return t[0]=t[1]=0,t[2]=r,t},o.lerp=function(t,e,n,r){var o=e[0],a=e[1];return t[0]=o+r*(n[0]-o),t[1]=a+r*(n[1]-a),t},o.random=function(t,e){e=e||1;var n=2*r.RANDOM()*Math.PI;return t[0]=Math.cos(n)*e,t[1]=Math.sin(n)*e,t},o.transformMat2=function(t,e,n){var r=e[0],o=e[1];return t[0]=n[0]*r+n[2]*o,t[1]=n[1]*r+n[3]*o,t},o.transformMat2d=function(t,e,n){var r=e[0],o=e[1];return t[0]=n[0]*r+n[2]*o+n[4],t[1]=n[1]*r+n[3]*o+n[5],t},o.transformMat3=function(t,e,n){var r=e[0],o=e[1];return t[0]=n[0]*r+n[3]*o+n[6],t[1]=n[1]*r+n[4]*o+n[7],t},o.transformMat4=function(t,e,n){var r=e[0],o=e[1];return t[0]=n[0]*r+n[4]*o+n[12],t[1]=n[1]*r+n[5]*o+n[13],t},o.forEach=function(){var t=o.create();return function(e,n,r,o,a,i){var u,c;for(n||(n=2),r||(r=0),c=o?Math.min(o*n+r,e.length):e.length,u=r;c>u;u+=n)t[0]=e[u],t[1]=e[u+1],a(t,t,i),e[u]=t[0],e[u+1]=t[1];return e}}(),o.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},t.exports=o},function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e["default"]={init:function(t,e){for(var n=t.length;n--;)t[n]=e},shuffle:function(t){var e,n,r=t.length-1;for(r;r>=0;r--)e=Math.floor(Math.random()*r),n=t[r],t[r]=t[e],t[e]=n;return t},toPointList:function(t){var e,n,r=[],o=[];for(e=0;e=e&&o.push(t[r]);return o},maxIndex:function(t){var e,n=0;for(e=0;et[n]&&(n=e);return n},max:function n(t){var e,n=0;for(e=0;en&&(n=t[e]);return n},sum:function r(t){for(var e=t.length,r=0;e--;)r+=t[e];return r}},t.exports=e["default"]},function(t,e,n){(function(r){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function a(){var t;m=g.halfSample?new T["default"]({x:E.size.x/2|0,y:E.size.y/2|0}):E,A=S["default"].calculatePatchSize(g.patchSize,m.size),Y.x=m.size.x/A.x|0,Y.y=m.size.y/A.y|0,R=new T["default"](m.size,void 0,Uint8Array,!1),x=new T["default"](A,void 0,Array,!0),t=new ArrayBuffer(65536),_=new T["default"](A,new Uint8Array(t,0,A.x*A.y)),y=new T["default"](A,new Uint8Array(t,A.x*A.y*3,A.x*A.y),void 0,!0),C=j["default"]("undefined"!=typeof window?window:"undefined"!=typeof self?self:r,{size:A.x},t),w=new T["default"]({x:m.size.x/_.size.x|0,y:m.size.y/_.size.y|0},void 0,Array,!0),M=new T["default"](w.size,void 0,void 0,!0),b=new T["default"](w.size,void 0,Int32Array,!0)}function i(){g.useWorker||"undefined"==typeof document||(G.dom.binary=document.createElement("canvas"),G.dom.binary.className="binaryBuffer",g.showCanvas===!0&&document.querySelector("#debug").appendChild(G.dom.binary),G.ctx.binary=G.dom.binary.getContext("2d"),G.dom.binary.width=R.size.x,G.dom.binary.height=R.size.y)}function u(t){var e,n,r,o,a,i,u,c=R.size.x,f=R.size.y,l=-R.size.x,s=-R.size.y;for(e=0,n=0;ne&&(e+=180),e=(180-e)*Math.PI/180,a=H.clone([Math.cos(e),Math.sin(e),-Math.sin(e),Math.cos(e)]),n=0;nr;r++)V.transformMat2(o.box[r],o.box[r],a);g.boxFromPatches.showTransformed&&W["default"].drawPath(o.box,{x:0,y:1},G.ctx.binary,{color:"#99ff00",lineWidth:2})}for(n=0;nr;r++)o.box[r][0]l&&(l=o.box[r][0]),o.box[r][1]s&&(s=o.box[r][1]);for(i=[[c,f],[l,f],[l,s],[c,s]],g.boxFromPatches.showTransformedBox&&W["default"].drawPath(i,{x:0,y:1},G.ctx.binary,{color:"#ff0000",lineWidth:2}),u=g.halfSample?2:1,a=H.invert(a,a),r=0;4>r;r++)V.transformMat2(i[r],i[r],a);for(g.boxFromPatches.showBB&&W["default"].drawPath(i,{x:0,y:1},G.ctx.binary,{color:"#ff0000",lineWidth:2}),r=0;4>r;r++)V.scale(i[r],i[r],u);return i}function c(){S["default"].otsuThreshold(m,R),R.zeroBorder(),g.showCanvas&&R.show(G.dom.binary,255)}function f(){var t,e,n,r,o,a,i,u,c=[];for(t=0;te;e++)r.push(0);for(n=b.data.length;n--;)b.data[n]>0&&r[b.data[n]-1]++;return r=r.map(function(t,e){return{val:t,label:e+1}}),r.sort(function(t,e){return e.val-t.val}),o=r.filter(function(t){return t.val>=5})}function s(t,e){var n,r,o,a,i,c=[],f=[],l=[0,1,1],s=[0,0,0];for(n=0;n=2){for(o=0;ol&&c.push(t[o]);if(c.length>=2){for(i=d(c),a=0,o=0;o1&&i.length>=c.length/4*3&&i.length>t.length/4&&(a/=i.length,u={index:e[1]*Y.x+e[0],pos:{x:n,y:r},box:[V.clone([n,r]),V.clone([n+_.size.x,r]),V.clone([n+_.size.x,r+_.size.y]),V.clone([n,r+_.size.y])],moments:i,rad:a,vec:V.clone([Math.cos(a),Math.sin(a)])},f.push(u))}}return f}function v(t){function e(){var t;for(t=0;ti&&n(u)):b.data[u]=Number.MAX_VALUE}var r,o,a=0,i=.95,u=0,c=[0,1,1],f=[0,0,0];for(q["default"].init(M.data,0),q["default"].init(b.data,0),q["default"].init(w.data,null),r=0;r0&&b.data[r]<=a&&(o=w.data[r],c[0]=b.data[r]/(a+1)*360,S["default"].hsv2rgb(c,f),W["default"].drawRect(o.pos,_.size,G.ctx.binary,{color:"rgb("+f.join(",")+")",lineWidth:2}));return a}Object.defineProperty(e,"__esModule",{value:!0});var g,m,y,_,x,M,b,w,R,A,E,C,O=n(3),T=o(O),D=n(5),S=o(D),P=n(19),z=o(P),I=n(20),N=o(I),L=n(21),j=o(L),k=n(17),q=o(k),U=n(22),W=o(U),F=n(7),B=o(F),G={ctx:{binary:null},dom:{binary:null}},Y={x:0,y:0},V=B["default"].vec2,H=B["default"].mat2;e["default"]={init:function(t,e){g=e,E=t,a(),i()},locate:function(){var t,e,n;if(g.halfSample&&S["default"].halfSample(E,m),c(),t=f(),t.lengthr?null:(e=l(r),0===e.length?null:n=s(e,r))},checkImageConstraints:function(t,e){var n,r,o,a=t.getWidth(),i=t.getHeight(),u=e.halfSample?.5:1;if(t.getConfig().area&&(o=S["default"].computeImageArea(a,i,t.getConfig().area),t.setTopRight({x:o.sx,y:o.sy}),t.setCanvasSize({x:a,y:i}),a=o.sw,i=o.sh),r={x:Math.floor(a*u),y:Math.floor(i*u)},n=S["default"].calculatePatchSize(e.patchSize,r),console.log("Patch-Size: "+JSON.stringify(n)),t.setWidth(Math.floor(Math.floor(r.x/n.x)*(1/u)*n.x)),t.setHeight(Math.floor(Math.floor(r.y/n.y)*(1/u)*n.y)),t.getWidth()%n.x===0&&t.getHeight()%n.y===0)return!0;throw new Error("Image dimensions do not comply with the current settings: Width ("+a+" )and height ("+i+") must a multiple of "+n.x)}},t.exports=e["default"]}).call(e,function(){return this}())},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(20),a=r(o),i={createContour2D:function(){return{dir:null,index:null,firstVertex:null,insideContours:null,nextpeer:null,prevpeer:null}},CONTOUR_DIR:{CW_DIR:0,CCW_DIR:1,UNKNOWN_DIR:2},DIR:{OUTSIDE_EDGE:-32767,INSIDE_EDGE:-32766},create:function(t,e){var n=t.data,r=e.data,o=t.size.x,u=t.size.y,c=a["default"].create(t,e);return{rasterize:function(t){var e,a,f,l,s,d,h,p,v,g,m,y,_=[],x=0;for(y=0;400>y;y++)_[y]=0;for(_[0]=n[0],v=null,d=1;u-1>d;d++)for(l=0,a=_[0],s=1;o-1>s;s++)if(m=d*o+s,0===r[m])if(e=n[m],e!==a){if(0===l)f=x+1,_[f]=e,a=e,h=c.contourTracing(d,s,f,e,i.DIR.OUTSIDE_EDGE),null!==h&&(x++,l=f,p=i.createContour2D(),p.dir=i.CONTOUR_DIR.CW_DIR,p.index=l,p.firstVertex=h,p.nextpeer=v,p.insideContours=null,null!==v&&(v.prevpeer=p),v=p);else if(h=c.contourTracing(d,s,i.DIR.INSIDE_EDGE,e,l),null!==h){for(p=i.createContour2D(),p.firstVertex=h,p.insideContours=null,0===t?p.dir=i.CONTOUR_DIR.CCW_DIR:p.dir=i.CONTOUR_DIR.CW_DIR,p.index=t,g=v;null!==g&&g.index!==l;)g=g.nextpeer;null!==g&&(p.nextpeer=g.insideContours,null!==g.insideContours&&(g.insideContours.prevpeer=p),g.insideContours=p)}}else r[m]=l;else r[m]===i.DIR.OUTSIDE_EDGE||r[m]===i.DIR.INSIDE_EDGE?(l=0,a=r[m]===i.DIR.INSIDE_EDGE?n[m]:_[0]):(l=r[m],a=_[l]);for(g=v;null!==g;)g.index=t,g=g.nextpeer;return{cc:v,count:x}},debug:{drawContour:function(t,e){var n,r,o,a=t.getContext("2d"),u=e;for(a.strokeStyle="red",a.fillStyle="red",a.lineWidth=1,n=null!==u?u.insideContours:null;null!==u;){switch(null!==n?(r=n,n=n.nextpeer):(r=u,u=u.nextpeer,n=null!==u?u.insideContours:null),r.dir){case i.CONTOUR_DIR.CW_DIR:a.strokeStyle="red";break;case i.CONTOUR_DIR.CCW_DIR:a.strokeStyle="blue";break;case i.CONTOUR_DIR.UNKNOWN_DIR:a.strokeStyle="green"}o=r.firstVertex,a.beginPath(),a.moveTo(o.x,o.y);do o=o.next,a.lineTo(o.x,o.y);while(o!==r.firstVertex);a.stroke()}}}}}};e["default"]=i,t.exports=e["default"]},function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={searchDirections:[[0,1],[1,1],[1,0],[1,-1],[0,-1],[-1,-1],[-1,0],[-1,1]],create:function(t,e){function n(t,e,n,r){var o,l,s;for(o=0;7>o;o++){if(l=t.cy+c[t.dir][0],s=t.cx+c[t.dir][1],a=l*f+s,i[a]===e&&(0===u[a]||u[a]===n))return u[a]=n,t.cy=l,t.cx=s,!0;0===u[a]&&(u[a]=r),t.dir=(t.dir+1)%8}return!1}function r(t,e,n){return{dir:n,x:t,y:e,next:null,prev:null}}function o(t,e,o,a,i){var u,c,f,l=null,s={cx:e,cy:t,dir:0};if(n(s,a,o,i)){l=r(e,t,s.dir),u=l,f=s.dir,c=r(s.cx,s.cy,0),c.prev=u,u.next=c,c.next=null,u=c;do s.dir=(s.dir+6)%8,n(s,a,o,i),f!==s.dir?(u.dir=s.dir,c=r(s.cx,s.cy,0),c.prev=u,u.next=c,c.next=null,u=c):(u.dir=f,u.x=s.cx,u.y=s.cy),f=s.dir;while(s.cx!==e||s.cy!==t);l.prev=u.prev,u.prev.next=l}return l}var a,i=t.data,u=e.data,c=this.searchDirections,f=t.size.x;return{trace:function(t,e,r,o){return n(t,e,r,o)},contourTracing:function(t,e,n,r,a){return o(t,e,n,r,a)}}}};e["default"]=n,t.exports=e["default"]},function(module, exports) {"use strict";Object.defineProperty(exports, "__esModule", {value: true});function Skeletonizer(stdlib, foreign, buffer) {"use asm";var images=new stdlib.Uint8Array(buffer),size=foreign.size|0,imul=stdlib.Math.imul;function erode(inImagePtr, outImagePtr) {inImagePtr=inImagePtr|0;outImagePtr=outImagePtr|0;var v=0,u=0,sum=0,yStart1=0,yStart2=0,xStart1=0,xStart2=0,offset=0;for (v=1; (v|0)<(size - 1|0); v=v+1|0) {offset=offset+size|0;for (u=1; (u|0)<(size - 1|0); u=u+1|0) {yStart1=offset - size|0;yStart2=offset+size|0;xStart1=u - 1|0;xStart2=u+1|0;sum=(images[inImagePtr+yStart1+xStart1|0]|0)+(images[inImagePtr+yStart1+xStart2|0]|0)+(images[inImagePtr+offset+u|0]|0)+(images[inImagePtr+yStart2+xStart1|0]|0)+(images[inImagePtr+yStart2+xStart2|0]|0)|0;if ((sum|0) == (5|0)) {images[outImagePtr+offset+u|0]=1;} else {images[outImagePtr+offset+u|0]=0;}}}return;}function subtract(aImagePtr, bImagePtr, outImagePtr) {aImagePtr=aImagePtr|0;bImagePtr=bImagePtr|0;outImagePtr=outImagePtr|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=length - 1|0;images[outImagePtr+length|0]=(images[aImagePtr+length|0]|0) - (images[bImagePtr+length|0]|0)|0;}}function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) {aImagePtr=aImagePtr|0;bImagePtr=bImagePtr|0;outImagePtr=outImagePtr|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=length - 1|0;images[outImagePtr+length|0]=images[aImagePtr+length|0]|0|(images[bImagePtr+length|0]|0)|0;}}function countNonZero(imagePtr) {imagePtr=imagePtr|0;var sum=0,length=0;length=imul(size, size)|0;while ((length|0)>0) {length=length - 1|0;sum=(sum|0)+(images[imagePtr+length|0]|0)|0;}return sum|0;}function init(imagePtr, value) {imagePtr=imagePtr|0;value=value|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=length - 1|0;images[imagePtr+length|0]=value;}}function dilate(inImagePtr, outImagePtr) {inImagePtr=inImagePtr|0;outImagePtr=outImagePtr|0;var v=0,u=0,sum=0,yStart1=0,yStart2=0,xStart1=0,xStart2=0,offset=0;for (v=1; (v|0)<(size - 1|0); v=v+1|0) {offset=offset+size|0;for (u=1; (u|0)<(size - 1|0); u=u+1|0) {yStart1=offset - size|0;yStart2=offset+size|0;xStart1=u - 1|0;xStart2=u+1|0;sum=(images[inImagePtr+yStart1+xStart1|0]|0)+(images[inImagePtr+yStart1+xStart2|0]|0)+(images[inImagePtr+offset+u|0]|0)+(images[inImagePtr+yStart2+xStart1|0]|0)+(images[inImagePtr+yStart2+xStart2|0]|0)|0;if ((sum|0)>(0|0)) {images[outImagePtr+offset+u|0]=1;} else {images[outImagePtr+offset+u|0]=0;}}}return;}function memcpy(srcImagePtr, dstImagePtr) {srcImagePtr=srcImagePtr|0;dstImagePtr=dstImagePtr|0;var length=0;length=imul(size, size)|0;while ((length|0)>0) {length=length - 1|0;images[dstImagePtr+length|0]=images[srcImagePtr+length|0]|0;}}function zeroBorder(imagePtr) {imagePtr=imagePtr|0;var x=0,y=0;for (x=0; (x|0)<(size - 1|0); x=x+1|0) {images[imagePtr+x|0]=0;images[imagePtr+y|0]=0;y=y+size - 1|0;images[imagePtr+y|0]=0;y=y+1|0;}for (x=0; (x|0)<(size|0); x=x+1|0) {images[imagePtr+y|0]=0;y=y+1|0;}}function skeletonize() {var subImagePtr=0,erodedImagePtr=0,tempImagePtr=0,skelImagePtr=0,sum=0,done=0;erodedImagePtr=imul(size, size)|0;tempImagePtr=erodedImagePtr+erodedImagePtr|0;skelImagePtr=tempImagePtr+erodedImagePtr|0;init(skelImagePtr, 0);zeroBorder(subImagePtr);do {erode(subImagePtr, erodedImagePtr);dilate(erodedImagePtr, tempImagePtr);subtract(subImagePtr, tempImagePtr, tempImagePtr);bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr);memcpy(erodedImagePtr, subImagePtr);sum=countNonZero(subImagePtr)|0;done=(sum|0) == 0|0;} while (!done);}return {skeletonize: skeletonize};}exports["default"]=Skeletonizer;module.exports=exports["default"]; },function(t,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e["default"]={drawRect:function(t,e,n,r){n.strokeStyle=r.color,n.fillStyle=r.color,n.lineWidth=1,n.beginPath(),n.strokeRect(t.x,t.y,e.x,e.y)},drawPath:function(t,e,n,r){n.strokeStyle=r.color,n.fillStyle=r.color,n.lineWidth=r.lineWidth,n.beginPath(),n.moveTo(t[0][e.x],t[0][e.y]);for(var o=1;o1&&(!e.inImageWithBorder(t[0],0)||!e.inImageWithBorder(t[1],0));)r-=Math.ceil(r/2),o(-r);return t}function c(t){return[{x:(t[1][0]-t[0][0])/2+t[0][0],y:(t[1][1]-t[0][1])/2+t[0][1]},{x:(t[3][0]-t[2][0])/2+t[2][0],y:(t[3][1]-t[2][1])/2+t[2][1]}]}function f(n){var r,o=null,i=a["default"].getBarcodeLine(e,n[0],n[1]);for(t.showFrequency&&(u["default"].drawPath(n,{x:"x",y:"y"},h.ctx.overlay,{color:"red",lineWidth:3}),a["default"].debug.printFrequency(i.line,h.dom.frequency)),a["default"].toBinaryLine(i),t.showPattern&&a["default"].debug.printPattern(i.line,h.dom.pattern),r=0;rr&&null===c;r++)o=i/u*r*(r%2===0?-1:1),a={y:o*l,x:o*s},e[0].y+=a.x,e[0].x-=a.y,e[1].y+=a.x,e[1].x-=a.y,c=f(e);return c}function s(t){return Math.sqrt(Math.pow(Math.abs(t[1].y-t[0].y),2)+Math.pow(Math.abs(t[1].x-t[0].x),2))}function d(e){var n,r,o,a,d=h.ctx.overlay;return t.drawBoundingBox&&d&&u["default"].drawPath(e,{x:0,y:1},d,{color:"blue",lineWidth:2}),n=c(e),a=s(n),r=Math.atan2(n[1].y-n[0].y,n[1].x-n[0].x),n=i(n,r,Math.floor(.1*a)),null===n?null:(o=f(n),null===o&&(o=l(e,n,r)),null===o?null:(o&&t.drawScanline&&d&&u["default"].drawPath(n,{x:"x",y:"y"},d,{color:"red",lineWidth:3}),{codeResult:o.codeResult,line:n,angle:r,pattern:o.barcodeLine.line,threshold:o.barcodeLine.threshold}))}var h={ctx:{frequency:null,pattern:null,overlay:null},dom:{frequency:null,pattern:null,overlay:null}},p=[];return n(),r(),o(),{decodeFromBoundingBox:function(t){return d(t)},decodeFromBoundingBoxes:function(t){var e,n;for(e=0;es?s:M,b=s>b?s:b,m.push(s)}var o,a,i,u,c,f,l,s,d=0|e.x,h=0|e.y,p=0|n.x,v=0|n.y,g=Math.abs(v-h)>Math.abs(p-d),m=[],y=t.data,_=t.size.x,x=0,M=255,b=0;for(g&&(f=d,d=h,h=f,f=p,p=v,v=f),d>p&&(f=d,d=p,p=f,f=h,h=v,v=f),o=p-d,a=Math.abs(v-h),i=o/2|0,c=h,u=v>h?1:-1,l=d;p>l;l++)g?r(c,l):r(l,c),i-=a,0>i&&(c+=u,i+=o);return{line:m,min:M,max:b}},c.toOtsuBinaryLine=function(t){var e=t.line,n=new u["default"]({x:e.length-1,y:1},e),r=a["default"].determineOtsuThreshold(n,5);return e=a["default"].sharpenLine(e),a["default"].thresholdImage(n,r),{line:e,threshold:r}},c.toBinaryLine=function(t){var e,n,r,o,a,i,u=t.min,c=t.max,l=t.line,s=u+(c-u)/2,d=[],h=(c-u)/12,p=-h;for(r=l[0]>s?f.DIR.UP:f.DIR.DOWN,d.push({pos:0,val:l[0]}),a=0;ae+n&&l[a+1]<1.5*s?f.DIR.DOWN:e+n>h&&l[a+1]>.5*s?f.DIR.UP:r,r!==o&&(d.push({pos:a,val:l[a]}),r=o);for(d.push({pos:l.length,val:l[l.length-1]}),i=d[0].pos;is?0:1;for(a=1;ad[a].val?d[a].val+(d[a+1].val-d[a].val)/3*2|0:d[a+1].val+(d[a].val-d[a+1].val)/3|0,i=d[a].pos;ih?0:1;return{line:l,threshold:h}},c.debug={printFrequency:function(t,e){var n,r=e.getContext("2d");for(e.width=t.length,e.height=256,r.beginPath(),r.strokeStyle="blue",n=0;nr;r++)i[r]=i[r+2];i[4]=0,i[5]=0,l--}else l++;i[l]=1,f=!f}return null},o.prototype._decode=function(){var t,e,n=this,r=n._findStart(),o=null,a=!1,i=[],u=0,c=0,f=[],l=[],s=!1;if(null===r)return null;switch(o={code:r.code,start:r.start,end:r.end},l.push(o),c=o.code,o.code){case n.START_CODE_A:t=n.CODE_A;break;case n.START_CODE_B:t=n.CODE_B;break;case n.START_CODE_C:t=n.CODE_C;break;default:return null}for(;!a;){if(e=s,s=!1,o=n._decodeCode(o.end),null!==o)switch(o.code!==n.STOP_CODE&&(f.push(o.code),u++,c+=u*o.code),l.push(o),t){case n.CODE_A:if(o.code<64)i.push(String.fromCharCode(32+o.code));else if(o.code<96)i.push(String.fromCharCode(o.code-64));else switch(o.code){case n.CODE_SHIFT:s=!0,t=n.CODE_B;break;case n.CODE_B:t=n.CODE_B;break;case n.CODE_C:t=n.CODE_C;break;case n.STOP_CODE:a=!0}break;case n.CODE_B:if(o.code<96)i.push(String.fromCharCode(32+o.code));else switch(o.code){case n.CODE_SHIFT:s=!0,t=n.CODE_A;break;case n.CODE_A:t=n.CODE_A;break;case n.CODE_C:t=n.CODE_C;break;case n.STOP_CODE:a=!0}break;case n.CODE_C:switch(o.code<100&&i.push(o.code<10?"0"+o.code:o.code),o.code){case n.CODE_A:t=n.CODE_A;break;case n.CODE_B:t=n.CODE_B;break;case n.STOP_CODE:a=!0}}else a=!0;e&&(t=t===n.CODE_A?n.CODE_B:n.CODE_A)}return null===o?null:(o.end=n._nextUnset(n._row,o.end),n._verifyTrailingWhitespace(o)?(c-=u*f[f.length-1],c%103!==f[f.length-1]?null:i.length?(i.splice(i.length-1,1),{code:i.join(""),start:r.start,end:o.end,codeset:t,startInfo:r,decodedCodes:l,endInfo:o}):null):null)},i["default"].prototype._verifyTrailingWhitespace=function(t){var e,n=this;return e=t.end+(t.end-t.start)/2,ei)return Number.MAX_VALUE;r+=o}return r/a},n.prototype._nextSet=function(t,e){var n;for(e=e||0,n=e;n1)for(n=0;nr?(f.start=n-i,f.end=n,f.counter=o,f):null;c++,o[c]=1,u=!u}}else for(o.push(0),n=i;nt?0:t,r=t;e>r;r++)if(this._row[r]!==n)return!1;return!0},n.prototype._fillCounters=function(t,e,n){var r,o=this,a=0,i=[];for(n="undefined"!=typeof n?n:!0,t="undefined"!=typeof t?t:o._nextUnset(o._row),e=e||o._row.length,i[a]=0,r=t;e>r;r++)o._row[r]^n?i[a]++:(a++,i[a]=1,n=!n);return i},Object.defineProperty(n.prototype,"FORMAT",{value:"unknown",writeable:!1}),n.DIRECTION={FORWARD:1,REVERSE:-1},n.Exception={StartNotFoundException:"Start-Info was not found!",CodeNotFoundException:"Code could not be found!",PatternNotFoundException:"Pattern could not be found!"},n.CONFIG_KEYS={},e["default"]=n,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t){i["default"].call(this,t)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(26),i=r(a),u={CODE_L_START:{value:0},MODULO:{value:7},CODE_G_START:{value:10},START_PATTERN:{value:[1/3*7,1/3*7,1/3*7]},STOP_PATTERN:{value:[1/3*7,1/3*7,1/3*7]},MIDDLE_PATTERN:{value:[.2*7,.2*7,.2*7,.2*7,.2*7]},CODE_PATTERN:{value:[[3,2,1,1],[2,2,2,1],[2,1,2,2],[1,4,1,1],[1,1,3,2],[1,2,3,1],[1,1,1,4],[1,3,1,2],[1,2,1,3],[3,1,1,2],[1,1,2,3],[1,2,2,2],[2,2,1,2],[1,1,4,1],[2,3,1,1],[1,3,2,1],[4,1,1,1],[2,1,3,1],[3,1,2,1],[2,1,1,3]]},CODE_FREQUENCY:{value:[0,11,13,14,19,25,28,21,22,26]},SINGLE_CODE_ERROR:{value:.67},AVG_CODE_ERROR:{value:.27},FORMAT:{value:"ean_13",writeable:!1}};o.prototype=Object.create(i["default"].prototype,u),o.prototype.constructor=o,o.prototype._decodeCode=function(t,e){var n,r,o,a,i=[0,0,0,0],u=this,c=t,f=!u._row[c],l=0,s={error:Number.MAX_VALUE,code:-1,start:t,end:t};for(e||(e=u.CODE_PATTERN.length),n=c;nr;r++)o=u._matchPattern(a,u.CODE_PATTERN[r]),ou.AVG_CODE_ERROR?null:s}}else l++;i[l]=1,f=!f}return null},o.prototype._findPattern=function(t,e,n,r,o){var a,i,u,c,f,l=[],s=this,d=0,h={error:Number.MAX_VALUE,code:-1,start:0,end:0};for(e||(e=s._nextSet(s._row)),void 0===n&&(n=!1),void 0===r&&(r=!0),void 0===o&&(o=s.AVG_CODE_ERROR),a=0;ai))return h.error=i,h.start=a-c,h.end=a,h;if(!r)return null;for(u=0;u=0&&n._matchRange(t,e.start,0))return e;r=e.end,e=null}},o.prototype._verifyTrailingWhitespace=function(t){var e,n=this;return e=t.end+(t.end-t.start),er;r++){if(t=a._decodeCode(t.end),!t)return null;t.code>=a.CODE_G_START?(t.code=t.code-a.CODE_G_START,i|=1<<5-r):i|=0<<5-r,e.push(t.code),n.push(t)}if(o=a._calculateFirstDigit(i),null===o)return null;if(e.unshift(o),t=a._findPattern(a.MIDDLE_PATTERN,t.end,!0,!1),null===t)return null;for(n.push(t),r=0;6>r;r++){if(t=a._decodeCode(t.end,a.CODE_G_START),!t)return null;n.push(t),e.push(t.code)}return t},o.prototype._decode=function(){var t,e,n=this,r=[],o=[];return(t=n._findStart())?(e={code:t.code,start:t.start,end:t.end},o.push(e),(e=n._decodePayload(e,r,o))&&(e=n._findEnd(e.end,!1))?(o.push(e),n._checksum(r)?{code:r.join(""),start:t.start,end:e.end,codeset:"",startInfo:t,decodedCodes:o}:null):null):null},o.prototype._checksum=function(t){var e,n=0;for(e=t.length-2;e>=0;e-=2)n+=t[e];for(n*=3,e=t.length-1;e>=0;e-=2)n+=t[e];return n%10===0},e["default"]=o,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(){i["default"].call(this)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(26),i=r(a),u=n(17),c=r(u),f={ALPHABETH_STRING:{value:"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"},ALPHABET:{value:[48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,45,46,32,42,36,47,43,37]},CHARACTER_ENCODINGS:{value:[52,289,97,352,49,304,112,37,292,100,265,73,328,25,280,88,13,268,76,28,259,67,322,19,274,82,7,262,70,22,385,193,448,145,400,208,133,388,196,148,168,162,138,42]},ASTERISK:{value:148},FORMAT:{value:"code_39",writeable:!1}};o.prototype=Object.create(i["default"].prototype,f),o.prototype.constructor=o,o.prototype._toCounters=function(t,e){var n,r=this,o=e.length,a=r._row.length,i=!r._row[t],u=0;for(c["default"].init(e,0),n=t;a>n;n++)if(r._row[n]^i)e[u]++;else{if(u++,u===o)break;e[u]=1,i=!i}return e},o.prototype._decode=function(){var t,e,n,r,o=this,a=[0,0,0,0,0,0,0,0,0],i=[],u=o._findStart();if(!u)return null;r=o._nextSet(o._row,u.end);do{if(a=o._toCounters(r,a),n=o._toPattern(a),0>n)return null;if(t=o._patternToChar(n),0>t)return null;i.push(t),e=r,r+=c["default"].sum(a),r=o._nextSet(o._row,r)}while("*"!==t);return i.pop(),i.length&&o._verifyTrailingWhitespace(e,r,a)?{code:i.join(""),start:u.start,end:r,startInfo:u,decodedCodes:i}:null},o.prototype._verifyTrailingWhitespace=function(t,e,n){var r,o=c["default"].sum(n);return r=e-t-o,3*r>=o?!0:!1},o.prototype._patternToChar=function(t){var e,n=this;for(e=0;ee&&(r=t[n]);return r},o.prototype._toPattern=function(t){for(var e,n,r=t.length,o=0,a=r,i=0,u=this;a>3;){for(o=u._findNextWidth(t,o),a=0,e=0,n=0;r>n;n++)t[n]>o&&(e|=1<n&&a>0;n++)if(t[n]>o&&(a--,2*t[n]>=i))return-1;return e}}return-1},o.prototype._findStart=function(){var t,e,n,r=this,o=r._nextSet(r._row),a=o,i=[0,0,0,0,0,0,0,0,0],u=0,c=!1;for(t=o;te;e++)i[e]=i[e+2];i[7]=0,i[8]=0,u--}else u++;i[u]=1,c=!c}return null},e["default"]=o,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(){i["default"].call(this)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(28),i=r(a),u={IOQ:/[IOQ]/g,AZ09:/[A-Z0-9]{17}/};o.prototype=Object.create(i["default"].prototype),o.prototype.constructor=o,o.prototype._decode=function(){var t=i["default"].prototype._decode.apply(this);if(!t)return null;var e=t.code;return e?(e=e.replace(u.IOQ,""),e.match(u.AZ09)?this._checkChecksum(e)?(t.code=e,t):null:(console.log("Failed AZ09 pattern code:",e),null)):null},o.prototype._checkChecksum=function(t){return!!t},e["default"]=o,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(){i["default"].call(this),this._counters=[]}Object.defineProperty(e,"__esModule",{value:!0});var a=n(26),i=r(a),u={ALPHABETH_STRING:{value:"0123456789-$:/.+ABCD"},ALPHABET:{value:[48,49,50,51,52,53,54,55,56,57,45,36,58,47,46,43,65,66,67,68]},CHARACTER_ENCODINGS:{value:[3,6,9,96,18,66,33,36,48,72,12,24,69,81,84,21,26,41,11,14]},START_END:{value:[26,41,11,14]},MIN_ENCODED_CHARS:{value:4},MAX_ACCEPTABLE:{value:2},PADDING:{value:1.5},FORMAT:{value:"codabar",writeable:!1}};o.prototype=Object.create(i["default"].prototype,u),o.prototype.constructor=o,o.prototype._decode=function(){var t,e,n,r,o,a=this,i=[];if(this._counters=a._fillCounters(),t=a._findStart(),!t)return null;r=t.startCounter;do{if(n=a._toPattern(r),0>n)return null;if(e=a._patternToChar(n),0>e)return null;if(i.push(e),r+=8,i.length>1&&a._isStartEnd(n))break}while(ra._counters.length?a._counters.length:r,o=t.start+a._sumCounters(t.startCounter,r-8),{code:i.join(""),start:t.start,end:o,startInfo:t,decodedCodes:i}):null},o.prototype._verifyWhitespace=function(t,e){return(0>=t-1||this._counters[t-1]>=this._calculatePatternLength(t)/2)&&(e+8>=this._counters.length||this._counters[e+7]>=this._calculatePatternLength(e)/2)?!0:!1},o.prototype._calculatePatternLength=function(t){var e,n=0;for(e=t;t+7>e;e++)n+=this._counters[e];return n},o.prototype._thresholdResultPattern=function(t,e){var n,r,o,a,i,u=this,c={space:{narrow:{size:0,counts:0,min:0,max:Number.MAX_VALUE},wide:{size:0,counts:0,min:0,max:Number.MAX_VALUE}},bar:{narrow:{size:0,counts:0,min:0,max:Number.MAX_VALUE},wide:{size:0,counts:0,min:0,max:Number.MAX_VALUE}}},f=e;for(o=0;o=0;a--)n=2===(1&a)?c.bar:c.space,r=1===(1&i)?n.wide:n.narrow,r.size+=u._counters[f+a],r.counts++,i>>=1;f+=8}return["space","bar"].forEach(function(t){var e=c[t];e.wide.min=Math.floor((e.narrow.size/e.narrow.counts+e.wide.size/e.wide.counts)/2),e.narrow.max=Math.ceil(e.wide.min),e.wide.max=Math.ceil((e.wide.size*u.MAX_ACCEPTABLE+u.PADDING)/e.wide.counts)}),c},o.prototype._charToPattern=function(t){var e,n=this,r=t.charCodeAt(0);for(e=0;e=0;r--){if(o=0===(1&r)?f.bar:f.space,a=1===(1&u)?o.wide:o.narrow,i=c._counters[l+r],ia.max)return!1;u>>=1}l+=8}return!0},o.prototype._patternToChar=function(t){var e,n=this;for(e=0;en;n+=2)r=this._counters[n],r>a&&(a=r),o>r&&(o=r);return(o+a)/2|0},o.prototype._toPattern=function(t){var e,n,r,o,a=7,i=t+a,u=1<this._counters.length)return-1;for(e=this._computeAlternatingThreshold(t,i),n=this._computeAlternatingThreshold(t+1,i),r=0;a>r;r++)o=0===(1&r)?e:n,this._counters[t+r]>o&&(c|=u),u>>=1;return c},o.prototype._isStartEnd=function(t){var e;for(e=0;en;n++)r+=this._counters[n];return r},o.prototype._findStart=function(){var t,e,n,r=this,o=r._nextUnset(r._row);for(t=1;tr;r++){if(t=o._decodeCode(t.end,o.CODE_G_START),!t)return null;e.push(t.code),n.push(t)}if(t=o._findPattern(o.MIDDLE_PATTERN,t.end,!0,!1),null===t)return null;for(n.push(t),r=0;4>r;r++){if(t=o._decodeCode(t.end,o.CODE_G_START),!t)return null;n.push(t),e.push(t.code)}return t},e["default"]=o,t.exports=e["default"]},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(){i["default"].call(this)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(27),i=r(a),u={CODE_FREQUENCY:{value:[[56,52,50,49,44,38,35,42,41,37],[7,11,13,14,19,25,28,21,22,26]]},STOP_PATTERN:{value:[1/6*7,1/6*7,1/6*7,1/6*7,1/6*7,1/6*7]},FORMAT:{value:"upc_e",writeable:!1}};o.prototype=Object.create(i["default"].prototype,u),o.prototype.constructor=o,o.prototype._decodePayload=function(t,e,n){var r,o=this,a=0;for(r=0;6>r;r++){if(t=o._decodeCode(t.end),!t)return null;t.code>=o.CODE_G_START&&(t.code=t.code-o.CODE_G_START,a|=1<<5-r),e.push(t.code),n.push(t)}return o._determineParity(a,e)?t:null},o.prototype._determineParity=function(t,e){var n,r;for(r=0;r=n?e.concat(t.slice(1,3)).concat([n,0,0,0,0]).concat(t.slice(3,6)):3===n?e.concat(t.slice(1,4)).concat([0,0,0,0,0]).concat(t.slice(4,6)):4===n?e.concat(t.slice(1,5)).concat([0,0,0,0,0,t[5]]):e.concat(t.slice(1,6)).concat([0,0,0,0,n]),e.push(t[t.length-1]),e},o.prototype._checksum=function(t){return i["default"].prototype._checksum.call(this,this._convertToUPCA(t))},o.prototype._findEnd=function(t,e){return e=!0,i["default"].prototype._findEnd.call(this,t,e)},o.prototype._verifyTrailingWhitespace=function(t){var e,n=this;return e=t.end+(t.end-t.start)/2,ea))return d.error=a,d.start=o-u,d.end=o,d;if(!r)return null;for(i=0;i=0&&n._matchRange(t,e.start,0))return e;r=e.end,e=null}},o.prototype._verifyTrailingWhitespace=function(t){var e,n=this;return e=t.end+(t.end-t.start)/2,ei;){for(r=0;5>r;r++)c[0][r]=t[i]*this.barSpaceRatio[0],c[1][r]=t[i+1]*this.barSpaceRatio[1],i+=2;if(o=a._decodePair(c),!o)return null;for(r=0;r-1&&t%1==0&&r>=t}var r=9007199254740991;t.exports=n; +},function(t,e){function n(t){return!!t&&"object"==typeof t}t.exports=n},function(t,e,n){var r=n(47),o=n(44),a=n(45),i="[object Array]",u=Object.prototype,c=u.toString,f=r(Array,"isArray"),l=f||function(t){return a(t)&&o(t.length)&&c.call(t)==i};t.exports=l},function(t,e,n){function r(t,e){var n=null==t?void 0:t[e];return o(n)?n:void 0}var o=n(48);t.exports=r},function(t,e,n){function r(t){return null==t?!1:o(t)?l.test(c.call(t)):a(t)&&i.test(t)}var o=n(49),a=n(45),i=/^\[object .+?Constructor\]$/,u=Object.prototype,c=Function.prototype.toString,f=u.hasOwnProperty,l=RegExp("^"+c.call(f).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=r},function(t,e,n){function r(t){return o(t)&&u.call(t)==a}var o=n(50),a="[object Function]",i=Object.prototype,u=i.toString;t.exports=r},function(t,e){function n(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){function r(t){var e;if(!i(t)||l.call(t)!=u||a(t)||!f.call(t,"constructor")&&(e=t.constructor,"function"==typeof e&&!(e instanceof e)))return!1;var n;return o(t,function(t,e){n=e}),void 0===n||f.call(t,n)}var o=n(52),a=n(40),i=n(45),u="[object Object]",c=Object.prototype,f=c.hasOwnProperty,l=c.toString;t.exports=r},function(t,e,n){function r(t,e){return o(t,e,a)}var o=n(53),a=n(56);t.exports=r},function(t,e,n){var r=n(54),o=r();t.exports=o},function(t,e,n){function r(t){return function(e,n,r){for(var a=o(e),i=r(e),u=i.length,c=t?u:-1;t?c--:++c0;++r-1&&t%1==0&&e>t}var r=/^\d+$/,o=9007199254740991;t.exports=n},function(t,e,n){function r(t){return a(t)&&o(t.length)&&!!T[S.call(t)]}var o=n(44),a=n(45),i="[object Arguments]",u="[object Array]",c="[object Boolean]",f="[object Date]",l="[object Error]",s="[object Function]",d="[object Map]",h="[object Number]",p="[object Object]",v="[object RegExp]",g="[object Set]",m="[object String]",y="[object WeakMap]",_="[object ArrayBuffer]",x="[object Float32Array]",M="[object Float64Array]",b="[object Int8Array]",w="[object Int16Array]",R="[object Int32Array]",A="[object Uint8Array]",E="[object Uint8ClampedArray]",C="[object Uint16Array]",O="[object Uint32Array]",T={};T[x]=T[M]=T[b]=T[w]=T[R]=T[A]=T[E]=T[C]=T[O]=!0,T[i]=T[u]=T[_]=T[c]=T[f]=T[l]=T[s]=T[d]=T[h]=T[p]=T[v]=T[g]=T[m]=T[y]=!1;var D=Object.prototype,S=D.toString;t.exports=r},function(t,e,n){function r(t){return o(t,a(t))}var o=n(60),a=n(56);t.exports=r},function(t,e){function n(t,e,n){n||(n={});for(var r=-1,o=e.length;++r2?n[i-2]:void 0,c=i>2?n[2]:void 0,f=i>1?n[i-1]:void 0;for("function"==typeof u?(u=o(u,f,5),i-=2):(u="function"==typeof f?f:void 0,i-=u?1:0),c&&a(n[0],n[1],c)&&(u=3>i?void 0:u,i=1);++r0?t.videoWidth>0&&t.videoHeight>0?(console.log(t.videoWidth+"px x "+t.videoHeight+"px"),e()):window.setTimeout(n,500):e("Unable to play video stream. Is webcam working?"),r--}var r=10;n()}function a(t,e,n){r(t,function(t){e.src=t,f&&e.removeEventListener("loadeddata",f,!1),f=o.bind(null,e,n),e.addEventListener("loadeddata",f,!1),e.play()},function(t){n(t)})}function i(t,e){var n={audio:!1,video:!0},r=l({width:640,height:480,minAspectRatio:0,maxAspectRatio:100,facing:"environment"},t);return"undefined"==typeof MediaStreamTrack||"undefined"==typeof MediaStreamTrack.getSources?(n.video={mediaSource:"camera",width:{min:r.width,max:r.width},height:{min:r.height,max:r.height},require:["width","height"]},e(n)):void MediaStreamTrack.getSources(function(t){for(var o,a=0;a1?a.size:Math.floor(e/o*a.size):e,r=a.size?e/o>1?Math.floor(o/e*a.size):a.size:o,f.x=n,f.y=r}var n,r,o={},a=null,i=["canrecord","ended"],u={},c={x:0,y:0},f={x:0,y:0};return o.getRealWidth=function(){return t.videoWidth},o.getRealHeight=function(){return t.videoHeight},o.getWidth=function(){return n},o.getHeight=function(){return r},o.setWidth=function(t){n=t},o.setHeight=function(t){r=t},o.setInputStream=function(e){a=e,t.src="undefined"!=typeof e.src?e.src:""},o.ended=function(){return t.ended},o.getConfig=function(){return a},o.setAttribute=function(e,n){t.setAttribute(e,n)},o.pause=function(){t.pause()},o.play=function(){t.play()},o.setCurrentTime=function(e){"LiveStream"!==a.type&&(t.currentTime=e)},o.addEventListener=function(e,n,r){-1!==i.indexOf(e)?(u[e]||(u[e]=[]),u[e].push(n)):t.addEventListener(e,n,r)},o.clearEventHandlers=function(){i.forEach(function(e){var n=u[e];n&&n.length>0&&n.forEach(function(n){t.removeEventListener(e,n)})})},o.trigger=function(t,n){var r,a=u[t];if("canrecord"===t&&e(),a&&a.length>0)for(r=0;r1?i.size:Math.floor(u/c*i.size):u,r=i.size?u/c>1?Math.floor(c/u*i.size):i.size:c,x.x=n,x.y=r,s=!0,f=0,setTimeout(function(){e("canrecord",[])},0)},p,h,i.sequence)}function e(t,e){var n,r=y[t];if(r&&r.length>0)for(n=0;nf?f++:setTimeout(function(){g=!0,e("ended",[])},0)),t):null},o},e["default"]=i,t.exports=e["default"]},function(t,e){"use strict";function n(t,e){t.onload=function(){e.loaded(this)}}Object.defineProperty(e,"__esModule",{value:!0});var r={};r.load=function(t,e,r,o,a){var i,u,c,f=new Array(o),l=new Array(f.length);if(a===!1)f[0]=t;else for(i=0;iWorking with file-input © Copyright by Christoph Oberhofer

- + - \ No newline at end of file + diff --git a/example/file_input_require.html b/example/file_input_require.html deleted file mode 100644 index 5c7b9cd0..00000000 --- a/example/file_input_require.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - index - - - - - - - - -
-
-

QuaggaJS

- -

An advanced barcode-scanner written in JavaScript

-
-
-
-

Working with file-input

- -

This example let's you select an image from your local filesystem. - QuaggaJS then tries to decode the barcode using - the preferred method (Code128 or EAN). - There is no server interaction needed as the - file is simply accessed through the File API.

- -

This also works great on a wide range of mobile-phones where the camera - access through getUserMedia is still very limited.

- -
-
- - -
-
- - - - - -
-
-
-
    -
    -
    -
    -
    -
    -

    - © Copyright by Christoph Oberhofer -

    -
    - - - - \ No newline at end of file diff --git a/example/file_input_require.js b/example/file_input_require.js deleted file mode 100644 index baf9498b..00000000 --- a/example/file_input_require.js +++ /dev/null @@ -1,161 +0,0 @@ -requirejs.config({ - "baseUrl" : "../src", - "shim" : { - "typedefs" : { - "deps" : [], - "exports" : "typedefs" - } - }, - - "paths" : { - "typedefs" : "typedefs", - "gl-matrix": "../node_modules/gl-matrix/dist/gl-matrix-min" - } -}); - -requirejs(['quagga'], function(Quagga) { - var App = { - init: function() { - App.attachListeners(); - }, - attachListeners: function() { - var self = this; - - $(".controls input[type=file]").on("change", function(e) { - if (e.target.files && e.target.files.length) { - App.decode(URL.createObjectURL(e.target.files[0])); - } - }); - - $(".controls button").on("click", function(e) { - var input = document.querySelector(".controls input[type=file]"); - if (input.files && input.files.length) { - App.decode(URL.createObjectURL(input.files[0])); - } - }); - - $(".controls .reader-config-group").on("change", "input, select", function(e) { - e.preventDefault(); - var $target = $(e.target), - value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(), - name = $target.attr("name"), - state = self._convertNameToState(name); - - console.log("Value of "+ state + " changed to " + value); - self.setState(state, value); - }); - }, - _accessByPath: function(obj, path, val) { - var parts = path.split('.'), - depth = parts.length, - setter = (typeof val !== "undefined") ? true : false; - - return parts.reduce(function(o, key, i) { - if (setter && (i + 1) === depth) { - o[key] = val; - } - return key in o ? o[key] : {}; - }, obj); - }, - _convertNameToState: function(name) { - return name.replace("_", ".").split("-").reduce(function(result, value) { - return result + value.charAt(0).toUpperCase() + value.substring(1); - }); - }, - detachListeners: function() { - $(".controls input[type=file]").off("change"); - $(".controls .reader-config-group").off("change", "input, select"); - $(".controls button").off("click"); - }, - decode: function(src) { - var self = this, - config = $.extend({}, self.state, {src: src}); - - Quagga.decodeSingle(config, function(result) {}); - }, - setState: function(path, value) { - var self = this; - - if (typeof self._accessByPath(self.inputMapper, path) === "function") { - value = self._accessByPath(self.inputMapper, path)(value); - } - - self._accessByPath(self.state, path, value); - - console.log(JSON.stringify(self.state)); - App.detachListeners(); - App.init(); - }, - inputMapper: { - inputStream: { - size: function(value){ - return parseInt(value); - } - }, - numOfWorkers: function(value) { - return parseInt(value); - }, - decoder: { - readers: function(value) { - return [value + "_reader"]; - } - } - }, - state: { - inputStream: { - size: 640, - singleChannel: false - }, - locator: { - patchSize: "medium", - halfSample: true, - showCanvas: true - }, - numOfWorkers: 0, - decoder: { - readers: ["code_128_reader"], - showFrequency: true, - showPattern: true - }, - locate: true, - src: null - } - }; - - App.init(); - - Quagga.onProcessed(function(result) { - var drawingCtx = Quagga.canvas.ctx.overlay, - drawingCanvas = Quagga.canvas.dom.overlay; - - if (result) { - if (result.boxes) { - drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height"))); - result.boxes.filter(function (box) { - return box !== result.box; - }).forEach(function (box) { - Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2}); - }); - } - - if (result.box) { - Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2}); - } - - if (result.codeResult && result.codeResult.code) { - Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3}); - } - } - }); - - Quagga.onDetected(function(result) { - var code = result.codeResult.code, - $node, - canvas = Quagga.canvas.dom.image; - - $node = $('
  • '); - $node.find("img").attr("src", canvas.toDataURL()); - $node.find("h4.code").html(code + " (" + result.codeResult.format + ")"); - $("#result_strip ul.thumbnails").prepend($node); - }); -}); \ No newline at end of file diff --git a/src/vendor/jquery-1.9.0.min.js b/example/vendor/jquery-1.9.0.min.js similarity index 100% rename from src/vendor/jquery-1.9.0.min.js rename to example/vendor/jquery-1.9.0.min.js diff --git a/karma-integration.conf.js b/karma-integration.conf.js index 5a3151d7..4d16230d 100644 --- a/karma-integration.conf.js +++ b/karma-integration.conf.js @@ -1,32 +1,52 @@ +var path = require('path'); + module.exports = function(config) { - config.set({ - basePath: '', - frameworks: ['mocha', 'requirejs', 'chai', 'sinon', 'sinon-chai'], - files: [ - 'test-main.js', - 'src/typedefs.js', - {pattern: 'node_modules/async/lib/async.js', included: false}, - {pattern: 'node_modules/gl-matrix/dist/gl-matrix-min.js', included: false}, - {pattern: 'src/*.js', included: false}, - {pattern: 'spec/**/*integration.spec.js', included: false}, - {pattern: 'test/**/*.*', included: false} - ], - exclude: [ - ], - plugins: [ - 'karma-chrome-launcher', - 'karma-mocha', - 'karma-requirejs', - 'karma-chai', - 'karma-sinon', - 'karma-sinon-chai' - ], - reporters: ['progress'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false - }); + config.set({ + basePath: '', + frameworks: ['mocha', 'chai', 'sinon', 'sinon-chai'], + files: [ + 'test/test-main-integration.js', + {pattern: 'test/integration/**/*.js', included: false}, + {pattern: 'test/fixtures/**/*.*', included: false} + ], + preprocessors: { + 'test/test-main-integration.js': ['webpack'] + }, + webpack: { + module: { + preLoaders: [ + { + test: /\.js$/, + exclude: [ + /node_modules/ + ], + loader: 'babel' + } + ] + }, + resolve: { + extensions: ['', '.js', '.jsx'], + alias: { + 'input_stream$': path.resolve(__dirname, 'src/input_stream'), + 'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber') + } + } + }, + plugins: [ + 'karma-chrome-launcher', + 'karma-mocha', + 'karma-requirejs', + 'karma-chai', + 'karma-sinon', + 'karma-sinon-chai', + require('karma-webpack') + ], + reporters: ['progress'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); }; diff --git a/karma.conf.js b/karma.conf.js index 1f91d3fb..5e4b172e 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,42 +1,64 @@ +var path = require('path'); + module.exports = function(config) { - config.set({ - basePath: '', - frameworks: ['mocha', 'requirejs', 'chai', 'sinon', 'sinon-chai'], - files: [ - 'test-main.js', - 'src/typedefs.js', - {pattern: 'node_modules/async/lib/async.js', included: false}, - {pattern: 'node_modules/gl-matrix/dist/gl-matrix-min.js', included: false}, - {pattern: 'src/*.js', included: false}, - {pattern: 'spec/**/*.js', included: false}, - {pattern: 'test/**/*.*', included: false} - ], - exclude: [ - 'spec/**/*integration.spec.js' - ], - preprocessors: { - 'src/*.js': ['coverage'] - }, - plugins: [ - 'karma-chrome-launcher', - 'karma-coverage', - 'karma-mocha', - 'karma-requirejs', - 'karma-chai', - 'karma-sinon', - 'karma-sinon-chai', - 'karma-phantomjs-launcher' - ], - reporters: ['progress', 'coverage'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false, - coverageReporter: { - type : 'html', - dir : 'coverage/' - } - }); + config.set({ + basePath: '', + frameworks: ['source-map-support', 'mocha', 'chai', 'sinon', 'sinon-chai'], + files: [ + 'test/test-main.js', + {pattern: 'test/spec/**/*.js', included: false} + ], + preprocessors: { + 'test/test-main.js': ['webpack'] + }, + webpack: { + module: { + preLoaders: [ + { + test: /\.js$/, + exclude: [ + /node_modules/ + ], + loader: 'babel' + }, + { + test: /\.js$/, + include: [ + path.resolve('src') + ], + exclude: /node_modules/, + loader: 'isparta' + } + ] + }, + resolve: { + extensions: ['', '.js', '.jsx'], + alias: { + 'input_stream$': path.resolve(__dirname, 'src/input_stream'), + 'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber') + } + }, + }, + plugins: [ + 'karma-chrome-launcher', + 'karma-coverage', + 'karma-mocha', + 'karma-chai', + 'karma-sinon', + 'karma-sinon-chai', + 'karma-source-map-support', + require('karma-webpack') + ], + reporters: ['progress', 'coverage'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + coverageReporter: { + type: 'html', + dir: 'coverage/' + } + }); }; diff --git a/lib/frame_grabber.js b/lib/frame_grabber.js index 002787f9..7b71398b 100644 --- a/lib/frame_grabber.js +++ b/lib/frame_grabber.js @@ -1,101 +1,95 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(["cv_utils"], function(CVUtils) { - "use strict"; - - var Ndarray = require("ndarray"), - Interp2D = require("ndarray-linear-interpolate").d2; - - var FrameGrabber = {}; - - FrameGrabber.create = function(inputStream) { - var _that = {}, - _streamConfig = inputStream.getConfig(), - _video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), - _canvasSize = inputStream.getCanvasSize(), - _size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), - _topRight = inputStream.getTopRight(), - _data = new Uint8Array(_size.x * _size.y), - _grayData = new Uint8Array(_video_size.x * _video_size.y), - _canvasData = new Uint8Array(_canvasSize.x * _canvasSize.y), - _grayImageArray = Ndarray(_grayData, [_video_size.y, _video_size.x]).transpose(1, 0), - _canvasImageArray = Ndarray(_canvasData, [_canvasSize.y, _canvasSize.x]).transpose(1, 0), - _targetImageArray = _canvasImageArray.hi(_topRight.x + _size.x, _topRight.y + _size.y).lo(_topRight.x, _topRight.y), - _stepSizeX = _video_size.x/_canvasSize.x, - _stepSizeY = _video_size.y/_canvasSize.y; - - console.log("FrameGrabber", JSON.stringify({ - videoSize: _grayImageArray.shape, - canvasSize: _canvasImageArray.shape, - stepSize: [_stepSizeX, _stepSizeY], - size: _targetImageArray.shape, - topRight: _topRight - })); - - /** - * Uses the given array as frame-buffer - */ - _that.attachData = function(data) { - _data = data; - }; - - /** - * Returns the used frame-buffer - */ - _that.getData = function() { - return _data; - }; - - /** - * Fetches a frame from the input-stream and puts into the frame-buffer. - * The image-data is converted to gray-scale and then half-sampled if configured. - */ - _that.grab = function() { - var frame = inputStream.getFrame(); +const CVUtils = require('../src/cv_utils'), + Ndarray = require("ndarray"), + Interp2D = require("ndarray-linear-interpolate").d2; + +var FrameGrabber = {}; + +FrameGrabber.create = function(inputStream) { + var _that = {}, + _streamConfig = inputStream.getConfig(), + _video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), + _canvasSize = inputStream.getCanvasSize(), + _size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), + _topRight = inputStream.getTopRight(), + _data = new Uint8Array(_size.x * _size.y), + _grayData = new Uint8Array(_video_size.x * _video_size.y), + _canvasData = new Uint8Array(_canvasSize.x * _canvasSize.y), + _grayImageArray = Ndarray(_grayData, [_video_size.y, _video_size.x]).transpose(1, 0), + _canvasImageArray = Ndarray(_canvasData, [_canvasSize.y, _canvasSize.x]).transpose(1, 0), + _targetImageArray = _canvasImageArray.hi(_topRight.x + _size.x, _topRight.y + _size.y).lo(_topRight.x, _topRight.y), + _stepSizeX = _video_size.x/_canvasSize.x, + _stepSizeY = _video_size.y/_canvasSize.y; + + console.log("FrameGrabber", JSON.stringify({ + videoSize: _grayImageArray.shape, + canvasSize: _canvasImageArray.shape, + stepSize: [_stepSizeX, _stepSizeY], + size: _targetImageArray.shape, + topRight: _topRight + })); + + /** + * Uses the given array as frame-buffer + */ + _that.attachData = function(data) { + _data = data; + }; - if (frame) { - this.scaleAndCrop(frame); - return true; - } else { - return false; - } - }; + /** + * Returns the used frame-buffer + */ + _that.getData = function() { + return _data; + }; - _that.scaleAndCrop = function(frame) { - var x, - y; + /** + * Fetches a frame from the input-stream and puts into the frame-buffer. + * The image-data is converted to gray-scale and then half-sampled if configured. + */ + _that.grab = function() { + var frame = inputStream.getFrame(); + + if (frame) { + this.scaleAndCrop(frame); + return true; + } else { + return false; + } + }; - // 1. compute full-sized gray image - CVUtils.computeGray(frame.data, _grayData); + _that.scaleAndCrop = function(frame) { + var x, + y; - // 2. interpolate - for (y = 0; y < _canvasSize.y; y++) { - for (x = 0; x < _canvasSize.x; x++) { - _canvasImageArray.set(x, y, (Interp2D(_grayImageArray, x * _stepSizeX, y * _stepSizeY)) | 0); - } - } + // 1. compute full-sized gray image + CVUtils.computeGray(frame.data, _grayData); - // targetImageArray must be equal to targetSize - if (_targetImageArray.shape[0] !== _size.x || - _targetImageArray.shape[1] !== _size.y) { - throw new Error("Shapes do not match!"); + // 2. interpolate + for (y = 0; y < _canvasSize.y; y++) { + for (x = 0; x < _canvasSize.x; x++) { + _canvasImageArray.set(x, y, (Interp2D(_grayImageArray, x * _stepSizeX, y * _stepSizeY)) | 0); } - - // 3. crop - for (y = 0; y < _size.y; y++) { - for (x = 0; x < _size.x; x++) { - _data[y * _size.x + x] = _targetImageArray.get(x, y); - } + } + + // targetImageArray must be equal to targetSize + if (_targetImageArray.shape[0] !== _size.x || + _targetImageArray.shape[1] !== _size.y) { + throw new Error("Shapes do not match!"); + } + + // 3. crop + for (y = 0; y < _size.y; y++) { + for (x = 0; x < _size.x; x++) { + _data[y * _size.x + x] = _targetImageArray.get(x, y); } - }, + } + }, - _that.getSize = function() { - return _size; - }; - - return _that; + _that.getSize = function() { + return _size; }; - return (FrameGrabber); -}); + return _that; +}; + +module.exports = FrameGrabber; diff --git a/lib/input_stream.js b/lib/input_stream.js index 22e58283..5829c620 100644 --- a/lib/input_stream.js +++ b/lib/input_stream.js @@ -1,160 +1,153 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(function() { - "use strict"; - - var GetPixels = require("get-pixels"); - - var InputStream = {}; - - InputStream.createImageStream = function() { - var that = {}; - var _config = null; - - var width = 0, - height = 0, - frameIdx = 0, - paused = true, - loaded = false, - frame = null, - baseUrl, - ended = false, - size, - calculatedWidth, - calculatedHeight, - _eventNames = ['canrecord', 'ended'], - _eventHandlers = {}, - _topRight = {x: 0, y: 0}, - _canvasSize = {x: 0, y: 0}; - - function loadImages() { - loaded = false; - GetPixels(baseUrl, function(err, pixels) { - if (err) { - console.log(err); - exit(1); - } - loaded = true; - console.log(pixels.shape); - frame = pixels; - width = pixels.shape[0]; - height = pixels.shape[1]; - calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; - calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; - - _canvasSize.x = calculatedWidth; - _canvasSize.y = calculatedHeight; - - setTimeout(function() { - publishEvent("canrecord", []); - }, 0); - }); - } - - function publishEvent(eventName, args) { - var j, - handlers = _eventHandlers[eventName]; - - if (handlers && handlers.length > 0) { - for ( j = 0; j < handlers.length; j++) { - handlers[j].apply(that, args); - } +const GetPixels = require("get-pixels"); + +var InputStream = {}; + +InputStream.createImageStream = function() { + var that = {}; + var _config = null; + + var width = 0, + height = 0, + frameIdx = 0, + paused = true, + loaded = false, + frame = null, + baseUrl, + ended = false, + size, + calculatedWidth, + calculatedHeight, + _eventNames = ['canrecord', 'ended'], + _eventHandlers = {}, + _topRight = {x: 0, y: 0}, + _canvasSize = {x: 0, y: 0}; + + function loadImages() { + loaded = false; + GetPixels(baseUrl, function(err, pixels) { + if (err) { + console.log(err); + exit(1); + } + loaded = true; + console.log(pixels.shape); + frame = pixels; + width = pixels.shape[0]; + height = pixels.shape[1]; + calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; + calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; + + _canvasSize.x = calculatedWidth; + _canvasSize.y = calculatedHeight; + + setTimeout(function() { + publishEvent("canrecord", []); + }, 0); + }); + } + + function publishEvent(eventName, args) { + var j, + handlers = _eventHandlers[eventName]; + + if (handlers && handlers.length > 0) { + for ( j = 0; j < handlers.length; j++) { + handlers[j].apply(that, args); } } + } - that.trigger = publishEvent; + that.trigger = publishEvent; - that.getWidth = function() { - return calculatedWidth; - }; + that.getWidth = function() { + return calculatedWidth; + }; - that.getHeight = function() { - return calculatedHeight; - }; + that.getHeight = function() { + return calculatedHeight; + }; - that.setWidth = function(width) { - calculatedWidth = width; - }; + that.setWidth = function(width) { + calculatedWidth = width; + }; - that.setHeight = function(height) { - calculatedHeight = height; - }; + that.setHeight = function(height) { + calculatedHeight = height; + }; - that.getRealWidth = function() { - return width; - }; + that.getRealWidth = function() { + return width; + }; - that.getRealHeight = function() { - return height; - }; + that.getRealHeight = function() { + return height; + }; - that.setInputStream = function(stream) { - _config = stream; - baseUrl = _config.src; - size = 1; - loadImages(); - }; + that.setInputStream = function(stream) { + _config = stream; + baseUrl = _config.src; + size = 1; + loadImages(); + }; - that.ended = function() { - return ended; - }; + that.ended = function() { + return ended; + }; - that.setAttribute = function() {}; + that.setAttribute = function() {}; - that.getConfig = function() { - return _config; - }; + that.getConfig = function() { + return _config; + }; - that.pause = function() { - paused = true; - }; + that.pause = function() { + paused = true; + }; - that.play = function() { - paused = false; - }; + that.play = function() { + paused = false; + }; - that.setCurrentTime = function(time) { - frameIdx = time; - }; + that.setCurrentTime = function(time) { + frameIdx = time; + }; - that.addEventListener = function(event, f) { - if (_eventNames.indexOf(event) !== -1) { - if (!_eventHandlers[event]) { - _eventHandlers[event] = []; - } - _eventHandlers[event].push(f); + that.addEventListener = function(event, f) { + if (_eventNames.indexOf(event) !== -1) { + if (!_eventHandlers[event]) { + _eventHandlers[event] = []; } - }; - - that.setTopRight = function(topRight) { - _topRight.x = topRight.x; - _topRight.y = topRight.y; - }; + _eventHandlers[event].push(f); + } + }; - that.getTopRight = function() { - return _topRight; - }; + that.setTopRight = function(topRight) { + _topRight.x = topRight.x; + _topRight.y = topRight.y; + }; - that.setCanvasSize = function(size) { - _canvasSize.x = size.x; - _canvasSize.y = size.y; - }; + that.getTopRight = function() { + return _topRight; + }; - that.getCanvasSize = function() { - return _canvasSize; - }; + that.setCanvasSize = function(size) { + _canvasSize.x = size.x; + _canvasSize.y = size.y; + }; - that.getFrame = function() { - if (!loaded){ - return null; - } - return frame; - }; + that.getCanvasSize = function() { + return _canvasSize; + }; - return that; + that.getFrame = function() { + if (!loaded){ + return null; + } + return frame; }; - return (InputStream); -}); + return that; +}; + +module.exports = InputStream; diff --git a/lib/quagga.js b/lib/quagga.js index bab5ec7d..b93caf5a 100644 --- a/lib/quagga.js +++ b/lib/quagga.js @@ -1,13 +1,7800 @@ -var requirejs = require('requirejs'), - path = require('path'); - -requirejs.config({ - "baseUrl" : path.resolve(__dirname, '..', 'src'), - "paths" : { - "typedefs" : "typedefs", - "input_stream": "../lib/input_stream", - "frame_grabber": "../lib/frame_grabber" - }, - nodeRequire: require -}); -module.exports = requirejs('quagga'); +module.exports = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; +/******/ +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/"; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(1); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _typedefs = __webpack_require__(2); + + var _typedefs2 = _interopRequireDefault(_typedefs); + + // eslint-disable-line no-unused-vars + + var _image_wrapper = __webpack_require__(3); + + var _image_wrapper2 = _interopRequireDefault(_image_wrapper); + + var _barcode_locator = __webpack_require__(9); + + var _barcode_locator2 = _interopRequireDefault(_barcode_locator); + + var _barcode_decoder = __webpack_require__(14); + + var _barcode_decoder2 = _interopRequireDefault(_barcode_decoder); + + var _config2 = __webpack_require__(59); + + var _config3 = _interopRequireDefault(_config2); + + var _events = __webpack_require__(60); + + var _events2 = _interopRequireDefault(_events); + + var _camera_access = __webpack_require__(61); + + var _camera_access2 = _interopRequireDefault(_camera_access); + + var _image_debug = __webpack_require__(13); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + var _glMatrix = __webpack_require__(7); + + var _result_collector = __webpack_require__(62); + + var _result_collector2 = _interopRequireDefault(_result_collector); + + var merge = __webpack_require__(26); + var InputStream = __webpack_require__(63); + var FrameGrabber = __webpack_require__(65); + + var _inputStream, + _framegrabber, + _stopped, + _canvasContainer = { + ctx: { + image: null, + overlay: null + }, + dom: { + image: null, + overlay: null + } + }, + _inputImageWrapper, + _boxSize, + _decoder, + _workerPool = [], + _onUIThread = true, + _resultCollector, + _config = {}; + + function initializeData(imageWrapper) { + initBuffers(imageWrapper); + _decoder = _barcode_decoder2['default'].create(_config.decoder, _inputImageWrapper); + } + + function initConfig() { + if (typeof document !== "undefined") { + var vis = [{ + node: document.querySelector("div[data-controls]"), + prop: _config.controls + }, { + node: _canvasContainer.dom.overlay, + prop: _config.visual.show + }]; + + for (var i = 0; i < vis.length; i++) { + if (vis[i].node) { + if (vis[i].prop === true) { + vis[i].node.style.display = "block"; + } else { + vis[i].node.style.display = "none"; + } + } + } + } + } + + function initInputStream(cb) { + var video; + if (_config.inputStream.type === "VideoStream") { + video = document.createElement("video"); + _inputStream = InputStream.createVideoStream(video); + } else if (_config.inputStream.type === "ImageStream") { + _inputStream = InputStream.createImageStream(); + } else if (_config.inputStream.type === "LiveStream") { + var $viewport = document.querySelector("#interactive.viewport"); + if ($viewport) { + video = $viewport.querySelector("video"); + if (!video) { + video = document.createElement("video"); + $viewport.appendChild(video); + } + } + _inputStream = InputStream.createLiveStream(video); + _camera_access2['default'].request(video, _config.inputStream.constraints, function (err) { + if (!err) { + _inputStream.trigger("canrecord"); + } else { + return cb(err); + } + }); + } + + _inputStream.setAttribute("preload", "auto"); + _inputStream.setAttribute("autoplay", true); + _inputStream.setInputStream(_config.inputStream); + _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); + } + + function canRecord(cb) { + _barcode_locator2['default'].checkImageConstraints(_inputStream, _config.locator); + initCanvas(); + _framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image); + initConfig(); + + if (_config.numOfWorkers > 0) { + initWorkers(function () { + console.log("Workers created"); + ready(cb); + }); + } else { + initializeData(); + ready(cb); + } + } + + function ready(cb) { + _inputStream.play(); + cb(); + } + + function initCanvas() { + if (typeof document !== "undefined") { + var $viewport = document.querySelector("#interactive.viewport"); + _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); + if (!_canvasContainer.dom.image) { + _canvasContainer.dom.image = document.createElement("canvas"); + _canvasContainer.dom.image.className = "imgBuffer"; + if ($viewport && _config.inputStream.type === "ImageStream") { + $viewport.appendChild(_canvasContainer.dom.image); + } + } + _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); + _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; + _canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; + + _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer"); + if (!_canvasContainer.dom.overlay) { + _canvasContainer.dom.overlay = document.createElement("canvas"); + _canvasContainer.dom.overlay.className = "drawingBuffer"; + if ($viewport) { + $viewport.appendChild(_canvasContainer.dom.overlay); + } + var clearFix = document.createElement("br"); + clearFix.setAttribute("clear", "all"); + if ($viewport) { + $viewport.appendChild(clearFix); + } + } + _canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); + _canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; + _canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; + } + } + + function initBuffers(imageWrapper) { + if (imageWrapper) { + _inputImageWrapper = imageWrapper; + } else { + _inputImageWrapper = new _image_wrapper2['default']({ + x: _inputStream.getWidth(), + y: _inputStream.getHeight() + }); + } + + console.log(_inputImageWrapper.size); + _boxSize = [_glMatrix.vec2.clone([0, 0]), _glMatrix.vec2.clone([0, _inputImageWrapper.size.y]), _glMatrix.vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]), _glMatrix.vec2.clone([_inputImageWrapper.size.x, 0])]; + _barcode_locator2['default'].init(_inputImageWrapper, _config.locator); + } + + function getBoundingBoxes() { + if (_config.locate) { + return _barcode_locator2['default'].locate(); + } else { + return [[_glMatrix.vec2.clone(_boxSize[0]), _glMatrix.vec2.clone(_boxSize[1]), _glMatrix.vec2.clone(_boxSize[2]), _glMatrix.vec2.clone(_boxSize[3])]]; + } + } + + function transformResult(result) { + var topRight = _inputStream.getTopRight(), + xOffset = topRight.x, + yOffset = topRight.y, + i; + + if (!result || xOffset === 0 && yOffset === 0) { + return; + } + + if (result.line && result.line.length === 2) { + moveLine(result.line); + } + if (result.boxes && result.boxes.length > 0) { + for (i = 0; i < result.boxes.length; i++) { + moveBox(result.boxes[i]); + } + } + + function moveBox(box) { + var corner = box.length; + + while (corner--) { + box[corner][0] += xOffset; + box[corner][1] += yOffset; + } + } + + function moveLine(line) { + line[0].x += xOffset; + line[0].y += yOffset; + line[1].x += xOffset; + line[1].y += yOffset; + } + } + + function publishResult(result, imageData) { + if (_onUIThread) { + transformResult(result); + if (imageData && result && result.codeResult) { + if (_resultCollector) { + _resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult); + } + } + } + + _events2['default'].publish("processed", result); + if (result && result.codeResult) { + _events2['default'].publish("detected", result); + } + } + + function locateAndDecode() { + var result, boxes; + + boxes = getBoundingBoxes(); + if (boxes) { + result = _decoder.decodeFromBoundingBoxes(boxes); + result = result || {}; + result.boxes = boxes; + publishResult(result, _inputImageWrapper.data); + } else { + publishResult(); + } + } + + function update() { + var availableWorker; + + if (_onUIThread) { + if (_workerPool.length > 0) { + availableWorker = _workerPool.filter(function (workerThread) { + return !workerThread.busy; + })[0]; + if (availableWorker) { + _framegrabber.attachData(availableWorker.imageData); + } else { + return; // all workers are busy + } + } else { + _framegrabber.attachData(_inputImageWrapper.data); + } + if (_framegrabber.grab()) { + if (availableWorker) { + availableWorker.busy = true; + availableWorker.worker.postMessage({ + cmd: 'process', + imageData: availableWorker.imageData + }, [availableWorker.imageData.buffer]); + } else { + locateAndDecode(); + } + } + } else { + locateAndDecode(); + } + } + + function _start() { + _stopped = false; + (function frame() { + if (!_stopped) { + update(); + if (_onUIThread && _config.inputStream.type === "LiveStream") { + window.requestAnimFrame(frame); + } + } + })(); + } + + function initWorkers(cb) { + var i; + _workerPool = []; + + for (i = 0; i < _config.numOfWorkers; i++) { + initWorker(workerInitialized); + } + + function workerInitialized(workerThread) { + _workerPool.push(workerThread); + if (_workerPool.length >= _config.numOfWorkers) { + cb(); + } + } + } + + function initWorker(cb) { + var blobURL, + workerThread = { + worker: undefined, + imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), + busy: true + }; + + blobURL = generateWorkerBlob(); + workerThread.worker = new Worker(blobURL); + + workerThread.worker.onmessage = function (e) { + if (e.data.event === 'initialized') { + URL.revokeObjectURL(blobURL); + workerThread.busy = false; + workerThread.imageData = new Uint8Array(e.data.imageData); + console.log("Worker initialized"); + return cb(workerThread); + } else if (e.data.event === 'processed') { + workerThread.imageData = new Uint8Array(e.data.imageData); + workerThread.busy = false; + publishResult(e.data.result, workerThread.imageData); + } else if (e.data.event === 'error') { + console.log("Worker error: " + e.data.message); + } + }; + + workerThread.worker.postMessage({ + cmd: 'init', + size: { x: _inputStream.getWidth(), y: _inputStream.getHeight() }, + imageData: workerThread.imageData, + config: _config + }, [workerThread.imageData.buffer]); + } + + function workerInterface(factory) { + /* eslint-disable no-undef*/ + if (factory) { + var Quagga = factory(); + if (!Quagga) { + self.postMessage({ 'event': 'error', message: 'Quagga could not be created' }); + return; + } + } + var imageWrapper; + + self.onmessage = function (e) { + if (e.data.cmd === 'init') { + var config = e.data.config; + config.numOfWorkers = 0; + imageWrapper = new Quagga.ImageWrapper({ + x: e.data.size.x, + y: e.data.size.y + }, new Uint8Array(e.data.imageData)); + Quagga.init(config, ready, imageWrapper); + Quagga.onProcessed(onProcessed); + } else if (e.data.cmd === 'process') { + imageWrapper.data = new Uint8Array(e.data.imageData); + Quagga.start(); + } else if (e.data.cmd === 'setReaders') { + Quagga.setReaders(e.data.readers); + } + }; + + function onProcessed(result) { + self.postMessage({ + 'event': 'processed', + imageData: imageWrapper.data, + result: result + }, [imageWrapper.data.buffer]); + } + + function ready() { + // eslint-disable-line + self.postMessage({ 'event': 'initialized', imageData: imageWrapper.data }, [imageWrapper.data.buffer]); + } + + /* eslint-enable */ + } + + function generateWorkerBlob() { + var blob, factorySource; + + /* jshint ignore:start */ + if (typeof __factorySource__ !== 'undefined') { + factorySource = __factorySource__; // eslint-disable-line no-undef + } + /* jshint ignore:end */ + + blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'], { type: 'text/javascript' }); + + return window.URL.createObjectURL(blob); + } + + function _setReaders(readers) { + if (_decoder) { + _decoder.setReaders(readers); + } else if (_onUIThread && _workerPool.length > 0) { + _workerPool.forEach(function (workerThread) { + workerThread.worker.postMessage({ cmd: 'setReaders', readers: readers }); + }); + } + } + + exports['default'] = { + init: function init(config, cb, imageWrapper) { + _config = merge({}, _config3['default'], config); + if (imageWrapper) { + _onUIThread = false; + initializeData(imageWrapper); + return cb(); + } else { + initInputStream(cb); + } + }, + start: function start() { + _start(); + }, + stop: function stop() { + _stopped = true; + _workerPool.forEach(function (workerThread) { + workerThread.worker.terminate(); + console.log("Worker terminated!"); + }); + _workerPool.length = 0; + if (_config.inputStream.type === "LiveStream") { + _camera_access2['default'].release(); + _inputStream.clearEventHandlers(); + } + }, + pause: function pause() { + _stopped = true; + }, + onDetected: function onDetected(callback) { + _events2['default'].subscribe("detected", callback); + }, + offDetected: function offDetected(callback) { + _events2['default'].unsubscribe("detected", callback); + }, + onProcessed: function onProcessed(callback) { + _events2['default'].subscribe("processed", callback); + }, + offProcessed: function offProcessed(callback) { + _events2['default'].unsubscribe("processed", callback); + }, + setReaders: function setReaders(readers) { + _setReaders(readers); + }, + registerResultCollector: function registerResultCollector(resultCollector) { + if (resultCollector && typeof resultCollector.addResult === 'function') { + _resultCollector = resultCollector; + } + }, + canvas: _canvasContainer, + decodeSingle: function decodeSingle(config, resultCallback) { + config = merge({ + inputStream: { + type: "ImageStream", + sequence: false, + size: 800, + src: config.src + }, + numOfWorkers: 1, + locator: { + halfSample: false + } + }, config); + this.init(config, function () { + _events2['default'].once("processed", function (result) { + _stopped = true; + resultCallback.call(null, result); + }, true); + _start(); + }); + }, + ImageWrapper: _image_wrapper2['default'], + ImageDebug: _image_debug2['default'], + ResultCollector: _result_collector2['default'] + }; + module.exports = exports['default']; + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + /* + * typedefs.js + * Normalizes browser-specific prefixes + */ + + 'use strict'; + + if (typeof window !== 'undefined') { + window.requestAnimFrame = (function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function ( /* function FrameRequestCallback */callback) { + window.setTimeout(callback, 1000 / 60); + }; + })(); + + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; + } + Math.imul = Math.imul || function (a, b) { + var ah = a >>> 16 & 0xffff, + al = a & 0xffff, + bh = b >>> 16 & 0xffff, + bl = b & 0xffff; + // the shift by 0 fixes the sign on the high part + // the final |0 converts the unsigned value into a signed value + return al * bl + (ah * bl + al * bh << 16 >>> 0) | 0; + }; + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _subImage = __webpack_require__(4); + + var _subImage2 = _interopRequireDefault(_subImage); + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var _array_helper = __webpack_require__(8); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + var _glMatrix = __webpack_require__(7); + + /** + * Represents a basic image combining the data and size. + * In addition, some methods for manipulation are contained. + * @param size {x,y} The size of the image in pixel + * @param data {Array} If given, a flat array containing the pixel data + * @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed) + * @param initialize {Boolean} Indicating if the array should be initialized on creation. + * @returns {ImageWrapper} + */ + function ImageWrapper(size, data, ArrayType, initialize) { + if (!data) { + if (ArrayType) { + this.data = new ArrayType(size.x * size.y); + if (ArrayType === Array && initialize) { + _array_helper2['default'].init(this.data, 0); + } + } else { + this.data = new Uint8Array(size.x * size.y); + if (Uint8Array === Array && initialize) { + _array_helper2['default'].init(this.data, 0); + } + } + } else { + this.data = data; + } + this.size = size; + } + + /** + * tests if a position is within the image with a given offset + * @param imgRef {x, y} The location to test + * @param border Number the padding value in pixel + * @returns {Boolean} true if location inside the image's border, false otherwise + * @see cvd/image.h + */ + ImageWrapper.prototype.inImageWithBorder = function (imgRef, border) { + return imgRef.x >= border && imgRef.y >= border && imgRef.x < this.size.x - border && imgRef.y < this.size.y - border; + }; + + /** + * Performs bilinear sampling + * @param inImg Image to extract sample from + * @param x the x-coordinate + * @param y the y-coordinate + * @returns the sampled value + * @see cvd/vision.h + */ + ImageWrapper.sample = function (inImg, x, y) { + var lx = Math.floor(x); + var ly = Math.floor(y); + var w = inImg.size.x; + var base = ly * inImg.size.x + lx; + var a = inImg.data[base + 0]; + var b = inImg.data[base + 1]; + var c = inImg.data[base + w]; + var d = inImg.data[base + w + 1]; + var e = a - b; + x -= lx; + y -= ly; + + var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a); + return result; + }; + + /** + * Initializes a given array. Sets each element to zero. + * @param array {Array} The array to initialize + */ + ImageWrapper.clearArray = function (array) { + var l = array.length; + while (l--) { + array[l] = 0; + } + }; + + /** + * Creates a {SubImage} from the current image ({this}). + * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) + * @param size {ImageRef} The size of the resulting image + * @returns {SubImage} A shared part of the original image + */ + ImageWrapper.prototype.subImage = function (from, size) { + return new _subImage2['default'](from, size, this); + }; + + /** + * Creates an {ImageWrapper) and copies the needed underlying image-data area + * @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied + * @param from {ImageRef} The location where to copy from (top-left location) + */ + ImageWrapper.prototype.subImageAsCopy = function (imageWrapper, from) { + var sizeY = imageWrapper.size.y, + sizeX = imageWrapper.size.x; + var x, y; + for (x = 0; x < sizeX; x++) { + for (y = 0; y < sizeY; y++) { + imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x]; + } + } + }; + + ImageWrapper.prototype.copyTo = function (imageWrapper) { + var length = this.data.length, + srcData = this.data, + dstData = imageWrapper.data; + + while (length--) { + dstData[length] = srcData[length]; + } + }; + + /** + * Retrieves a given pixel position from the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ + ImageWrapper.prototype.get = function (x, y) { + return this.data[y * this.size.x + x]; + }; + + /** + * Retrieves a given pixel position from the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ + ImageWrapper.prototype.getSafe = function (x, y) { + var i; + + if (!this.indexMapping) { + this.indexMapping = { + x: [], + y: [] + }; + for (i = 0; i < this.size.x; i++) { + this.indexMapping.x[i] = i; + this.indexMapping.x[i + this.size.x] = i; + } + for (i = 0; i < this.size.y; i++) { + this.indexMapping.y[i] = i; + this.indexMapping.y[i + this.size.y] = i; + } + } + return this.data[this.indexMapping.y[y + this.size.y] * this.size.x + this.indexMapping.x[x + this.size.x]]; + }; + + /** + * Sets a given pixel position in the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @param value {Number} The grayscale value to set + * @returns {ImageWrapper} The Image itself (for possible chaining) + */ + ImageWrapper.prototype.set = function (x, y, value) { + this.data[y * this.size.x + x] = value; + return this; + }; + + /** + * Sets the border of the image (1 pixel) to zero + */ + ImageWrapper.prototype.zeroBorder = function () { + var i, + width = this.size.x, + height = this.size.y, + data = this.data; + for (i = 0; i < width; i++) { + data[i] = data[(height - 1) * width + i] = 0; + } + for (i = 1; i < height - 1; i++) { + data[i * width] = data[i * width + (width - 1)] = 0; + } + }; + + /** + * Inverts a binary image in place + */ + ImageWrapper.prototype.invert = function () { + var data = this.data, + length = data.length; + + while (length--) { + data[length] = data[length] ? 0 : 1; + } + }; + + ImageWrapper.prototype.convolve = function (kernel) { + var x, + y, + kx, + ky, + kSize = kernel.length / 2 | 0, + accu = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + accu = 0; + for (ky = -kSize; ky <= kSize; ky++) { + for (kx = -kSize; kx <= kSize; kx++) { + accu += kernel[ky + kSize][kx + kSize] * this.getSafe(x + kx, y + ky); + } + } + this.data[y * this.size.x + x] = accu; + } + } + }; + + ImageWrapper.prototype.moments = function (labelcount) { + var data = this.data, + x, + y, + height = this.size.y, + width = this.size.x, + val, + ysq, + labelsum = [], + i, + label, + mu11, + mu02, + mu20, + x_, + y_, + tmp, + result = [], + PI = Math.PI, + PI_4 = PI / 4; + + if (labelcount <= 0) { + return result; + } + + for (i = 0; i < labelcount; i++) { + labelsum[i] = { + m00: 0, + m01: 0, + m10: 0, + m11: 0, + m02: 0, + m20: 0, + theta: 0, + rad: 0 + }; + } + + for (y = 0; y < height; y++) { + ysq = y * y; + for (x = 0; x < width; x++) { + val = data[y * width + x]; + if (val > 0) { + label = labelsum[val - 1]; + label.m00 += 1; + label.m01 += y; + label.m10 += x; + label.m11 += x * y; + label.m02 += ysq; + label.m20 += x * x; + } + } + } + + for (i = 0; i < labelcount; i++) { + label = labelsum[i]; + if (!isNaN(label.m00) && label.m00 !== 0) { + x_ = label.m10 / label.m00; + y_ = label.m01 / label.m00; + mu11 = label.m11 / label.m00 - x_ * y_; + mu02 = label.m02 / label.m00 - y_ * y_; + mu20 = label.m20 / label.m00 - x_ * x_; + tmp = (mu02 - mu20) / (2 * mu11); + tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4) + PI; + label.theta = (tmp * 180 / PI + 90) % 180 - 90; + if (label.theta < 0) { + label.theta += 180; + } + label.rad = tmp > PI ? tmp - PI : tmp; + label.vec = _glMatrix.vec2.clone([Math.cos(tmp), Math.sin(tmp)]); + result.push(label); + } + } + + return result; + }; + + /** + * Displays the {ImageWrapper} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ + ImageWrapper.prototype.show = function (canvas, scale) { + var ctx, frame, data, current, pixel, x, y; + + if (!scale) { + scale = 1.0; + } + ctx = canvas.getContext('2d'); + canvas.width = this.size.x; + canvas.height = this.size.y; + frame = ctx.getImageData(0, 0, canvas.width, canvas.height); + data = frame.data; + current = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + pixel = y * this.size.x + x; + current = this.get(x, y) * scale; + data[pixel * 4 + 0] = current; + data[pixel * 4 + 1] = current; + data[pixel * 4 + 2] = current; + data[pixel * 4 + 3] = 255; + } + } + //frame.data = data; + ctx.putImageData(frame, 0, 0); + }; + + /** + * Displays the {SubImage} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ + ImageWrapper.prototype.overlay = function (canvas, scale, from) { + if (!scale || scale < 0 || scale > 360) { + scale = 360; + } + var hsv = [0, 1, 1]; + var rgb = [0, 0, 0]; + var whiteRgb = [255, 255, 255]; + var blackRgb = [0, 0, 0]; + var result = []; + var ctx = canvas.getContext('2d'); + var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y); + var data = frame.data; + var length = this.data.length; + while (length--) { + hsv[0] = this.data[length] * scale; + result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : _cv_utils2['default'].hsv2rgb(hsv, rgb); + data[length * 4 + 0] = result[0]; + data[length * 4 + 1] = result[1]; + data[length * 4 + 2] = result[2]; + data[length * 4 + 3] = 255; + } + ctx.putImageData(frame, from.x, from.y); + }; + + exports['default'] = ImageWrapper; + module.exports = exports['default']; + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + /** + * Construct representing a part of another {ImageWrapper}. Shares data + * between the parent and the child. + * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) + * @param size {ImageRef} The size of the resulting image + * @param I {ImageWrapper} The {ImageWrapper} to share from + * @returns {SubImage} A shared part of the original image + */ + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + function SubImage(from, size, I) { + if (!I) { + I = { + data: null, + size: size + }; + } + this.data = I.data; + this.originalSize = I.size; + this.I = I; + + this.from = from; + this.size = size; + } + + /** + * Displays the {SubImage} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ + SubImage.prototype.show = function (canvas, scale) { + var ctx, frame, data, current, y, x, pixel; + + if (!scale) { + scale = 1.0; + } + ctx = canvas.getContext('2d'); + canvas.width = this.size.x; + canvas.height = this.size.y; + frame = ctx.getImageData(0, 0, canvas.width, canvas.height); + data = frame.data; + current = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + pixel = y * this.size.x + x; + current = this.get(x, y) * scale; + data[pixel * 4 + 0] = current; + data[pixel * 4 + 1] = current; + data[pixel * 4 + 2] = current; + data[pixel * 4 + 3] = 255; + } + } + frame.data = data; + ctx.putImageData(frame, 0, 0); + }; + + /** + * Retrieves a given pixel position from the {SubImage} + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ + SubImage.prototype.get = function (x, y) { + return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x]; + }; + + /** + * Updates the underlying data from a given {ImageWrapper} + * @param image {ImageWrapper} The updated image + */ + SubImage.prototype.updateData = function (image) { + this.originalSize = image.size; + this.data = image.data; + }; + + /** + * Updates the position of the shared area + * @param from {x,y} The new location + * @returns {SubImage} returns {this} for possible chaining + */ + SubImage.prototype.updateFrom = function (from) { + this.from = from; + return this; + }; + + exports['default'] = SubImage; + module.exports = exports['default']; + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _cluster = __webpack_require__(6); + + var _cluster2 = _interopRequireDefault(_cluster); + + var _array_helper = __webpack_require__(8); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + var _glMatrix = __webpack_require__(7); + + var CVUtils = {}; + + /** + * @param x x-coordinate + * @param y y-coordinate + * @return ImageReference {x,y} Coordinate + */ + CVUtils.imageRef = function (x, y) { + var that = { + x: x, + y: y, + toVec2: function toVec2() { + return _glMatrix.vec2.clone([this.x, this.y]); + }, + toVec3: function toVec3() { + return _glMatrix.vec3.clone([this.x, this.y, 1]); + }, + round: function round() { + this.x = this.x > 0.0 ? Math.floor(this.x + 0.5) : Math.floor(this.x - 0.5); + this.y = this.y > 0.0 ? Math.floor(this.y + 0.5) : Math.floor(this.y - 0.5); + return this; + } + }; + return that; + }; + + /** + * Computes an integral image of a given grayscale image. + * @param imageDataContainer {ImageDataContainer} the image to be integrated + */ + CVUtils.computeIntegralImage2 = function (imageWrapper, integralWrapper) { + var imageData = imageWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0, + posA = 0, + posB = 0, + posC = 0, + posD = 0, + x, + y; + + // sum up first column + posB = width; + sum = 0; + for (y = 1; y < height; y++) { + sum += imageData[posA]; + integralImageData[posB] += sum; + posA += width; + posB += width; + } + + posA = 0; + posB = 1; + sum = 0; + for (x = 1; x < width; x++) { + sum += imageData[posA]; + integralImageData[posB] += sum; + posA++; + posB++; + } + + for (y = 1; y < height; y++) { + posA = y * width + 1; + posB = (y - 1) * width + 1; + posC = y * width; + posD = (y - 1) * width; + for (x = 1; x < width; x++) { + integralImageData[posA] += imageData[posA] + integralImageData[posB] + integralImageData[posC] - integralImageData[posD]; + posA++; + posB++; + posC++; + posD++; + } + } + }; + + CVUtils.computeIntegralImage = function (imageWrapper, integralWrapper) { + var imageData = imageWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0; + + // sum up first row + for (var i = 0; i < width; i++) { + sum += imageData[i]; + integralImageData[i] = sum; + } + + for (var v = 1; v < height; v++) { + sum = 0; + for (var u = 0; u < width; u++) { + sum += imageData[v * width + u]; + integralImageData[v * width + u] = sum + integralImageData[(v - 1) * width + u]; + } + } + }; + + CVUtils.thresholdImage = function (imageWrapper, threshold, targetWrapper) { + if (!targetWrapper) { + targetWrapper = imageWrapper; + } + var imageData = imageWrapper.data, + length = imageData.length, + targetData = targetWrapper.data; + + while (length--) { + targetData[length] = imageData[length] < threshold ? 1 : 0; + } + }; + + CVUtils.computeHistogram = function (imageWrapper, bitsPerPixel) { + if (!bitsPerPixel) { + bitsPerPixel = 8; + } + var imageData = imageWrapper.data, + length = imageData.length, + bitShift = 8 - bitsPerPixel, + bucketCnt = 1 << bitsPerPixel, + hist = new Int32Array(bucketCnt); + + while (length--) { + hist[imageData[length] >> bitShift]++; + } + return hist; + }; + + CVUtils.sharpenLine = function (line) { + var i, + length = line.length, + left = line[0], + center = line[1], + right; + + for (i = 1; i < length - 1; i++) { + right = line[i + 1]; + // -1 4 -1 kernel + line[i - 1] = center * 2 - left - right & 255; + left = center; + center = right; + } + return line; + }; + + CVUtils.determineOtsuThreshold = function (imageWrapper, bitsPerPixel) { + if (!bitsPerPixel) { + bitsPerPixel = 8; + } + var hist, + threshold, + bitShift = 8 - bitsPerPixel; + + function px(init, end) { + var sum = 0, + i; + for (i = init; i <= end; i++) { + sum += hist[i]; + } + return sum; + } + + function mx(init, end) { + var i, + sum = 0; + + for (i = init; i <= end; i++) { + sum += i * hist[i]; + } + + return sum; + } + + function determineThreshold() { + var vet = [0], + p1, + p2, + p12, + k, + m1, + m2, + m12, + max = (1 << bitsPerPixel) - 1; + + hist = CVUtils.computeHistogram(imageWrapper, bitsPerPixel); + for (k = 1; k < max; k++) { + p1 = px(0, k); + p2 = px(k + 1, max); + p12 = p1 * p2; + if (p12 === 0) { + p12 = 1; + } + m1 = mx(0, k) * p2; + m2 = mx(k + 1, max) * p1; + m12 = m1 - m2; + vet[k] = m12 * m12 / p12; + } + return _array_helper2['default'].maxIndex(vet); + } + + threshold = determineThreshold(); + return threshold << bitShift; + }; + + CVUtils.otsuThreshold = function (imageWrapper, targetWrapper) { + var threshold = CVUtils.determineOtsuThreshold(imageWrapper); + + CVUtils.thresholdImage(imageWrapper, threshold, targetWrapper); + return threshold; + }; + + // local thresholding + CVUtils.computeBinaryImage = function (imageWrapper, integralWrapper, targetWrapper) { + CVUtils.computeIntegralImage(imageWrapper, integralWrapper); + + if (!targetWrapper) { + targetWrapper = imageWrapper; + } + var imageData = imageWrapper.data; + var targetData = targetWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0, + v, + u, + kernel = 3, + A, + B, + C, + D, + avg, + size = (kernel * 2 + 1) * (kernel * 2 + 1); + + // clear out top & bottom-border + for (v = 0; v <= kernel; v++) { + for (u = 0; u < width; u++) { + targetData[v * width + u] = 0; + targetData[(height - 1 - v) * width + u] = 0; + } + } + + // clear out left & right border + for (v = kernel; v < height - kernel; v++) { + for (u = 0; u <= kernel; u++) { + targetData[v * width + u] = 0; + targetData[v * width + (width - 1 - u)] = 0; + } + } + + for (v = kernel + 1; v < height - kernel - 1; v++) { + for (u = kernel + 1; u < width - kernel; u++) { + A = integralImageData[(v - kernel - 1) * width + (u - kernel - 1)]; + B = integralImageData[(v - kernel - 1) * width + (u + kernel)]; + C = integralImageData[(v + kernel) * width + (u - kernel - 1)]; + D = integralImageData[(v + kernel) * width + (u + kernel)]; + sum = D - C - B + A; + avg = sum / size; + targetData[v * width + u] = imageData[v * width + u] > avg + 5 ? 0 : 1; + } + } + }; + + CVUtils.cluster = function (points, threshold, property) { + var i, + k, + cluster, + point, + clusters = []; + + if (!property) { + property = "rad"; + } + + function addToCluster(newPoint) { + var found = false; + for (k = 0; k < clusters.length; k++) { + cluster = clusters[k]; + if (cluster.fits(newPoint)) { + cluster.add(newPoint); + found = true; + } + } + return found; + } + + // iterate over each cloud + for (i = 0; i < points.length; i++) { + point = _cluster2['default'].createPoint(points[i], i, property); + if (!addToCluster(point)) { + clusters.push(_cluster2['default'].create(point, threshold)); + } + } + return clusters; + }; + + CVUtils.Tracer = { + trace: function trace(points, vec) { + var iteration, + maxIterations = 10, + top = [], + result = [], + centerPos = 0, + currentPos = 0; + + function trace(idx, forward) { + var from, + to, + toIdx, + predictedPos, + thresholdX = 1, + thresholdY = Math.abs(vec[1] / 10), + found = false; + + function match(pos, predicted) { + if (pos.x > predicted.x - thresholdX && pos.x < predicted.x + thresholdX && pos.y > predicted.y - thresholdY && pos.y < predicted.y + thresholdY) { + return true; + } else { + return false; + } + } + + // check if the next index is within the vec specifications + // if not, check as long as the threshold is met + + from = points[idx]; + if (forward) { + predictedPos = { + x: from.x + vec[0], + y: from.y + vec[1] + }; + } else { + predictedPos = { + x: from.x - vec[0], + y: from.y - vec[1] + }; + } + + toIdx = forward ? idx + 1 : idx - 1; + to = points[toIdx]; + while (to && (found = match(to, predictedPos)) !== true && Math.abs(to.y - from.y) < vec[1]) { + toIdx = forward ? toIdx + 1 : toIdx - 1; + to = points[toIdx]; + } + + return found ? toIdx : null; + } + + for (iteration = 0; iteration < maxIterations; iteration++) { + // randomly select point to start with + centerPos = Math.floor(Math.random() * points.length); + + // trace forward + top = []; + currentPos = centerPos; + top.push(points[currentPos]); + while ((currentPos = trace(currentPos, true)) !== null) { + top.push(points[currentPos]); + } + if (centerPos > 0) { + currentPos = centerPos; + while ((currentPos = trace(currentPos, false)) !== null) { + top.push(points[currentPos]); + } + } + + if (top.length > result.length) { + result = top; + } + } + return result; + } + }; + + CVUtils.DILATE = 1; + CVUtils.ERODE = 2; + + CVUtils.dilate = function (inImageWrapper, outImageWrapper) { + var v, + u, + inImageData = inImageWrapper.data, + outImageData = outImageWrapper.data, + height = inImageWrapper.size.y, + width = inImageWrapper.size.x, + sum, + yStart1, + yStart2, + xStart1, + xStart2; + + for (v = 1; v < height - 1; v++) { + for (u = 1; u < width - 1; u++) { + yStart1 = v - 1; + yStart2 = v + 1; + xStart1 = u - 1; + xStart2 = u + 1; + sum = inImageData[yStart1 * width + xStart1] + inImageData[yStart1 * width + xStart2] + inImageData[v * width + u] + inImageData[yStart2 * width + xStart1] + inImageData[yStart2 * width + xStart2]; + outImageData[v * width + u] = sum > 0 ? 1 : 0; + } + } + }; + + CVUtils.erode = function (inImageWrapper, outImageWrapper) { + var v, + u, + inImageData = inImageWrapper.data, + outImageData = outImageWrapper.data, + height = inImageWrapper.size.y, + width = inImageWrapper.size.x, + sum, + yStart1, + yStart2, + xStart1, + xStart2; + + for (v = 1; v < height - 1; v++) { + for (u = 1; u < width - 1; u++) { + yStart1 = v - 1; + yStart2 = v + 1; + xStart1 = u - 1; + xStart2 = u + 1; + sum = inImageData[yStart1 * width + xStart1] + inImageData[yStart1 * width + xStart2] + inImageData[v * width + u] + inImageData[yStart2 * width + xStart1] + inImageData[yStart2 * width + xStart2]; + outImageData[v * width + u] = sum === 5 ? 1 : 0; + } + } + }; + + CVUtils.subtract = function (aImageWrapper, bImageWrapper, resultImageWrapper) { + if (!resultImageWrapper) { + resultImageWrapper = aImageWrapper; + } + var length = aImageWrapper.data.length, + aImageData = aImageWrapper.data, + bImageData = bImageWrapper.data, + cImageData = resultImageWrapper.data; + + while (length--) { + cImageData[length] = aImageData[length] - bImageData[length]; + } + }; + + CVUtils.bitwiseOr = function (aImageWrapper, bImageWrapper, resultImageWrapper) { + if (!resultImageWrapper) { + resultImageWrapper = aImageWrapper; + } + var length = aImageWrapper.data.length, + aImageData = aImageWrapper.data, + bImageData = bImageWrapper.data, + cImageData = resultImageWrapper.data; + + while (length--) { + cImageData[length] = aImageData[length] || bImageData[length]; + } + }; + + CVUtils.countNonZero = function (imageWrapper) { + var length = imageWrapper.data.length, + data = imageWrapper.data, + sum = 0; + + while (length--) { + sum += data[length]; + } + return sum; + }; + + CVUtils.topGeneric = function (list, top, scoreFunc) { + var i, + minIdx = 0, + min = 0, + queue = [], + score, + hit, + pos; + + for (i = 0; i < top; i++) { + queue[i] = { + score: 0, + item: null + }; + } + + for (i = 0; i < list.length; i++) { + score = scoreFunc.apply(this, [list[i]]); + if (score > min) { + hit = queue[minIdx]; + hit.score = score; + hit.item = list[i]; + min = Number.MAX_VALUE; + for (pos = 0; pos < top; pos++) { + if (queue[pos].score < min) { + min = queue[pos].score; + minIdx = pos; + } + } + } + } + + return queue; + }; + + CVUtils.grayArrayFromImage = function (htmlImage, offsetX, ctx, array) { + ctx.drawImage(htmlImage, offsetX, 0, htmlImage.width, htmlImage.height); + var ctxData = ctx.getImageData(offsetX, 0, htmlImage.width, htmlImage.height).data; + CVUtils.computeGray(ctxData, array); + }; + + CVUtils.grayArrayFromContext = function (ctx, size, offset, array) { + var ctxData = ctx.getImageData(offset.x, offset.y, size.x, size.y).data; + CVUtils.computeGray(ctxData, array); + }; + + CVUtils.grayAndHalfSampleFromCanvasData = function (canvasData, size, outArray) { + var topRowIdx = 0; + var bottomRowIdx = size.x; + var endIdx = Math.floor(canvasData.length / 4); + var outWidth = size.x / 2; + var outImgIdx = 0; + var inWidth = size.x; + var i; + + while (bottomRowIdx < endIdx) { + for (i = 0; i < outWidth; i++) { + outArray[outImgIdx] = Math.floor((0.299 * canvasData[topRowIdx * 4 + 0] + 0.587 * canvasData[topRowIdx * 4 + 1] + 0.114 * canvasData[topRowIdx * 4 + 2] + (0.299 * canvasData[(topRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(topRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(topRowIdx + 1) * 4 + 2]) + (0.299 * canvasData[bottomRowIdx * 4 + 0] + 0.587 * canvasData[bottomRowIdx * 4 + 1] + 0.114 * canvasData[bottomRowIdx * 4 + 2]) + (0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4); + outImgIdx++; + topRowIdx = topRowIdx + 2; + bottomRowIdx = bottomRowIdx + 2; + } + topRowIdx = topRowIdx + inWidth; + bottomRowIdx = bottomRowIdx + inWidth; + } + }; + + CVUtils.computeGray = function (imageData, outArray, config) { + var l = imageData.length / 4 | 0, + i, + singleChannel = config && config.singleChannel === true; + + if (singleChannel) { + for (i = 0; i < l; i++) { + outArray[i] = imageData[i * 4 + 0]; + } + } else { + for (i = 0; i < l; i++) { + outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); + } + } + }; + + CVUtils.loadImageArray = function (src, callback, canvas) { + if (!canvas) { + canvas = document.createElement('canvas'); + } + var img = new Image(); + img.callback = callback; + img.onload = function () { + canvas.width = this.width; + canvas.height = this.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0); + var array = new Uint8Array(this.width * this.height); + ctx.drawImage(this, 0, 0); + var data = ctx.getImageData(0, 0, this.width, this.height).data; + CVUtils.computeGray(data, array); + this.callback(array, { + x: this.width, + y: this.height + }, this); + }; + img.src = src; + }; + + /** + * @param inImg {ImageWrapper} input image to be sampled + * @param outImg {ImageWrapper} to be stored in + */ + CVUtils.halfSample = function (inImgWrapper, outImgWrapper) { + var inImg = inImgWrapper.data; + var inWidth = inImgWrapper.size.x; + var outImg = outImgWrapper.data; + var topRowIdx = 0; + var bottomRowIdx = inWidth; + var endIdx = inImg.length; + var outWidth = inWidth / 2; + var outImgIdx = 0; + while (bottomRowIdx < endIdx) { + for (var i = 0; i < outWidth; i++) { + outImg[outImgIdx] = Math.floor((inImg[topRowIdx] + inImg[topRowIdx + 1] + inImg[bottomRowIdx] + inImg[bottomRowIdx + 1]) / 4); + outImgIdx++; + topRowIdx = topRowIdx + 2; + bottomRowIdx = bottomRowIdx + 2; + } + topRowIdx = topRowIdx + inWidth; + bottomRowIdx = bottomRowIdx + inWidth; + } + }; + + CVUtils.hsv2rgb = function (hsv, rgb) { + var h = hsv[0], + s = hsv[1], + v = hsv[2], + c = v * s, + x = c * (1 - Math.abs(h / 60 % 2 - 1)), + m = v - c, + r = 0, + g = 0, + b = 0; + + rgb = rgb || [0, 0, 0]; + + if (h < 60) { + r = c; + g = x; + } else if (h < 120) { + r = x; + g = c; + } else if (h < 180) { + g = c; + b = x; + } else if (h < 240) { + g = x; + b = c; + } else if (h < 300) { + r = x; + b = c; + } else if (h < 360) { + r = c; + b = x; + } + rgb[0] = (r + m) * 255 | 0; + rgb[1] = (g + m) * 255 | 0; + rgb[2] = (b + m) * 255 | 0; + return rgb; + }; + + CVUtils._computeDivisors = function (n) { + var largeDivisors = [], + divisors = [], + i; + + for (i = 1; i < Math.sqrt(n) + 1; i++) { + if (n % i === 0) { + divisors.push(i); + if (i !== n / i) { + largeDivisors.unshift(Math.floor(n / i)); + } + } + } + return divisors.concat(largeDivisors); + }; + + CVUtils._computeIntersection = function (arr1, arr2) { + var i = 0, + j = 0, + result = []; + + while (i < arr1.length && j < arr2.length) { + if (arr1[i] === arr2[j]) { + result.push(arr1[i]); + i++; + j++; + } else if (arr1[i] > arr2[j]) { + j++; + } else { + i++; + } + } + return result; + }; + + CVUtils.calculatePatchSize = function (patchSize, imgSize) { + var divisorsX = this._computeDivisors(imgSize.x), + divisorsY = this._computeDivisors(imgSize.y), + wideSide = Math.max(imgSize.x, imgSize.y), + common = this._computeIntersection(divisorsX, divisorsY), + nrOfPatchesList = [8, 10, 15, 20, 32, 60, 80], + nrOfPatchesMap = { + "x-small": 5, + "small": 4, + "medium": 3, + "large": 2, + "x-large": 1 + }, + nrOfPatchesIdx = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium, + nrOfPatches = nrOfPatchesList[nrOfPatchesIdx], + desiredPatchSize = Math.floor(wideSide / nrOfPatches), + optimalPatchSize; + + function findPatchSizeForDivisors(divisors) { + var i = 0, + found = divisors[Math.floor(divisors.length / 2)]; + + while (i < divisors.length - 1 && divisors[i] < desiredPatchSize) { + i++; + } + if (i > 0) { + if (Math.abs(divisors[i] - desiredPatchSize) > Math.abs(divisors[i - 1] - desiredPatchSize)) { + found = divisors[i - 1]; + } else { + found = divisors[i]; + } + } + if (desiredPatchSize / found < nrOfPatchesList[nrOfPatchesIdx + 1] / nrOfPatchesList[nrOfPatchesIdx] && desiredPatchSize / found > nrOfPatchesList[nrOfPatchesIdx - 1] / nrOfPatchesList[nrOfPatchesIdx]) { + return { x: found, y: found }; + } + return null; + } + + optimalPatchSize = findPatchSizeForDivisors(common); + if (!optimalPatchSize) { + optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(wideSide)); + if (!optimalPatchSize) { + optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(desiredPatchSize * nrOfPatches)); + } + } + return optimalPatchSize; + }; + + CVUtils._parseCSSDimensionValues = function (value) { + var dimension = { + value: parseFloat(value), + unit: value.indexOf("%") === value.length - 1 ? "%" : "%" + }; + + return dimension; + }; + + CVUtils._dimensionsConverters = { + top: function top(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.height * (dimension.value / 100)); + } + }, + right: function right(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.width - context.width * (dimension.value / 100)); + } + }, + bottom: function bottom(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.height - context.height * (dimension.value / 100)); + } + }, + left: function left(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.width * (dimension.value / 100)); + } + } + }; + + CVUtils.computeImageArea = function (inputWidth, inputHeight, area) { + var context = { width: inputWidth, height: inputHeight }; + + var parsedArea = Object.keys(area).reduce(function (result, key) { + var value = area[key], + parsed = CVUtils._parseCSSDimensionValues(value), + calculated = CVUtils._dimensionsConverters[key](parsed, context); + + result[key] = calculated; + return result; + }, {}); + + return { + sx: parsedArea.left, + sy: parsedArea.top, + sw: parsedArea.right - parsedArea.left, + sh: parsedArea.bottom - parsedArea.top + }; + }; + + exports['default'] = CVUtils; + module.exports = exports['default']; + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _glMatrix = __webpack_require__(7); + + /** + * Creates a cluster for grouping similar orientations of datapoints + */ + exports['default'] = { + create: function create(point, threshold) { + var points = [], + center = { + rad: 0, + vec: _glMatrix.vec2.clone([0, 0]) + }, + pointMap = {}; + + function init() { + _add(point); + updateCenter(); + } + + function _add(pointToAdd) { + pointMap[pointToAdd.id] = pointToAdd; + points.push(pointToAdd); + } + + function updateCenter() { + var i, + sum = 0; + for (i = 0; i < points.length; i++) { + sum += points[i].rad; + } + center.rad = sum / points.length; + center.vec = _glMatrix.vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]); + } + + init(); + + return { + add: function add(pointToAdd) { + if (!pointMap[pointToAdd.id]) { + _add(pointToAdd); + updateCenter(); + } + }, + fits: function fits(otherPoint) { + // check cosine similarity to center-angle + var similarity = Math.abs(_glMatrix.vec2.dot(otherPoint.point.vec, center.vec)); + if (similarity > threshold) { + return true; + } + return false; + }, + getPoints: function getPoints() { + return points; + }, + getCenter: function getCenter() { + return center; + } + }; + }, + createPoint: function createPoint(newPoint, id, property) { + return { + rad: newPoint[property], + point: newPoint, + id: id + }; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + module.exports = require("gl-matrix"); + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports["default"] = { + init: function init(arr, val) { + var l = arr.length; + while (l--) { + arr[l] = val; + } + }, + + /** + * Shuffles the content of an array + * @return {Array} the array itself shuffled + */ + shuffle: function shuffle(arr) { + var i = arr.length - 1, + j, + x; + for (i; i >= 0; i--) { + j = Math.floor(Math.random() * i); + x = arr[i]; + arr[i] = arr[j]; + arr[j] = x; + } + return arr; + }, + + toPointList: function toPointList(arr) { + var i, + j, + row = [], + rows = []; + for (i = 0; i < arr.length; i++) { + row = []; + for (j = 0; j < arr[i].length; j++) { + row[j] = arr[i][j]; + } + rows[i] = "[" + row.join(",") + "]"; + } + return "[" + rows.join(",\r\n") + "]"; + }, + + /** + * returns the elements which's score is bigger than the threshold + * @return {Array} the reduced array + */ + threshold: function threshold(arr, _threshold, scoreFunc) { + var i, + queue = []; + for (i = 0; i < arr.length; i++) { + if (scoreFunc.apply(arr, [arr[i]]) >= _threshold) { + queue.push(arr[i]); + } + } + return queue; + }, + + maxIndex: function maxIndex(arr) { + var i, + max = 0; + for (i = 0; i < arr.length; i++) { + if (arr[i] > arr[max]) { + max = i; + } + } + return max; + }, + + max: function max(arr) { + var i, + max = 0; + for (i = 0; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + } + return max; + }, + + sum: function sum(arr) { + var length = arr.length, + sum = 0; + + while (length--) { + sum += arr[length]; + } + return sum; + } + }; + module.exports = exports["default"]; + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _image_wrapper = __webpack_require__(3); + + var _image_wrapper2 = _interopRequireDefault(_image_wrapper); + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var _rasterizer = __webpack_require__(10); + + var _rasterizer2 = _interopRequireDefault(_rasterizer); + + var _tracer = __webpack_require__(11); + + var _tracer2 = _interopRequireDefault(_tracer); + + var _skeletonizer2 = __webpack_require__(12); + + var _skeletonizer3 = _interopRequireDefault(_skeletonizer2); + + var _array_helper = __webpack_require__(8); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + var _image_debug = __webpack_require__(13); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + var _glMatrix = __webpack_require__(7); + + var _glMatrix2 = _interopRequireDefault(_glMatrix); + + var _config, + _currentImageWrapper, + _skelImageWrapper, + _subImageWrapper, + _labelImageWrapper, + _patchGrid, + _patchLabelGrid, + _imageToPatchGrid, + _binaryImageWrapper, + _patchSize, + _canvasContainer = { + ctx: { + binary: null + }, + dom: { + binary: null + } + }, + _numPatches = { x: 0, y: 0 }, + _inputImageWrapper, + _skeletonizer, + vec2 = _glMatrix2['default'].vec2, + mat2 = _glMatrix2['default'].mat2; + + function initBuffers() { + var skeletonImageData; + + if (_config.halfSample) { + _currentImageWrapper = new _image_wrapper2['default']({ + x: _inputImageWrapper.size.x / 2 | 0, + y: _inputImageWrapper.size.y / 2 | 0 + }); + } else { + _currentImageWrapper = _inputImageWrapper; + } + + _patchSize = _cv_utils2['default'].calculatePatchSize(_config.patchSize, _currentImageWrapper.size); + + _numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0; + _numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0; + + _binaryImageWrapper = new _image_wrapper2['default'](_currentImageWrapper.size, undefined, Uint8Array, false); + + _labelImageWrapper = new _image_wrapper2['default'](_patchSize, undefined, Array, true); + + skeletonImageData = new ArrayBuffer(64 * 1024); + _subImageWrapper = new _image_wrapper2['default'](_patchSize, new Uint8Array(skeletonImageData, 0, _patchSize.x * _patchSize.y)); + _skelImageWrapper = new _image_wrapper2['default'](_patchSize, new Uint8Array(skeletonImageData, _patchSize.x * _patchSize.y * 3, _patchSize.x * _patchSize.y), undefined, true); + _skeletonizer = (0, _skeletonizer3['default'])(typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : global, { + size: _patchSize.x + }, skeletonImageData); + + _imageToPatchGrid = new _image_wrapper2['default']({ + x: _currentImageWrapper.size.x / _subImageWrapper.size.x | 0, + y: _currentImageWrapper.size.y / _subImageWrapper.size.y | 0 + }, undefined, Array, true); + _patchGrid = new _image_wrapper2['default'](_imageToPatchGrid.size, undefined, undefined, true); + _patchLabelGrid = new _image_wrapper2['default'](_imageToPatchGrid.size, undefined, Int32Array, true); + } + + function initCanvas() { + if (_config.useWorker || typeof document === 'undefined') { + return; + } + _canvasContainer.dom.binary = document.createElement("canvas"); + _canvasContainer.dom.binary.className = "binaryBuffer"; + if (_config.showCanvas === true) { + document.querySelector("#debug").appendChild(_canvasContainer.dom.binary); + } + _canvasContainer.ctx.binary = _canvasContainer.dom.binary.getContext("2d"); + _canvasContainer.dom.binary.width = _binaryImageWrapper.size.x; + _canvasContainer.dom.binary.height = _binaryImageWrapper.size.y; + } + + /** + * Creates a bounding box which encloses all the given patches + * @returns {Array} The minimal bounding box + */ + function boxFromPatches(patches) { + var overAvg, + i, + j, + patch, + transMat, + minx = _binaryImageWrapper.size.x, + miny = _binaryImageWrapper.size.y, + maxx = -_binaryImageWrapper.size.x, + maxy = -_binaryImageWrapper.size.y, + box, + scale; + + // draw all patches which are to be taken into consideration + overAvg = 0; + for (i = 0; i < patches.length; i++) { + patch = patches[i]; + overAvg += patch.rad; + if (_config.showPatches) { + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "red" }); + } + } + + overAvg /= patches.length; + overAvg = (overAvg * 180 / Math.PI + 90) % 180 - 90; + if (overAvg < 0) { + overAvg += 180; + } + + overAvg = (180 - overAvg) * Math.PI / 180; + transMat = mat2.clone([Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]); + + // iterate over patches and rotate by angle + for (i = 0; i < patches.length; i++) { + patch = patches[i]; + for (j = 0; j < 4; j++) { + vec2.transformMat2(patch.box[j], patch.box[j], transMat); + } + + if (_config.boxFromPatches.showTransformed) { + _image_debug2['default'].drawPath(patch.box, { x: 0, y: 1 }, _canvasContainer.ctx.binary, { color: '#99ff00', lineWidth: 2 }); + } + } + + // find bounding box + for (i = 0; i < patches.length; i++) { + patch = patches[i]; + for (j = 0; j < 4; j++) { + if (patch.box[j][0] < minx) { + minx = patch.box[j][0]; + } + if (patch.box[j][0] > maxx) { + maxx = patch.box[j][0]; + } + if (patch.box[j][1] < miny) { + miny = patch.box[j][1]; + } + if (patch.box[j][1] > maxy) { + maxy = patch.box[j][1]; + } + } + } + + box = [[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy]]; + + if (_config.boxFromPatches.showTransformedBox) { + _image_debug2['default'].drawPath(box, { x: 0, y: 1 }, _canvasContainer.ctx.binary, { color: '#ff0000', lineWidth: 2 }); + } + + scale = _config.halfSample ? 2 : 1; + // reverse rotation; + transMat = mat2.invert(transMat, transMat); + for (j = 0; j < 4; j++) { + vec2.transformMat2(box[j], box[j], transMat); + } + + if (_config.boxFromPatches.showBB) { + _image_debug2['default'].drawPath(box, { x: 0, y: 1 }, _canvasContainer.ctx.binary, { color: '#ff0000', lineWidth: 2 }); + } + + for (j = 0; j < 4; j++) { + vec2.scale(box[j], box[j], scale); + } + + return box; + } + + /** + * Creates a binary image of the current image + */ + function binarizeImage() { + _cv_utils2['default'].otsuThreshold(_currentImageWrapper, _binaryImageWrapper); + _binaryImageWrapper.zeroBorder(); + if (_config.showCanvas) { + _binaryImageWrapper.show(_canvasContainer.dom.binary, 255); + } + } + + /** + * Iterate over the entire image + * extract patches + */ + function findPatches() { + var i, + j, + x, + y, + moments, + patchesFound = [], + rasterizer, + rasterResult, + patch; + for (i = 0; i < _numPatches.x; i++) { + for (j = 0; j < _numPatches.y; j++) { + x = _subImageWrapper.size.x * i; + y = _subImageWrapper.size.y * j; + + // seperate parts + skeletonize(x, y); + + // Rasterize, find individual bars + _skelImageWrapper.zeroBorder(); + _array_helper2['default'].init(_labelImageWrapper.data, 0); + rasterizer = _rasterizer2['default'].create(_skelImageWrapper, _labelImageWrapper); + rasterResult = rasterizer.rasterize(0); + + if (_config.showLabels) { + _labelImageWrapper.overlay(_canvasContainer.dom.binary, Math.floor(360 / rasterResult.count), { x: x, y: y }); + } + + // calculate moments from the skeletonized patch + moments = _labelImageWrapper.moments(rasterResult.count); + + // extract eligible patches + patchesFound = patchesFound.concat(describePatch(moments, [i, j], x, y)); + } + } + + if (_config.showFoundPatches) { + for (i = 0; i < patchesFound.length; i++) { + patch = patchesFound[i]; + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "#99ff00", lineWidth: 2 }); + } + } + + return patchesFound; + } + + /** + * Finds those connected areas which contain at least 6 patches + * and returns them ordered DESC by the number of contained patches + * @param {Number} maxLabel + */ + function findBiggestConnectedAreas(maxLabel) { + var i, + sum, + labelHist = [], + topLabels = []; + + for (i = 0; i < maxLabel; i++) { + labelHist.push(0); + } + sum = _patchLabelGrid.data.length; + while (sum--) { + if (_patchLabelGrid.data[sum] > 0) { + labelHist[_patchLabelGrid.data[sum] - 1]++; + } + } + + labelHist = labelHist.map(function (val, idx) { + return { + val: val, + label: idx + 1 + }; + }); + + labelHist.sort(function (a, b) { + return b.val - a.val; + }); + + // extract top areas with at least 6 patches present + topLabels = labelHist.filter(function (el) { + return el.val >= 5; + }); + + return topLabels; + } + + /** + * + */ + function findBoxes(topLabels, maxLabel) { + var i, + j, + sum, + patches = [], + patch, + box, + boxes = [], + hsv = [0, 1, 1], + rgb = [0, 0, 0]; + + for (i = 0; i < topLabels.length; i++) { + sum = _patchLabelGrid.data.length; + patches.length = 0; + while (sum--) { + if (_patchLabelGrid.data[sum] === topLabels[i].label) { + patch = _imageToPatchGrid.data[sum]; + patches.push(patch); + } + } + box = boxFromPatches(patches); + if (box) { + boxes.push(box); + + // draw patch-labels if requested + if (_config.showRemainingPatchLabels) { + for (j = 0; j < patches.length; j++) { + patch = patches[j]; + hsv[0] = topLabels[i].label / (maxLabel + 1) * 360; + _cv_utils2['default'].hsv2rgb(hsv, rgb); + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "rgb(" + rgb.join(",") + ")", lineWidth: 2 }); + } + } + } + } + return boxes; + } + + /** + * Find similar moments (via cluster) + * @param {Object} moments + */ + function similarMoments(moments) { + var clusters = _cv_utils2['default'].cluster(moments, 0.90); + var topCluster = _cv_utils2['default'].topGeneric(clusters, 1, function (e) { + return e.getPoints().length; + }); + var points = [], + result = []; + if (topCluster.length === 1) { + points = topCluster[0].item.getPoints(); + for (var i = 0; i < points.length; i++) { + result.push(points[i].point); + } + } + return result; + } + + function skeletonize(x, y) { + _binaryImageWrapper.subImageAsCopy(_subImageWrapper, _cv_utils2['default'].imageRef(x, y)); + _skeletonizer.skeletonize(); + + // Show skeleton if requested + if (_config.showSkeleton) { + _skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, _cv_utils2['default'].imageRef(x, y)); + } + } + + /** + * Extracts and describes those patches which seem to contain a barcode pattern + * @param {Array} moments + * @param {Object} patchPos, + * @param {Number} x + * @param {Number} y + * @returns {Array} list of patches + */ + function describePatch(moments, patchPos, x, y) { + var k, + avg, + eligibleMoments = [], + matchingMoments, + patch, + patchesFound = [], + minComponentWeight = Math.ceil(_patchSize.x / 3); + + if (moments.length >= 2) { + // only collect moments which's area covers at least minComponentWeight pixels. + for (k = 0; k < moments.length; k++) { + if (moments[k].m00 > minComponentWeight) { + eligibleMoments.push(moments[k]); + } + } + + // if at least 2 moments are found which have at least minComponentWeights covered + if (eligibleMoments.length >= 2) { + matchingMoments = similarMoments(eligibleMoments); + avg = 0; + // determine the similarity of the moments + for (k = 0; k < matchingMoments.length; k++) { + avg += matchingMoments[k].rad; + } + + // Only two of the moments are allowed not to fit into the equation + // add the patch to the set + if (matchingMoments.length > 1 && matchingMoments.length >= eligibleMoments.length / 4 * 3 && matchingMoments.length > moments.length / 4) { + avg /= matchingMoments.length; + patch = { + index: patchPos[1] * _numPatches.x + patchPos[0], + pos: { + x: x, + y: y + }, + box: [vec2.clone([x, y]), vec2.clone([x + _subImageWrapper.size.x, y]), vec2.clone([x + _subImageWrapper.size.x, y + _subImageWrapper.size.y]), vec2.clone([x, y + _subImageWrapper.size.y])], + moments: matchingMoments, + rad: avg, + vec: vec2.clone([Math.cos(avg), Math.sin(avg)]) + }; + patchesFound.push(patch); + } + } + } + return patchesFound; + } + + /** + * finds patches which are connected and share the same orientation + * @param {Object} patchesFound + */ + function rasterizeAngularSimilarity(patchesFound) { + var label = 0, + threshold = 0.95, + currIdx = 0, + j, + patch, + hsv = [0, 1, 1], + rgb = [0, 0, 0]; + + function notYetProcessed() { + var i; + for (i = 0; i < _patchLabelGrid.data.length; i++) { + if (_patchLabelGrid.data[i] === 0 && _patchGrid.data[i] === 1) { + return i; + } + } + return _patchLabelGrid.length; + } + + function trace(currentIdx) { + var x, + y, + currentPatch, + idx, + dir, + current = { + x: currentIdx % _patchLabelGrid.size.x, + y: currentIdx / _patchLabelGrid.size.x | 0 + }, + similarity; + + if (currentIdx < _patchLabelGrid.data.length) { + currentPatch = _imageToPatchGrid.data[currentIdx]; + // assign label + _patchLabelGrid.data[currentIdx] = label; + for (dir = 0; dir < _tracer2['default'].searchDirections.length; dir++) { + y = current.y + _tracer2['default'].searchDirections[dir][0]; + x = current.x + _tracer2['default'].searchDirections[dir][1]; + idx = y * _patchLabelGrid.size.x + x; + + // continue if patch empty + if (_patchGrid.data[idx] === 0) { + _patchLabelGrid.data[idx] = Number.MAX_VALUE; + continue; + } + + if (_patchLabelGrid.data[idx] === 0) { + similarity = Math.abs(vec2.dot(_imageToPatchGrid.data[idx].vec, currentPatch.vec)); + if (similarity > threshold) { + trace(idx); + } + } + } + } + } + + // prepare for finding the right patches + _array_helper2['default'].init(_patchGrid.data, 0); + _array_helper2['default'].init(_patchLabelGrid.data, 0); + _array_helper2['default'].init(_imageToPatchGrid.data, null); + + for (j = 0; j < patchesFound.length; j++) { + patch = patchesFound[j]; + _imageToPatchGrid.data[patch.index] = patch; + _patchGrid.data[patch.index] = 1; + } + + // rasterize the patches found to determine area + _patchGrid.zeroBorder(); + + while ((currIdx = notYetProcessed()) < _patchLabelGrid.data.length) { + label++; + trace(currIdx); + } + + // draw patch-labels if requested + if (_config.showPatchLabels) { + for (j = 0; j < _patchLabelGrid.data.length; j++) { + if (_patchLabelGrid.data[j] > 0 && _patchLabelGrid.data[j] <= label) { + patch = _imageToPatchGrid.data[j]; + hsv[0] = _patchLabelGrid.data[j] / (label + 1) * 360; + _cv_utils2['default'].hsv2rgb(hsv, rgb); + _image_debug2['default'].drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, { color: "rgb(" + rgb.join(",") + ")", lineWidth: 2 }); + } + } + } + + return label; + } + + exports['default'] = { + init: function init(inputImageWrapper, config) { + _config = config; + _inputImageWrapper = inputImageWrapper; + + initBuffers(); + initCanvas(); + }, + + locate: function locate() { + var patchesFound, topLabels, boxes; + + if (_config.halfSample) { + _cv_utils2['default'].halfSample(_inputImageWrapper, _currentImageWrapper); + } + + binarizeImage(); + patchesFound = findPatches(); + // return unless 5% or more patches are found + if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) { + return null; + } + + // rasterrize area by comparing angular similarity; + var maxLabel = rasterizeAngularSimilarity(patchesFound); + if (maxLabel < 1) { + return null; + } + + // search for area with the most patches (biggest connected area) + topLabels = findBiggestConnectedAreas(maxLabel); + if (topLabels.length === 0) { + return null; + } + + boxes = findBoxes(topLabels, maxLabel); + return boxes; + }, + + checkImageConstraints: function checkImageConstraints(inputStream, config) { + var patchSize, + width = inputStream.getWidth(), + height = inputStream.getHeight(), + halfSample = config.halfSample ? 0.5 : 1, + size, + area; + + // calculate width and height based on area + if (inputStream.getConfig().area) { + area = _cv_utils2['default'].computeImageArea(width, height, inputStream.getConfig().area); + inputStream.setTopRight({ x: area.sx, y: area.sy }); + inputStream.setCanvasSize({ x: width, y: height }); + width = area.sw; + height = area.sh; + } + + size = { + x: Math.floor(width * halfSample), + y: Math.floor(height * halfSample) + }; + + patchSize = _cv_utils2['default'].calculatePatchSize(config.patchSize, size); + console.log("Patch-Size: " + JSON.stringify(patchSize)); + + inputStream.setWidth(Math.floor(Math.floor(size.x / patchSize.x) * (1 / halfSample) * patchSize.x)); + inputStream.setHeight(Math.floor(Math.floor(size.y / patchSize.y) * (1 / halfSample) * patchSize.y)); + + if (inputStream.getWidth() % patchSize.x === 0 && inputStream.getHeight() % patchSize.y === 0) { + return true; + } + + throw new Error("Image dimensions do not comply with the current settings: Width (" + width + " )and height (" + height + ") must a multiple of " + patchSize.x); + } + }; + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _tracer = __webpack_require__(11); + + var _tracer2 = _interopRequireDefault(_tracer); + + /** + * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization + */ + var Rasterizer = { + createContour2D: function createContour2D() { + return { + dir: null, + index: null, + firstVertex: null, + insideContours: null, + nextpeer: null, + prevpeer: null + }; + }, + CONTOUR_DIR: { + CW_DIR: 0, + CCW_DIR: 1, + UNKNOWN_DIR: 2 + }, + DIR: { + OUTSIDE_EDGE: -32767, + INSIDE_EDGE: -32766 + }, + create: function create(imageWrapper, labelWrapper) { + var imageData = imageWrapper.data, + labelData = labelWrapper.data, + width = imageWrapper.size.x, + height = imageWrapper.size.y, + tracer = _tracer2["default"].create(imageWrapper, labelWrapper); + + return { + rasterize: function rasterize(depthlabel) { + var color, + bc, + lc, + labelindex, + cx, + cy, + colorMap = [], + vertex, + p, + cc, + sc, + pos, + connectedCount = 0, + i; + + for (i = 0; i < 400; i++) { + colorMap[i] = 0; + } + + colorMap[0] = imageData[0]; + cc = null; + for (cy = 1; cy < height - 1; cy++) { + labelindex = 0; + bc = colorMap[0]; + for (cx = 1; cx < width - 1; cx++) { + pos = cy * width + cx; + if (labelData[pos] === 0) { + color = imageData[pos]; + if (color !== bc) { + if (labelindex === 0) { + lc = connectedCount + 1; + colorMap[lc] = color; + bc = color; + vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE); + if (vertex !== null) { + connectedCount++; + labelindex = lc; + p = Rasterizer.createContour2D(); + p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; + p.index = labelindex; + p.firstVertex = vertex; + p.nextpeer = cc; + p.insideContours = null; + if (cc !== null) { + cc.prevpeer = p; + } + cc = p; + } + } else { + vertex = tracer.contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex); + if (vertex !== null) { + p = Rasterizer.createContour2D(); + p.firstVertex = vertex; + p.insideContours = null; + if (depthlabel === 0) { + p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR; + } else { + p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; + } + p.index = depthlabel; + sc = cc; + while (sc !== null && sc.index !== labelindex) { + sc = sc.nextpeer; + } + if (sc !== null) { + p.nextpeer = sc.insideContours; + if (sc.insideContours !== null) { + sc.insideContours.prevpeer = p; + } + sc.insideContours = p; + } + } + } + } else { + labelData[pos] = labelindex; + } + } else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { + labelindex = 0; + if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { + bc = imageData[pos]; + } else { + bc = colorMap[0]; + } + } else { + labelindex = labelData[pos]; + bc = colorMap[labelindex]; + } + } + } + sc = cc; + while (sc !== null) { + sc.index = depthlabel; + sc = sc.nextpeer; + } + return { + cc: cc, + count: connectedCount + }; + }, + debug: { + drawContour: function drawContour(canvas, firstContour) { + var ctx = canvas.getContext("2d"), + pq = firstContour, + iq, + q, + p; + + ctx.strokeStyle = "red"; + ctx.fillStyle = "red"; + ctx.lineWidth = 1; + + if (pq !== null) { + iq = pq.insideContours; + } else { + iq = null; + } + + while (pq !== null) { + if (iq !== null) { + q = iq; + iq = iq.nextpeer; + } else { + q = pq; + pq = pq.nextpeer; + if (pq !== null) { + iq = pq.insideContours; + } else { + iq = null; + } + } + + switch (q.dir) { + case Rasterizer.CONTOUR_DIR.CW_DIR: + ctx.strokeStyle = "red"; + break; + case Rasterizer.CONTOUR_DIR.CCW_DIR: + ctx.strokeStyle = "blue"; + break; + case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR: + ctx.strokeStyle = "green"; + break; + } + + p = q.firstVertex; + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + do { + p = p.next; + ctx.lineTo(p.x, p.y); + } while (p !== q.firstVertex); + ctx.stroke(); + } + } + } + }; + } + }; + + exports["default"] = Rasterizer; + module.exports = exports["default"]; + +/***/ }, +/* 11 */ +/***/ function(module, exports) { + + /** + * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization + */ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var Tracer = { + searchDirections: [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]], + create: function create(imageWrapper, labelWrapper) { + var imageData = imageWrapper.data, + labelData = labelWrapper.data, + searchDirections = this.searchDirections, + width = imageWrapper.size.x, + pos; + + function _trace(current, color, label, edgelabel) { + var i, y, x; + + for (i = 0; i < 7; i++) { + y = current.cy + searchDirections[current.dir][0]; + x = current.cx + searchDirections[current.dir][1]; + pos = y * width + x; + if (imageData[pos] === color && (labelData[pos] === 0 || labelData[pos] === label)) { + labelData[pos] = label; + current.cy = y; + current.cx = x; + return true; + } else { + if (labelData[pos] === 0) { + labelData[pos] = edgelabel; + } + current.dir = (current.dir + 1) % 8; + } + } + return false; + } + + function vertex2D(x, y, dir) { + return { + dir: dir, + x: x, + y: y, + next: null, + prev: null + }; + } + + function _contourTracing(sy, sx, label, color, edgelabel) { + var Fv = null, + Cv, + P, + ldir, + current = { + cx: sx, + cy: sy, + dir: 0 + }; + + if (_trace(current, color, label, edgelabel)) { + Fv = vertex2D(sx, sy, current.dir); + Cv = Fv; + ldir = current.dir; + P = vertex2D(current.cx, current.cy, 0); + P.prev = Cv; + Cv.next = P; + P.next = null; + Cv = P; + do { + current.dir = (current.dir + 6) % 8; + _trace(current, color, label, edgelabel); + if (ldir !== current.dir) { + Cv.dir = current.dir; + P = vertex2D(current.cx, current.cy, 0); + P.prev = Cv; + Cv.next = P; + P.next = null; + Cv = P; + } else { + Cv.dir = ldir; + Cv.x = current.cx; + Cv.y = current.cy; + } + ldir = current.dir; + } while (current.cx !== sx || current.cy !== sy); + Fv.prev = Cv.prev; + Cv.prev.next = Fv; + } + return Fv; + } + + return { + trace: function trace(current, color, label, edgelabel) { + return _trace(current, color, label, edgelabel); + }, + contourTracing: function contourTracing(sy, sx, label, color, edgelabel) { + return _contourTracing(sy, sx, label, color, edgelabel); + } + }; + } + }; + + exports["default"] = Tracer; + module.exports = exports["default"]; + +/***/ }, +/* 12 */ +/***/ function(module, exports) { + + /* @preserve ASM BEGIN */ + /* eslint-disable eqeqeq*/ + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + function Skeletonizer(stdlib, foreign, buffer) { + "use asm"; + + var images = new stdlib.Uint8Array(buffer), + size = foreign.size | 0, + imul = stdlib.Math.imul; + + function erode(inImagePtr, outImagePtr) { + inImagePtr = inImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var v = 0, + u = 0, + sum = 0, + yStart1 = 0, + yStart2 = 0, + xStart1 = 0, + xStart2 = 0, + offset = 0; + + for (v = 1; (v | 0) < (size - 1 | 0); v = v + 1 | 0) { + offset = offset + size | 0; + for (u = 1; (u | 0) < (size - 1 | 0); u = u + 1 | 0) { + yStart1 = offset - size | 0; + yStart2 = offset + size | 0; + xStart1 = u - 1 | 0; + xStart2 = u + 1 | 0; + sum = (images[inImagePtr + yStart1 + xStart1 | 0] | 0) + (images[inImagePtr + yStart1 + xStart2 | 0] | 0) + (images[inImagePtr + offset + u | 0] | 0) + (images[inImagePtr + yStart2 + xStart1 | 0] | 0) + (images[inImagePtr + yStart2 + xStart2 | 0] | 0) | 0; + if ((sum | 0) == (5 | 0)) { + images[outImagePtr + offset + u | 0] = 1; + } else { + images[outImagePtr + offset + u | 0] = 0; + } + } + } + return; + } + + function subtract(aImagePtr, bImagePtr, outImagePtr) { + aImagePtr = aImagePtr | 0; + bImagePtr = bImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[outImagePtr + length | 0] = (images[aImagePtr + length | 0] | 0) - (images[bImagePtr + length | 0] | 0) | 0; + } + } + + function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) { + aImagePtr = aImagePtr | 0; + bImagePtr = bImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[outImagePtr + length | 0] = images[aImagePtr + length | 0] | 0 | (images[bImagePtr + length | 0] | 0) | 0; + } + } + + function countNonZero(imagePtr) { + imagePtr = imagePtr | 0; + + var sum = 0, + length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + sum = (sum | 0) + (images[imagePtr + length | 0] | 0) | 0; + } + + return sum | 0; + } + + function init(imagePtr, value) { + imagePtr = imagePtr | 0; + value = value | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[imagePtr + length | 0] = value; + } + } + + function dilate(inImagePtr, outImagePtr) { + inImagePtr = inImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var v = 0, + u = 0, + sum = 0, + yStart1 = 0, + yStart2 = 0, + xStart1 = 0, + xStart2 = 0, + offset = 0; + + for (v = 1; (v | 0) < (size - 1 | 0); v = v + 1 | 0) { + offset = offset + size | 0; + for (u = 1; (u | 0) < (size - 1 | 0); u = u + 1 | 0) { + yStart1 = offset - size | 0; + yStart2 = offset + size | 0; + xStart1 = u - 1 | 0; + xStart2 = u + 1 | 0; + sum = (images[inImagePtr + yStart1 + xStart1 | 0] | 0) + (images[inImagePtr + yStart1 + xStart2 | 0] | 0) + (images[inImagePtr + offset + u | 0] | 0) + (images[inImagePtr + yStart2 + xStart1 | 0] | 0) + (images[inImagePtr + yStart2 + xStart2 | 0] | 0) | 0; + if ((sum | 0) > (0 | 0)) { + images[outImagePtr + offset + u | 0] = 1; + } else { + images[outImagePtr + offset + u | 0] = 0; + } + } + } + return; + } + + function memcpy(srcImagePtr, dstImagePtr) { + srcImagePtr = srcImagePtr | 0; + dstImagePtr = dstImagePtr | 0; + + var length = 0; + + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = length - 1 | 0; + images[dstImagePtr + length | 0] = images[srcImagePtr + length | 0] | 0; + } + } + + function zeroBorder(imagePtr) { + imagePtr = imagePtr | 0; + + var x = 0, + y = 0; + + for (x = 0; (x | 0) < (size - 1 | 0); x = x + 1 | 0) { + images[imagePtr + x | 0] = 0; + images[imagePtr + y | 0] = 0; + y = y + size - 1 | 0; + images[imagePtr + y | 0] = 0; + y = y + 1 | 0; + } + for (x = 0; (x | 0) < (size | 0); x = x + 1 | 0) { + images[imagePtr + y | 0] = 0; + y = y + 1 | 0; + } + } + + function skeletonize() { + var subImagePtr = 0, + erodedImagePtr = 0, + tempImagePtr = 0, + skelImagePtr = 0, + sum = 0, + done = 0; + + erodedImagePtr = imul(size, size) | 0; + tempImagePtr = erodedImagePtr + erodedImagePtr | 0; + skelImagePtr = tempImagePtr + erodedImagePtr | 0; + + // init skel-image + init(skelImagePtr, 0); + zeroBorder(subImagePtr); + + do { + erode(subImagePtr, erodedImagePtr); + dilate(erodedImagePtr, tempImagePtr); + subtract(subImagePtr, tempImagePtr, tempImagePtr); + bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr); + memcpy(erodedImagePtr, subImagePtr); + sum = countNonZero(subImagePtr) | 0; + done = (sum | 0) == 0 | 0; + } while (!done); + } + + return { + skeletonize: skeletonize + }; + } + + exports["default"] = Skeletonizer; + + /* eslint-enable eqeqeq*/ + /* @preserve ASM END */ + module.exports = exports["default"]; + +/***/ }, +/* 13 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports["default"] = { + drawRect: function drawRect(pos, size, ctx, style) { + ctx.strokeStyle = style.color; + ctx.fillStyle = style.color; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.strokeRect(pos.x, pos.y, size.x, size.y); + }, + drawPath: function drawPath(path, def, ctx, style) { + ctx.strokeStyle = style.color; + ctx.fillStyle = style.color; + ctx.lineWidth = style.lineWidth; + ctx.beginPath(); + ctx.moveTo(path[0][def.x], path[0][def.y]); + for (var j = 1; j < path.length; j++) { + ctx.lineTo(path[j][def.x], path[j][def.y]); + } + ctx.closePath(); + ctx.stroke(); + }, + drawImage: function drawImage(imageData, size, ctx) { + var canvasData = ctx.getImageData(0, 0, size.x, size.y), + data = canvasData.data, + imageDataPos = imageData.length, + canvasDataPos = data.length, + value; + + if (canvasDataPos / imageDataPos !== 4) { + return false; + } + while (imageDataPos--) { + value = imageData[imageDataPos]; + data[--canvasDataPos] = 255; + data[--canvasDataPos] = value; + data[--canvasDataPos] = value; + data[--canvasDataPos] = value; + } + ctx.putImageData(canvasData, 0, 0); + return true; + } + }; + module.exports = exports["default"]; + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _bresenham = __webpack_require__(15); + + var _bresenham2 = _interopRequireDefault(_bresenham); + + var _image_debug = __webpack_require__(13); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + var _code_128_reader = __webpack_require__(16); + + var _code_128_reader2 = _interopRequireDefault(_code_128_reader); + + var _ean_reader = __webpack_require__(18); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + var _code_39_reader = __webpack_require__(19); + + var _code_39_reader2 = _interopRequireDefault(_code_39_reader); + + var _code_39_vin_reader = __webpack_require__(20); + + var _code_39_vin_reader2 = _interopRequireDefault(_code_39_vin_reader); + + var _codabar_reader = __webpack_require__(21); + + var _codabar_reader2 = _interopRequireDefault(_codabar_reader); + + var _upc_reader = __webpack_require__(22); + + var _upc_reader2 = _interopRequireDefault(_upc_reader); + + var _ean_8_reader = __webpack_require__(23); + + var _ean_8_reader2 = _interopRequireDefault(_ean_8_reader); + + var _upc_e_reader = __webpack_require__(24); + + var _upc_e_reader2 = _interopRequireDefault(_upc_e_reader); + + var _i2of5_reader = __webpack_require__(25); + + var _i2of5_reader2 = _interopRequireDefault(_i2of5_reader); + + var READERS = { + code_128_reader: _code_128_reader2['default'], + ean_reader: _ean_reader2['default'], + ean_8_reader: _ean_8_reader2['default'], + code_39_reader: _code_39_reader2['default'], + code_39_vin_reader: _code_39_vin_reader2['default'], + codabar_reader: _codabar_reader2['default'], + upc_reader: _upc_reader2['default'], + upc_e_reader: _upc_e_reader2['default'], + i2of5_reader: _i2of5_reader2['default'] + }; + exports['default'] = { + create: function create(config, inputImageWrapper) { + var _canvas = { + ctx: { + frequency: null, + pattern: null, + overlay: null + }, + dom: { + frequency: null, + pattern: null, + overlay: null + } + }, + _barcodeReaders = []; + + initCanvas(); + initReaders(); + initConfig(); + + function initCanvas() { + if (typeof document !== 'undefined') { + var $debug = document.querySelector("#debug.detection"); + _canvas.dom.frequency = document.querySelector("canvas.frequency"); + if (!_canvas.dom.frequency) { + _canvas.dom.frequency = document.createElement("canvas"); + _canvas.dom.frequency.className = "frequency"; + if ($debug) { + $debug.appendChild(_canvas.dom.frequency); + } + } + _canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d"); + + _canvas.dom.pattern = document.querySelector("canvas.patternBuffer"); + if (!_canvas.dom.pattern) { + _canvas.dom.pattern = document.createElement("canvas"); + _canvas.dom.pattern.className = "patternBuffer"; + if ($debug) { + $debug.appendChild(_canvas.dom.pattern); + } + } + _canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d"); + + _canvas.dom.overlay = document.querySelector("canvas.drawingBuffer"); + if (_canvas.dom.overlay) { + _canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d"); + } + } + } + + function initReaders() { + config.readers.forEach(function (readerConfig) { + var reader, + configuration = {}; + + if (typeof readerConfig === 'object') { + reader = readerConfig.format; + configuration = readerConfig.config; + } else if (typeof readerConfig === 'string') { + reader = readerConfig; + } + console.log("Before registering reader: ", reader); + _barcodeReaders.push(new READERS[reader](configuration)); + }); + console.log("Registered Readers: " + _barcodeReaders.map(function (reader) { + return JSON.stringify({ format: reader.FORMAT, config: reader.config }); + }).join(', ')); + } + + function initConfig() { + if (typeof document !== 'undefined') { + var i, + vis = [{ + node: _canvas.dom.frequency, + prop: config.showFrequency + }, { + node: _canvas.dom.pattern, + prop: config.showPattern + }]; + + for (i = 0; i < vis.length; i++) { + if (vis[i].prop === true) { + vis[i].node.style.display = "block"; + } else { + vis[i].node.style.display = "none"; + } + } + } + } + + /** + * extend the line on both ends + * @param {Array} line + * @param {Number} angle + */ + function getExtendedLine(line, angle, ext) { + function extendLine(amount) { + var extension = { + y: amount * Math.sin(angle), + x: amount * Math.cos(angle) + }; + + line[0].y -= extension.y; + line[0].x -= extension.x; + line[1].y += extension.y; + line[1].x += extension.x; + } + + // check if inside image + extendLine(ext); + while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0))) { + ext -= Math.ceil(ext / 2); + extendLine(-ext); + } + return line; + } + + function getLine(box) { + return [{ + x: (box[1][0] - box[0][0]) / 2 + box[0][0], + y: (box[1][1] - box[0][1]) / 2 + box[0][1] + }, { + x: (box[3][0] - box[2][0]) / 2 + box[2][0], + y: (box[3][1] - box[2][1]) / 2 + box[2][1] + }]; + } + + function tryDecode(line) { + var result = null, + i, + barcodeLine = _bresenham2['default'].getBarcodeLine(inputImageWrapper, line[0], line[1]); + + if (config.showFrequency) { + _image_debug2['default'].drawPath(line, { x: 'x', y: 'y' }, _canvas.ctx.overlay, { color: 'red', lineWidth: 3 }); + _bresenham2['default'].debug.printFrequency(barcodeLine.line, _canvas.dom.frequency); + } + _bresenham2['default'].toBinaryLine(barcodeLine); + if (config.showPattern) { + _bresenham2['default'].debug.printPattern(barcodeLine.line, _canvas.dom.pattern); + } + + for (i = 0; i < _barcodeReaders.length && result === null; i++) { + result = _barcodeReaders[i].decodePattern(barcodeLine.line); + } + if (result === null) { + return null; + } + return { + codeResult: result, + barcodeLine: barcodeLine + }; + } + + /** + * This method slices the given area apart and tries to detect a barcode-pattern + * for each slice. It returns the decoded barcode, or null if nothing was found + * @param {Array} box + * @param {Array} line + * @param {Number} lineAngle + */ + function tryDecodeBruteForce(box, line, lineAngle) { + var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow(box[1][1] - box[0][1], 2)), + i, + slices = 16, + result = null, + dir, + extension, + xdir = Math.sin(lineAngle), + ydir = Math.cos(lineAngle); + + for (i = 1; i < slices && result === null; i++) { + // move line perpendicular to angle + dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1); + extension = { + y: dir * xdir, + x: dir * ydir + }; + line[0].y += extension.x; + line[0].x -= extension.y; + line[1].y += extension.x; + line[1].x -= extension.y; + + result = tryDecode(line); + } + return result; + } + + function getLineLength(line) { + return Math.sqrt(Math.pow(Math.abs(line[1].y - line[0].y), 2) + Math.pow(Math.abs(line[1].x - line[0].x), 2)); + } + + /** + * With the help of the configured readers (Code128 or EAN) this function tries to detect a + * valid barcode pattern within the given area. + * @param {Object} box The area to search in + * @returns {Object} the result {codeResult, line, angle, pattern, threshold} + */ + function _decodeFromBoundingBox(box) { + var line, + lineAngle, + ctx = _canvas.ctx.overlay, + result, + lineLength; + + if (config.drawBoundingBox && ctx) { + _image_debug2['default'].drawPath(box, { x: 0, y: 1 }, ctx, { color: "blue", lineWidth: 2 }); + } + + line = getLine(box); + lineLength = getLineLength(line); + lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); + line = getExtendedLine(line, lineAngle, Math.floor(lineLength * 0.1)); + if (line === null) { + return null; + } + + result = tryDecode(line); + if (result === null) { + result = tryDecodeBruteForce(box, line, lineAngle); + } + + if (result === null) { + return null; + } + + if (result && config.drawScanline && ctx) { + _image_debug2['default'].drawPath(line, { x: 'x', y: 'y' }, ctx, { color: 'red', lineWidth: 3 }); + } + + return { + codeResult: result.codeResult, + line: line, + angle: lineAngle, + pattern: result.barcodeLine.line, + threshold: result.barcodeLine.threshold + }; + } + + return { + decodeFromBoundingBox: function decodeFromBoundingBox(box) { + return _decodeFromBoundingBox(box); + }, + decodeFromBoundingBoxes: function decodeFromBoundingBoxes(boxes) { + var i, result; + for (i = 0; i < boxes.length; i++) { + result = _decodeFromBoundingBox(boxes[i]); + if (result && result.codeResult) { + result.box = boxes[i]; + return result; + } + } + }, + setReaders: function setReaders(readers) { + config.readers = readers; + _barcodeReaders.length = 0; + initReaders(); + } + }; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _cv_utils = __webpack_require__(5); + + var _cv_utils2 = _interopRequireDefault(_cv_utils); + + var _image_wrapper = __webpack_require__(3); + + var _image_wrapper2 = _interopRequireDefault(_image_wrapper); + + var Bresenham = {}; + + var Slope = { + DIR: { + UP: 1, + DOWN: -1 + } + }; + /** + * Scans a line of the given image from point p1 to p2 and returns a result object containing + * gray-scale values (0-255) of the underlying pixels in addition to the min + * and max values. + * @param {Object} imageWrapper + * @param {Object} p1 The start point {x,y} + * @param {Object} p2 The end point {x,y} + * @returns {line, min, max} + */ + Bresenham.getBarcodeLine = function (imageWrapper, p1, p2) { + var x0 = p1.x | 0, + y0 = p1.y | 0, + x1 = p2.x | 0, + y1 = p2.y | 0, + steep = Math.abs(y1 - y0) > Math.abs(x1 - x0), + deltax, + deltay, + error, + ystep, + y, + tmp, + x, + line = [], + imageData = imageWrapper.data, + width = imageWrapper.size.x, + sum = 0, + val, + min = 255, + max = 0; + + function read(a, b) { + val = imageData[b * width + a]; + sum += val; + min = val < min ? val : min; + max = val > max ? val : max; + line.push(val); + } + + if (steep) { + tmp = x0; + x0 = y0; + y0 = tmp; + + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + + tmp = y0; + y0 = y1; + y1 = tmp; + } + deltax = x1 - x0; + deltay = Math.abs(y1 - y0); + error = deltax / 2 | 0; + y = y0; + ystep = y0 < y1 ? 1 : -1; + for (x = x0; x < x1; x++) { + if (steep) { + read(y, x); + } else { + read(x, y); + } + error = error - deltay; + if (error < 0) { + y = y + ystep; + error = error + deltax; + } + } + + return { + line: line, + min: min, + max: max + }; + }; + + Bresenham.toOtsuBinaryLine = function (result) { + var line = result.line, + image = new _image_wrapper2['default']({ x: line.length - 1, y: 1 }, line), + threshold = _cv_utils2['default'].determineOtsuThreshold(image, 5); + + line = _cv_utils2['default'].sharpenLine(line); + _cv_utils2['default'].thresholdImage(image, threshold); + + return { + line: line, + threshold: threshold + }; + }; + + /** + * Converts the result from getBarcodeLine into a binary representation + * also considering the frequency and slope of the signal for more robust results + * @param {Object} result {line, min, max} + */ + Bresenham.toBinaryLine = function (result) { + var min = result.min, + max = result.max, + line = result.line, + slope, + slope2, + center = min + (max - min) / 2, + extrema = [], + currentDir, + dir, + threshold = (max - min) / 12, + rThreshold = -threshold, + i, + j; + + // 1. find extrema + currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN; + extrema.push({ + pos: 0, + val: line[0] + }); + for (i = 0; i < line.length - 2; i++) { + slope = line[i + 1] - line[i]; + slope2 = line[i + 2] - line[i + 1]; + if (slope + slope2 < rThreshold && line[i + 1] < center * 1.5) { + dir = Slope.DIR.DOWN; + } else if (slope + slope2 > threshold && line[i + 1] > center * 0.5) { + dir = Slope.DIR.UP; + } else { + dir = currentDir; + } + + if (currentDir !== dir) { + extrema.push({ + pos: i, + val: line[i] + }); + currentDir = dir; + } + } + extrema.push({ + pos: line.length, + val: line[line.length - 1] + }); + + for (j = extrema[0].pos; j < extrema[1].pos; j++) { + line[j] = line[j] > center ? 0 : 1; + } + + // iterate over extrema and convert to binary based on avg between minmax + for (i = 1; i < extrema.length - 1; i++) { + if (extrema[i + 1].val > extrema[i].val) { + threshold = extrema[i].val + (extrema[i + 1].val - extrema[i].val) / 3 * 2 | 0; + } else { + threshold = extrema[i + 1].val + (extrema[i].val - extrema[i + 1].val) / 3 | 0; + } + + for (j = extrema[i].pos; j < extrema[i + 1].pos; j++) { + line[j] = line[j] > threshold ? 0 : 1; + } + } + + return { + line: line, + threshold: threshold + }; + }; + + /** + * Used for development only + */ + Bresenham.debug = { + printFrequency: function printFrequency(line, canvas) { + var i, + ctx = canvas.getContext("2d"); + canvas.width = line.length; + canvas.height = 256; + + ctx.beginPath(); + ctx.strokeStyle = "blue"; + for (i = 0; i < line.length; i++) { + ctx.moveTo(i, 255); + ctx.lineTo(i, 255 - line[i]); + } + ctx.stroke(); + ctx.closePath(); + }, + + printPattern: function printPattern(line, canvas) { + var ctx = canvas.getContext("2d"), + i; + + canvas.width = line.length; + ctx.fillColor = "black"; + for (i = 0; i < line.length; i++) { + if (line[i] === 1) { + ctx.fillRect(i, 0, 1, 100); + } + } + } + }; + + exports['default'] = Bresenham; + module.exports = exports['default']; + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _barcode_reader = __webpack_require__(17); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + function Code128Reader() { + _barcode_reader2["default"].call(this); + } + + var properties = { + CODE_SHIFT: { value: 98 }, + CODE_C: { value: 99 }, + CODE_B: { value: 100 }, + CODE_A: { value: 101 }, + START_CODE_A: { value: 103 }, + START_CODE_B: { value: 104 }, + START_CODE_C: { value: 105 }, + STOP_CODE: { value: 106 }, + MODULO: { value: 11 }, + CODE_PATTERN: { value: [[2, 1, 2, 2, 2, 2], [2, 2, 2, 1, 2, 2], [2, 2, 2, 2, 2, 1], [1, 2, 1, 2, 2, 3], [1, 2, 1, 3, 2, 2], [1, 3, 1, 2, 2, 2], [1, 2, 2, 2, 1, 3], [1, 2, 2, 3, 1, 2], [1, 3, 2, 2, 1, 2], [2, 2, 1, 2, 1, 3], [2, 2, 1, 3, 1, 2], [2, 3, 1, 2, 1, 2], [1, 1, 2, 2, 3, 2], [1, 2, 2, 1, 3, 2], [1, 2, 2, 2, 3, 1], [1, 1, 3, 2, 2, 2], [1, 2, 3, 1, 2, 2], [1, 2, 3, 2, 2, 1], [2, 2, 3, 2, 1, 1], [2, 2, 1, 1, 3, 2], [2, 2, 1, 2, 3, 1], [2, 1, 3, 2, 1, 2], [2, 2, 3, 1, 1, 2], [3, 1, 2, 1, 3, 1], [3, 1, 1, 2, 2, 2], [3, 2, 1, 1, 2, 2], [3, 2, 1, 2, 2, 1], [3, 1, 2, 2, 1, 2], [3, 2, 2, 1, 1, 2], [3, 2, 2, 2, 1, 1], [2, 1, 2, 1, 2, 3], [2, 1, 2, 3, 2, 1], [2, 3, 2, 1, 2, 1], [1, 1, 1, 3, 2, 3], [1, 3, 1, 1, 2, 3], [1, 3, 1, 3, 2, 1], [1, 1, 2, 3, 1, 3], [1, 3, 2, 1, 1, 3], [1, 3, 2, 3, 1, 1], [2, 1, 1, 3, 1, 3], [2, 3, 1, 1, 1, 3], [2, 3, 1, 3, 1, 1], [1, 1, 2, 1, 3, 3], [1, 1, 2, 3, 3, 1], [1, 3, 2, 1, 3, 1], [1, 1, 3, 1, 2, 3], [1, 1, 3, 3, 2, 1], [1, 3, 3, 1, 2, 1], [3, 1, 3, 1, 2, 1], [2, 1, 1, 3, 3, 1], [2, 3, 1, 1, 3, 1], [2, 1, 3, 1, 1, 3], [2, 1, 3, 3, 1, 1], [2, 1, 3, 1, 3, 1], [3, 1, 1, 1, 2, 3], [3, 1, 1, 3, 2, 1], [3, 3, 1, 1, 2, 1], [3, 1, 2, 1, 1, 3], [3, 1, 2, 3, 1, 1], [3, 3, 2, 1, 1, 1], [3, 1, 4, 1, 1, 1], [2, 2, 1, 4, 1, 1], [4, 3, 1, 1, 1, 1], [1, 1, 1, 2, 2, 4], [1, 1, 1, 4, 2, 2], [1, 2, 1, 1, 2, 4], [1, 2, 1, 4, 2, 1], [1, 4, 1, 1, 2, 2], [1, 4, 1, 2, 2, 1], [1, 1, 2, 2, 1, 4], [1, 1, 2, 4, 1, 2], [1, 2, 2, 1, 1, 4], [1, 2, 2, 4, 1, 1], [1, 4, 2, 1, 1, 2], [1, 4, 2, 2, 1, 1], [2, 4, 1, 2, 1, 1], [2, 2, 1, 1, 1, 4], [4, 1, 3, 1, 1, 1], [2, 4, 1, 1, 1, 2], [1, 3, 4, 1, 1, 1], [1, 1, 1, 2, 4, 2], [1, 2, 1, 1, 4, 2], [1, 2, 1, 2, 4, 1], [1, 1, 4, 2, 1, 2], [1, 2, 4, 1, 1, 2], [1, 2, 4, 2, 1, 1], [4, 1, 1, 2, 1, 2], [4, 2, 1, 1, 1, 2], [4, 2, 1, 2, 1, 1], [2, 1, 2, 1, 4, 1], [2, 1, 4, 1, 2, 1], [4, 1, 2, 1, 2, 1], [1, 1, 1, 1, 4, 3], [1, 1, 1, 3, 4, 1], [1, 3, 1, 1, 4, 1], [1, 1, 4, 1, 1, 3], [1, 1, 4, 3, 1, 1], [4, 1, 1, 1, 1, 3], [4, 1, 1, 3, 1, 1], [1, 1, 3, 1, 4, 1], [1, 1, 4, 1, 3, 1], [3, 1, 1, 1, 4, 1], [4, 1, 1, 1, 3, 1], [2, 1, 1, 4, 1, 2], [2, 1, 1, 2, 1, 4], [2, 1, 1, 2, 3, 2], [2, 3, 3, 1, 1, 1, 2]] }, + SINGLE_CODE_ERROR: { value: 1 }, + AVG_CODE_ERROR: { value: 0.5 }, + FORMAT: { value: "code_128", writeable: false } + }; + + Code128Reader.prototype = Object.create(_barcode_reader2["default"].prototype, properties); + Code128Reader.prototype.constructor = Code128Reader; + + Code128Reader.prototype._decodeCode = function (start) { + var counter = [0, 0, 0, 0, 0, 0], + i, + self = this, + offset = start, + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: start, + end: start + }, + code, + error, + normalized; + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + bestMatch.end = i; + return bestMatch; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + Code128Reader.prototype._findStart = function () { + var counter = [0, 0, 0, 0, 0, 0], + i, + self = this, + offset = self._nextSet(self._row), + isWhite = false, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + code, + error, + j, + sum, + normalized; + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + if (bestMatch.error < self.AVG_CODE_ERROR) { + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + } + + for (j = 0; j < 4; j++) { + counter[j] = counter[j + 2]; + } + counter[4] = 0; + counter[5] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + Code128Reader.prototype._decode = function () { + var self = this, + startInfo = self._findStart(), + code = null, + done = false, + result = [], + multiplier = 0, + checksum = 0, + codeset, + rawResult = [], + decodedCodes = [], + shiftNext = false, + unshift; + + if (startInfo === null) { + return null; + } + code = { + code: startInfo.code, + start: startInfo.start, + end: startInfo.end + }; + decodedCodes.push(code); + checksum = code.code; + switch (code.code) { + case self.START_CODE_A: + codeset = self.CODE_A; + break; + case self.START_CODE_B: + codeset = self.CODE_B; + break; + case self.START_CODE_C: + codeset = self.CODE_C; + break; + default: + return null; + } + + while (!done) { + unshift = shiftNext; + shiftNext = false; + code = self._decodeCode(code.end); + if (code !== null) { + if (code.code !== self.STOP_CODE) { + rawResult.push(code.code); + multiplier++; + checksum += multiplier * code.code; + } + decodedCodes.push(code); + + switch (codeset) { + case self.CODE_A: + if (code.code < 64) { + result.push(String.fromCharCode(32 + code.code)); + } else if (code.code < 96) { + result.push(String.fromCharCode(code.code - 64)); + } else { + switch (code.code) { + case self.CODE_SHIFT: + shiftNext = true; + codeset = self.CODE_B; + break; + case self.CODE_B: + codeset = self.CODE_B; + break; + case self.CODE_C: + codeset = self.CODE_C; + break; + case self.STOP_CODE: + done = true; + break; + } + } + break; + case self.CODE_B: + if (code.code < 96) { + result.push(String.fromCharCode(32 + code.code)); + } else { + switch (code.code) { + case self.CODE_SHIFT: + shiftNext = true; + codeset = self.CODE_A; + break; + case self.CODE_A: + codeset = self.CODE_A; + break; + case self.CODE_C: + codeset = self.CODE_C; + break; + case self.STOP_CODE: + done = true; + break; + } + } + break; + case self.CODE_C: + if (code.code < 100) { + result.push(code.code < 10 ? "0" + code.code : code.code); + } + switch (code.code) { + case self.CODE_A: + codeset = self.CODE_A; + break; + case self.CODE_B: + codeset = self.CODE_B; + break; + case self.STOP_CODE: + done = true; + break; + } + break; + } + } else { + done = true; + } + if (unshift) { + codeset = codeset === self.CODE_A ? self.CODE_B : self.CODE_A; + } + } + + if (code === null) { + return null; + } + + // find end bar + code.end = self._nextUnset(self._row, code.end); + if (!self._verifyTrailingWhitespace(code)) { + return null; + } + + // checksum + // Does not work correctly yet!!! startcode - endcode? + checksum -= multiplier * rawResult[rawResult.length - 1]; + if (checksum % 103 !== rawResult[rawResult.length - 1]) { + return null; + } + + if (!result.length) { + return null; + } + + // remove last code from result (checksum) + result.splice(result.length - 1, 1); + + return { + code: result.join(""), + start: startInfo.start, + end: code.end, + codeset: codeset, + startInfo: startInfo, + decodedCodes: decodedCodes, + endInfo: code + }; + }; + + _barcode_reader2["default"].prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start) / 2; + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; + }; + + exports["default"] = Code128Reader; + module.exports = exports["default"]; + +/***/ }, +/* 17 */ +/***/ function(module, exports) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + function BarcodeReader(config) { + this._row = []; + this.config = config || {}; + return this; + } + + BarcodeReader.prototype._nextUnset = function (line, start) { + var i; + + if (start === undefined) { + start = 0; + } + for (i = start; i < line.length; i++) { + if (!line[i]) { + return i; + } + } + return line.length; + }; + + BarcodeReader.prototype._matchPattern = function (counter, code) { + var i, + error = 0, + singleError = 0, + modulo = this.MODULO, + maxSingleError = this.SINGLE_CODE_ERROR || 1; + + for (i = 0; i < counter.length; i++) { + singleError = Math.abs(code[i] - counter[i]); + if (singleError > maxSingleError) { + return Number.MAX_VALUE; + } + error += singleError; + } + return error / modulo; + }; + + BarcodeReader.prototype._nextSet = function (line, offset) { + var i; + + offset = offset || 0; + for (i = offset; i < line.length; i++) { + if (line[i]) { + return i; + } + } + return line.length; + }; + + BarcodeReader.prototype._normalize = function (counter, modulo) { + var i, + self = this, + sum = 0, + ratio, + numOnes = 0, + normalized = [], + norm = 0; + + if (!modulo) { + modulo = self.MODULO; + } + for (i = 0; i < counter.length; i++) { + if (counter[i] === 1) { + numOnes++; + } else { + sum += counter[i]; + } + } + ratio = sum / (modulo - numOnes); + if (ratio > 1.0) { + for (i = 0; i < counter.length; i++) { + norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; + normalized.push(norm); + } + } else { + ratio = (sum + numOnes) / modulo; + for (i = 0; i < counter.length; i++) { + norm = counter[i] / ratio; + normalized.push(norm); + } + } + return normalized; + }; + + BarcodeReader.prototype._matchTrace = function (cmpCounter, epsilon) { + var counter = [], + i, + self = this, + offset = self._nextSet(self._row), + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0 + }, + error; + + if (cmpCounter) { + for (i = 0; i < cmpCounter.length; i++) { + counter.push(0); + } + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + error = self._matchPattern(counter, cmpCounter); + + if (error < epsilon) { + bestMatch.start = i - offset; + bestMatch.end = i; + bestMatch.counter = counter; + return bestMatch; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } else { + counter.push(0); + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + counter.push(0); + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + // if cmpCounter was not given + bestMatch.start = offset; + bestMatch.end = self._row.length - 1; + bestMatch.counter = counter; + return bestMatch; + }; + + BarcodeReader.prototype.decodePattern = function (pattern) { + var self = this, + result; + + self._row = pattern; + result = self._decode(); + if (result === null) { + self._row.reverse(); + result = self._decode(); + if (result) { + result.direction = BarcodeReader.DIRECTION.REVERSE; + result.start = self._row.length - result.start; + result.end = self._row.length - result.end; + } + } else { + result.direction = BarcodeReader.DIRECTION.FORWARD; + } + if (result) { + result.format = self.FORMAT; + } + return result; + }; + + BarcodeReader.prototype._matchRange = function (start, end, value) { + var i; + + start = start < 0 ? 0 : start; + for (i = start; i < end; i++) { + if (this._row[i] !== value) { + return false; + } + } + return true; + }; + + BarcodeReader.prototype._fillCounters = function (offset, end, isWhite) { + var self = this, + counterPos = 0, + i, + counters = []; + + isWhite = typeof isWhite !== 'undefined' ? isWhite : true; + offset = typeof offset !== 'undefined' ? offset : self._nextUnset(self._row); + end = end || self._row.length; + + counters[counterPos] = 0; + for (i = offset; i < end; i++) { + if (self._row[i] ^ isWhite) { + counters[counterPos]++; + } else { + counterPos++; + counters[counterPos] = 1; + isWhite = !isWhite; + } + } + return counters; + }; + + Object.defineProperty(BarcodeReader.prototype, "FORMAT", { + value: 'unknown', + writeable: false + }); + + BarcodeReader.DIRECTION = { + FORWARD: 1, + REVERSE: -1 + }; + + BarcodeReader.Exception = { + StartNotFoundException: "Start-Info was not found!", + CodeNotFoundException: "Code could not be found!", + PatternNotFoundException: "Pattern could not be found!" + }; + + BarcodeReader.CONFIG_KEYS = {}; + + exports['default'] = BarcodeReader; + module.exports = exports['default']; + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _barcode_reader = __webpack_require__(17); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + function EANReader(opts) { + _barcode_reader2["default"].call(this, opts); + } + + var properties = { + CODE_L_START: { value: 0 }, + MODULO: { value: 7 }, + CODE_G_START: { value: 10 }, + START_PATTERN: { value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7] }, + STOP_PATTERN: { value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7] }, + MIDDLE_PATTERN: { value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7] }, + CODE_PATTERN: { value: [[3, 2, 1, 1], [2, 2, 2, 1], [2, 1, 2, 2], [1, 4, 1, 1], [1, 1, 3, 2], [1, 2, 3, 1], [1, 1, 1, 4], [1, 3, 1, 2], [1, 2, 1, 3], [3, 1, 1, 2], [1, 1, 2, 3], [1, 2, 2, 2], [2, 2, 1, 2], [1, 1, 4, 1], [2, 3, 1, 1], [1, 3, 2, 1], [4, 1, 1, 1], [2, 1, 3, 1], [3, 1, 2, 1], [2, 1, 1, 3]] }, + CODE_FREQUENCY: { value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26] }, + SINGLE_CODE_ERROR: { value: 0.67 }, + AVG_CODE_ERROR: { value: 0.27 }, + FORMAT: { value: "ean_13", writeable: false } + }; + + EANReader.prototype = Object.create(_barcode_reader2["default"].prototype, properties); + EANReader.prototype.constructor = EANReader; + + EANReader.prototype._decodeCode = function (start, coderange) { + var counter = [0, 0, 0, 0], + i, + self = this, + offset = start, + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: start, + end: start + }, + code, + error, + normalized; + + if (!coderange) { + coderange = self.CODE_PATTERN.length; + } + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < coderange; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + bestMatch.end = i; + if (bestMatch.error > self.AVG_CODE_ERROR) { + return null; + } + return bestMatch; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + EANReader.prototype._findPattern = function (pattern, offset, isWhite, tryHarder, epsilon) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized; + + if (!offset) { + offset = self._nextSet(self._row); + } + + if (isWhite === undefined) { + isWhite = false; + } + + if (tryHarder === undefined) { + tryHarder = true; + } + + if (epsilon === undefined) { + epsilon = self.AVG_CODE_ERROR; + } + + for (i = 0; i < pattern.length; i++) { + counter[i] = 0; + } + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + error = self._matchPattern(normalized, pattern); + + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + } + if (tryHarder) { + for (j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; + } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + EANReader.prototype._findStart = function () { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset); + if (!startInfo) { + return null; + } + leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start); + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; + } + } + offset = startInfo.end; + startInfo = null; + } + }; + + EANReader.prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; + }; + + EANReader.prototype._findEnd = function (offset, isWhite) { + var self = this, + endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false); + + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; + }; + + EANReader.prototype._calculateFirstDigit = function (codeFrequency) { + var i, + self = this; + + for (i = 0; i < self.CODE_FREQUENCY.length; i++) { + if (codeFrequency === self.CODE_FREQUENCY[i]) { + return i; + } + } + return null; + }; + + EANReader.prototype._decodePayload = function (code, result, decodedCodes) { + var i, + self = this, + codeFrequency = 0x0, + firstDigit; + + for (i = 0; i < 6; i++) { + code = self._decodeCode(code.end); + if (!code) { + return null; + } + if (code.code >= self.CODE_G_START) { + code.code = code.code - self.CODE_G_START; + codeFrequency |= 1 << 5 - i; + } else { + codeFrequency |= 0 << 5 - i; + } + result.push(code.code); + decodedCodes.push(code); + } + + firstDigit = self._calculateFirstDigit(codeFrequency); + if (firstDigit === null) { + return null; + } + result.unshift(firstDigit); + + code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); + if (code === null) { + return null; + } + decodedCodes.push(code); + + for (i = 0; i < 6; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + decodedCodes.push(code); + result.push(code.code); + } + + return code; + }; + + EANReader.prototype._decode = function () { + var startInfo, + self = this, + code, + result = [], + decodedCodes = []; + + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + code = { + code: startInfo.code, + start: startInfo.start, + end: startInfo.end + }; + decodedCodes.push(code); + code = self._decodePayload(code, result, decodedCodes); + if (!code) { + return null; + } + code = self._findEnd(code.end, false); + if (!code) { + return null; + } + + decodedCodes.push(code); + + // Checksum + if (!self._checksum(result)) { + return null; + } + + return { + code: result.join(""), + start: startInfo.start, + end: code.end, + codeset: "", + startInfo: startInfo, + decodedCodes: decodedCodes + }; + }; + + EANReader.prototype._checksum = function (result) { + var sum = 0, + i; + + for (i = result.length - 2; i >= 0; i -= 2) { + sum += result[i]; + } + sum *= 3; + for (i = result.length - 1; i >= 0; i -= 2) { + sum += result[i]; + } + return sum % 10 === 0; + }; + + exports["default"] = EANReader; + module.exports = exports["default"]; + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _barcode_reader = __webpack_require__(17); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + var _array_helper = __webpack_require__(8); + + var _array_helper2 = _interopRequireDefault(_array_helper); + + function Code39Reader() { + _barcode_reader2['default'].call(this); + } + + var properties = { + ALPHABETH_STRING: { value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%" }, + ALPHABET: { value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37] }, + CHARACTER_ENCODINGS: { value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A] }, + ASTERISK: { value: 0x094 }, + FORMAT: { value: "code_39", writeable: false } + }; + + Code39Reader.prototype = Object.create(_barcode_reader2['default'].prototype, properties); + Code39Reader.prototype.constructor = Code39Reader; + + Code39Reader.prototype._toCounters = function (start, counter) { + var self = this, + numCounters = counter.length, + end = self._row.length, + isWhite = !self._row[start], + i, + counterPos = 0; + + _array_helper2['default'].init(counter, 0); + + for (i = start; i < end; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + if (counterPos === numCounters) { + break; + } else { + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + return counter; + }; + + Code39Reader.prototype._decode = function () { + var self = this, + counters = [0, 0, 0, 0, 0, 0, 0, 0, 0], + result = [], + start = self._findStart(), + decodedChar, + lastStart, + pattern, + nextStart; + + if (!start) { + return null; + } + nextStart = self._nextSet(self._row, start.end); + + do { + counters = self._toCounters(nextStart, counters); + pattern = self._toPattern(counters); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + if (decodedChar < 0) { + return null; + } + result.push(decodedChar); + lastStart = nextStart; + nextStart += _array_helper2['default'].sum(counters); + nextStart = self._nextSet(self._row, nextStart); + } while (decodedChar !== '*'); + result.pop(); + + if (!result.length) { + return null; + } + + if (!self._verifyTrailingWhitespace(lastStart, nextStart, counters)) { + return null; + } + + return { + code: result.join(""), + start: start.start, + end: nextStart, + startInfo: start, + decodedCodes: result + }; + }; + + Code39Reader.prototype._verifyTrailingWhitespace = function (lastStart, nextStart, counters) { + var trailingWhitespaceEnd, + patternSize = _array_helper2['default'].sum(counters); + + trailingWhitespaceEnd = nextStart - lastStart - patternSize; + if (trailingWhitespaceEnd * 3 >= patternSize) { + return true; + } + return false; + }; + + Code39Reader.prototype._patternToChar = function (pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + }; + + Code39Reader.prototype._findNextWidth = function (counters, current) { + var i, + minWidth = Number.MAX_VALUE; + + for (i = 0; i < counters.length; i++) { + if (counters[i] < minWidth && counters[i] > current) { + minWidth = counters[i]; + } + } + + return minWidth; + }; + + Code39Reader.prototype._toPattern = function (counters) { + var numCounters = counters.length, + maxNarrowWidth = 0, + numWideBars = numCounters, + wideBarWidth = 0, + self = this, + pattern, + i; + + while (numWideBars > 3) { + maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); + numWideBars = 0; + pattern = 0; + for (i = 0; i < numCounters; i++) { + if (counters[i] > maxNarrowWidth) { + pattern |= 1 << numCounters - 1 - i; + numWideBars++; + wideBarWidth += counters[i]; + } + } + + if (numWideBars === 3) { + for (i = 0; i < numCounters && numWideBars > 0; i++) { + if (counters[i] > maxNarrowWidth) { + numWideBars--; + if (counters[i] * 2 >= wideBarWidth) { + return -1; + } + } + } + return pattern; + } + } + return -1; + }; + + Code39Reader.prototype._findStart = function () { + var self = this, + offset = self._nextSet(self._row), + patternStart = offset, + counter = [0, 0, 0, 0, 0, 0, 0, 0, 0], + counterPos = 0, + isWhite = false, + i, + j, + whiteSpaceMustStart; + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + // find start pattern + if (self._toPattern(counter) === self.ASTERISK) { + whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - (i - patternStart) / 4)); + if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { + return { + start: patternStart, + end: i + }; + } + } + + patternStart += counter[0] + counter[1]; + for (j = 0; j < 7; j++) { + counter[j] = counter[j + 2]; + } + counter[7] = 0; + counter[8] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + exports['default'] = Code39Reader; + module.exports = exports['default']; + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _code_39_reader = __webpack_require__(19); + + var _code_39_reader2 = _interopRequireDefault(_code_39_reader); + + function Code39VINReader() { + _code_39_reader2['default'].call(this); + } + + var patterns = { + IOQ: /[IOQ]/g, + AZ09: /[A-Z0-9]{17}/ + }; + + Code39VINReader.prototype = Object.create(_code_39_reader2['default'].prototype); + Code39VINReader.prototype.constructor = Code39VINReader; + + // Cribbed from: + // https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java + Code39VINReader.prototype._decode = function () { + var result = _code_39_reader2['default'].prototype._decode.apply(this); + if (!result) { + return null; + } + + var code = result.code; + + if (!code) { + return null; + } + + code = code.replace(patterns.IOQ, ''); + + if (!code.match(patterns.AZ09)) { + console.log('Failed AZ09 pattern code:', code); + return null; + } + + if (!this._checkChecksum(code)) { + return null; + } + + result.code = code; + return result; + }; + + Code39VINReader.prototype._checkChecksum = function (code) { + // TODO + return !!code; + }; + + exports['default'] = Code39VINReader; + module.exports = exports['default']; + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _barcode_reader = __webpack_require__(17); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + function CodabarReader() { + _barcode_reader2["default"].call(this); + this._counters = []; + } + + var properties = { + ALPHABETH_STRING: { value: "0123456789-$:/.+ABCD" }, + ALPHABET: { value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68] }, + CHARACTER_ENCODINGS: { value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E] }, + START_END: { value: [0x01A, 0x029, 0x00B, 0x00E] }, + MIN_ENCODED_CHARS: { value: 4 }, + MAX_ACCEPTABLE: { value: 2.0 }, + PADDING: { value: 1.5 }, + FORMAT: { value: "codabar", writeable: false } + }; + + CodabarReader.prototype = Object.create(_barcode_reader2["default"].prototype, properties); + CodabarReader.prototype.constructor = CodabarReader; + + CodabarReader.prototype._decode = function () { + var self = this, + result = [], + start, + decodedChar, + pattern, + nextStart, + end; + + this._counters = self._fillCounters(); + start = self._findStart(); + if (!start) { + return null; + } + nextStart = start.startCounter; + + do { + pattern = self._toPattern(nextStart); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + if (decodedChar < 0) { + return null; + } + result.push(decodedChar); + nextStart += 8; + if (result.length > 1 && self._isStartEnd(pattern)) { + break; + } + } while (nextStart < self._counters.length); + + // verify end + if (result.length - 2 < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) { + return null; + } + + // verify end white space + if (!self._verifyWhitespace(start.startCounter, nextStart - 8)) { + return null; + } + + if (!self._validateResult(result, start.startCounter)) { + return null; + } + + nextStart = nextStart > self._counters.length ? self._counters.length : nextStart; + end = start.start + self._sumCounters(start.startCounter, nextStart - 8); + + return { + code: result.join(""), + start: start.start, + end: end, + startInfo: start, + decodedCodes: result + }; + }; + + CodabarReader.prototype._verifyWhitespace = function (startCounter, endCounter) { + if (startCounter - 1 <= 0 || this._counters[startCounter - 1] >= this._calculatePatternLength(startCounter) / 2.0) { + if (endCounter + 8 >= this._counters.length || this._counters[endCounter + 7] >= this._calculatePatternLength(endCounter) / 2.0) { + return true; + } + } + return false; + }; + + CodabarReader.prototype._calculatePatternLength = function (offset) { + var i, + sum = 0; + + for (i = offset; i < offset + 7; i++) { + sum += this._counters[i]; + } + + return sum; + }; + + CodabarReader.prototype._thresholdResultPattern = function (result, startCounter) { + var self = this, + categorization = { + space: { + narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE }, + wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE } + }, + bar: { + narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE }, + wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE } + } + }, + kind, + cat, + i, + j, + pos = startCounter, + pattern; + + for (i = 0; i < result.length; i++) { + pattern = self._charToPattern(result[i]); + for (j = 6; j >= 0; j--) { + kind = (j & 1) === 2 ? categorization.bar : categorization.space; + cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; + cat.size += self._counters[pos + j]; + cat.counts++; + pattern >>= 1; + } + pos += 8; + } + + ["space", "bar"].forEach(function (key) { + var newkind = categorization[key]; + newkind.wide.min = Math.floor((newkind.narrow.size / newkind.narrow.counts + newkind.wide.size / newkind.wide.counts) / 2); + newkind.narrow.max = Math.ceil(newkind.wide.min); + newkind.wide.max = Math.ceil((newkind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / newkind.wide.counts); + }); + + return categorization; + }; + + CodabarReader.prototype._charToPattern = function (char) { + var self = this, + charCode = char.charCodeAt(0), + i; + + for (i = 0; i < self.ALPHABET.length; i++) { + if (self.ALPHABET[i] === charCode) { + return self.CHARACTER_ENCODINGS[i]; + } + } + return 0x0; + }; + + CodabarReader.prototype._validateResult = function (result, startCounter) { + var self = this, + thresholds = self._thresholdResultPattern(result, startCounter), + i, + j, + kind, + cat, + size, + pos = startCounter, + pattern; + + for (i = 0; i < result.length; i++) { + pattern = self._charToPattern(result[i]); + for (j = 6; j >= 0; j--) { + kind = (j & 1) === 0 ? thresholds.bar : thresholds.space; + cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; + size = self._counters[pos + j]; + if (size < cat.min || size > cat.max) { + return false; + } + pattern >>= 1; + } + pos += 8; + } + return true; + }; + + CodabarReader.prototype._patternToChar = function (pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + return -1; + }; + + CodabarReader.prototype._computeAlternatingThreshold = function (offset, end) { + var i, + min = Number.MAX_VALUE, + max = 0, + counter; + + for (i = offset; i < end; i += 2) { + counter = this._counters[i]; + if (counter > max) { + max = counter; + } + if (counter < min) { + min = counter; + } + } + + return (min + max) / 2.0 | 0; + }; + + CodabarReader.prototype._toPattern = function (offset) { + var numCounters = 7, + end = offset + numCounters, + barThreshold, + spaceThreshold, + bitmask = 1 << numCounters - 1, + pattern = 0, + i, + threshold; + + if (end > this._counters.length) { + return -1; + } + + barThreshold = this._computeAlternatingThreshold(offset, end); + spaceThreshold = this._computeAlternatingThreshold(offset + 1, end); + + for (i = 0; i < numCounters; i++) { + threshold = (i & 1) === 0 ? barThreshold : spaceThreshold; + if (this._counters[offset + i] > threshold) { + pattern |= bitmask; + } + bitmask >>= 1; + } + + return pattern; + }; + + CodabarReader.prototype._isStartEnd = function (pattern) { + var i; + + for (i = 0; i < this.START_END.length; i++) { + if (this.START_END[i] === pattern) { + return true; + } + } + return false; + }; + + CodabarReader.prototype._sumCounters = function (start, end) { + var i, + sum = 0; + + for (i = start; i < end; i++) { + sum += this._counters[i]; + } + return sum; + }; + + CodabarReader.prototype._findStart = function () { + var self = this, + i, + pattern, + start = self._nextUnset(self._row), + end; + + for (i = 1; i < this._counters.length; i++) { + pattern = self._toPattern(i); + if (pattern !== -1 && self._isStartEnd(pattern)) { + // TODO: Look for whitespace ahead + start += self._sumCounters(0, i); + end = start + self._sumCounters(i, i + 8); + return { + start: start, + end: end, + startCounter: i, + endCounter: i + 8 + }; + } + } + }; + + exports["default"] = CodabarReader; + module.exports = exports["default"]; + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _ean_reader = __webpack_require__(18); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + function UPCReader() { + _ean_reader2["default"].call(this); + } + + var properties = { + FORMAT: { value: "upc_a", writeable: false } + }; + + UPCReader.prototype = Object.create(_ean_reader2["default"].prototype, properties); + UPCReader.prototype.constructor = UPCReader; + + UPCReader.prototype._decode = function () { + var result = _ean_reader2["default"].prototype._decode.call(this); + + if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") { + result.code = result.code.substring(1); + return result; + } + return null; + }; + + exports["default"] = UPCReader; + module.exports = exports["default"]; + +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _ean_reader = __webpack_require__(18); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + function EAN8Reader() { + _ean_reader2["default"].call(this); + } + + var properties = { + FORMAT: { value: "ean_8", writeable: false } + }; + + EAN8Reader.prototype = Object.create(_ean_reader2["default"].prototype, properties); + EAN8Reader.prototype.constructor = EAN8Reader; + + EAN8Reader.prototype._decodePayload = function (code, result, decodedCodes) { + var i, + self = this; + + for (i = 0; i < 4; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + result.push(code.code); + decodedCodes.push(code); + } + + code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); + if (code === null) { + return null; + } + decodedCodes.push(code); + + for (i = 0; i < 4; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + decodedCodes.push(code); + result.push(code.code); + } + + return code; + }; + + exports["default"] = EAN8Reader; + module.exports = exports["default"]; + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + + var _ean_reader = __webpack_require__(18); + + var _ean_reader2 = _interopRequireDefault(_ean_reader); + + function UPCEReader() { + _ean_reader2["default"].call(this); + } + + var properties = { + CODE_FREQUENCY: { value: [[56, 52, 50, 49, 44, 38, 35, 42, 41, 37], [7, 11, 13, 14, 19, 25, 28, 21, 22, 26]] }, + STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7] }, + FORMAT: { value: "upc_e", writeable: false } + }; + + UPCEReader.prototype = Object.create(_ean_reader2["default"].prototype, properties); + UPCEReader.prototype.constructor = UPCEReader; + + UPCEReader.prototype._decodePayload = function (code, result, decodedCodes) { + var i, + self = this, + codeFrequency = 0x0; + + for (i = 0; i < 6; i++) { + code = self._decodeCode(code.end); + if (!code) { + return null; + } + if (code.code >= self.CODE_G_START) { + code.code = code.code - self.CODE_G_START; + codeFrequency |= 1 << 5 - i; + } + result.push(code.code); + decodedCodes.push(code); + } + if (!self._determineParity(codeFrequency, result)) { + return null; + } + + return code; + }; + + UPCEReader.prototype._determineParity = function (codeFrequency, result) { + var i, nrSystem; + + for (nrSystem = 0; nrSystem < this.CODE_FREQUENCY.length; nrSystem++) { + for (i = 0; i < this.CODE_FREQUENCY[nrSystem].length; i++) { + if (codeFrequency === this.CODE_FREQUENCY[nrSystem][i]) { + result.unshift(nrSystem); + result.push(i); + return true; + } + } + } + return false; + }; + + UPCEReader.prototype._convertToUPCA = function (result) { + var upca = [result[0]], + lastDigit = result[result.length - 2]; + + if (lastDigit <= 2) { + upca = upca.concat(result.slice(1, 3)).concat([lastDigit, 0, 0, 0, 0]).concat(result.slice(3, 6)); + } else if (lastDigit === 3) { + upca = upca.concat(result.slice(1, 4)).concat([0, 0, 0, 0, 0]).concat(result.slice(4, 6)); + } else if (lastDigit === 4) { + upca = upca.concat(result.slice(1, 5)).concat([0, 0, 0, 0, 0, result[5]]); + } else { + upca = upca.concat(result.slice(1, 6)).concat([0, 0, 0, 0, lastDigit]); + } + + upca.push(result[result.length - 1]); + return upca; + }; + + UPCEReader.prototype._checksum = function (result) { + return _ean_reader2["default"].prototype._checksum.call(this, this._convertToUPCA(result)); + }; + + UPCEReader.prototype._findEnd = function (offset, isWhite) { + isWhite = true; + return _ean_reader2["default"].prototype._findEnd.call(this, offset, isWhite); + }; + + UPCEReader.prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start) / 2; + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + }; + + exports["default"] = UPCEReader; + module.exports = exports["default"]; + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _barcode_reader = __webpack_require__(17); + + var _barcode_reader2 = _interopRequireDefault(_barcode_reader); + + var merge = __webpack_require__(26); + + function I2of5Reader(opts) { + opts = merge(getDefaulConfig(), opts); + _barcode_reader2['default'].call(this, opts); + this.barSpaceRatio = [1, 1]; + if (opts.normalizeBarSpaceWidth) { + this.SINGLE_CODE_ERROR = 0.38; + this.AVG_CODE_ERROR = 0.09; + } + } + + function getDefaulConfig() { + var config = {}; + + Object.keys(I2of5Reader.CONFIG_KEYS).forEach(function (key) { + config[key] = I2of5Reader.CONFIG_KEYS[key]['default']; + }); + return config; + } + + var N = 1, + W = 3, + properties = { + MODULO: { value: 10 }, + START_PATTERN: { value: [N * 2.5, N * 2.5, N * 2.5, N * 2.5] }, + STOP_PATTERN: { value: [N * 2, N * 2, W * 2] }, + CODE_PATTERN: { value: [[N, N, W, W, N], [W, N, N, N, W], [N, W, N, N, W], [W, W, N, N, N], [N, N, W, N, W], [W, N, W, N, N], [N, W, W, N, N], [N, N, N, W, W], [W, N, N, W, N], [N, W, N, W, N]] }, + SINGLE_CODE_ERROR: { value: 0.78, writable: true }, + AVG_CODE_ERROR: { value: 0.38, writable: true }, + MAX_CORRECTION_FACTOR: { value: 5 }, + FORMAT: { value: "i2of5" } + }; + + I2of5Reader.prototype = Object.create(_barcode_reader2['default'].prototype, properties); + I2of5Reader.prototype.constructor = I2of5Reader; + + I2of5Reader.prototype._matchPattern = function (counter, code) { + if (this.config.normalizeBarSpaceWidth) { + var i, + counterSum = [0, 0], + codeSum = [0, 0], + correction = [0, 0], + correctionRatio = this.MAX_CORRECTION_FACTOR, + correctionRatioInverse = 1 / correctionRatio; + + for (i = 0; i < counter.length; i++) { + counterSum[i % 2] += counter[i]; + codeSum[i % 2] += code[i]; + } + correction[0] = codeSum[0] / counterSum[0]; + correction[1] = codeSum[1] / counterSum[1]; + + correction[0] = Math.max(Math.min(correction[0], correctionRatio), correctionRatioInverse); + correction[1] = Math.max(Math.min(correction[1], correctionRatio), correctionRatioInverse); + this.barSpaceRatio = correction; + for (i = 0; i < counter.length; i++) { + counter[i] *= this.barSpaceRatio[i % 2]; + } + } + return _barcode_reader2['default'].prototype._matchPattern.call(this, counter, code); + }; + + I2of5Reader.prototype._findPattern = function (pattern, offset, isWhite, tryHarder) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized, + epsilon = self.AVG_CODE_ERROR; + + isWhite = isWhite || false; + tryHarder = tryHarder || false; + + if (!offset) { + offset = self._nextSet(self._row); + } + + for (i = 0; i < pattern.length; i++) { + counter[i] = 0; + } + + for (i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + error = self._matchPattern(normalized, pattern); + + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + } + if (tryHarder) { + for (j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; + } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + I2of5Reader.prototype._findStart = function () { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo, + narrowBarWidth = 1; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset, false, true); + if (!startInfo) { + return null; + } + narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / 4); + leadingWhitespaceStart = startInfo.start - narrowBarWidth * 10; + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; + } + } + offset = startInfo.end; + startInfo = null; + } + }; + + I2of5Reader.prototype._verifyTrailingWhitespace = function (endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start) / 2; + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; + }; + + I2of5Reader.prototype._findEnd = function () { + var self = this, + endInfo, + tmp; + + self._row.reverse(); + endInfo = self._findPattern(self.STOP_PATTERN); + self._row.reverse(); + + if (endInfo === null) { + return null; + } + + // reverse numbers + tmp = endInfo.start; + endInfo.start = self._row.length - endInfo.end; + endInfo.end = self._row.length - tmp; + + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; + }; + + I2of5Reader.prototype._decodePair = function (counterPair) { + var i, + code, + codes = [], + self = this; + + for (i = 0; i < counterPair.length; i++) { + code = self._decodeCode(counterPair[i]); + if (!code) { + return null; + } + codes.push(code); + } + return codes; + }; + + I2of5Reader.prototype._decodeCode = function (counter) { + var j, + self = this, + sum = 0, + normalized, + error, + epsilon = self.AVG_CODE_ERROR, + code, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }; + + for (j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + if (bestMatch.error < epsilon) { + return bestMatch; + } + } + return null; + }; + + I2of5Reader.prototype._decodePayload = function (counters, result, decodedCodes) { + var i, + self = this, + pos = 0, + counterLength = counters.length, + counterPair = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], + codes; + + while (pos < counterLength) { + for (i = 0; i < 5; i++) { + counterPair[0][i] = counters[pos] * this.barSpaceRatio[0]; + counterPair[1][i] = counters[pos + 1] * this.barSpaceRatio[1]; + pos += 2; + } + codes = self._decodePair(counterPair); + if (!codes) { + return null; + } + for (i = 0; i < codes.length; i++) { + result.push(codes[i].code + ""); + decodedCodes.push(codes[i]); + } + } + return codes; + }; + + I2of5Reader.prototype._verifyCounterLength = function (counters) { + return counters.length % 10 === 0; + }; + + I2of5Reader.prototype._decode = function () { + var startInfo, + endInfo, + self = this, + code, + result = [], + decodedCodes = [], + counters; + + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + decodedCodes.push(startInfo); + + endInfo = self._findEnd(); + if (!endInfo) { + return null; + } + + counters = self._fillCounters(startInfo.end, endInfo.start, false); + if (!self._verifyCounterLength(counters)) { + return null; + } + code = self._decodePayload(counters, result, decodedCodes); + if (!code) { + return null; + } + if (result.length % 2 !== 0 || result.length < 6) { + return null; + } + + decodedCodes.push(endInfo); + return { + code: result.join(""), + start: startInfo.start, + end: endInfo.end, + startInfo: startInfo, + decodedCodes: decodedCodes + }; + }; + + I2of5Reader.CONFIG_KEYS = { + normalizeBarSpaceWidth: { + 'type': 'boolean', + 'default': false, + 'description': 'If true, the reader tries to normalize the' + 'width-difference between bars and spaces' + } + }; + + exports['default'] = I2of5Reader; + module.exports = exports['default']; + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + var baseMerge = __webpack_require__(27), + createAssigner = __webpack_require__(54); + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * overwrite property assignments of previous sources. If `customizer` is + * provided it's invoked to produce the merged values of the destination and + * source properties. If `customizer` returns `undefined` merging is handled + * by the method instead. The `customizer` is bound to `thisArg` and invoked + * with five arguments: (objectValue, sourceValue, key, object, source). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * var users = { + * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] + * }; + * + * var ages = { + * 'data': [{ 'age': 36 }, { 'age': 40 }] + * }; + * + * _.merge(users, ages); + * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } + * + * // using a customizer callback + * var object = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var other = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(object, other, function(a, b) { + * if (_.isArray(a)) { + * return a.concat(b); + * } + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } + */ + var merge = createAssigner(baseMerge); + + module.exports = merge; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + var arrayEach = __webpack_require__(28), + baseMergeDeep = __webpack_require__(29), + isArray = __webpack_require__(37), + isArrayLike = __webpack_require__(32), + isObject = __webpack_require__(41), + isObjectLike = __webpack_require__(36), + isTypedArray = __webpack_require__(49), + keys = __webpack_require__(52); + + /** + * The base implementation of `_.merge` without support for argument juggling, + * multiple sources, and `this` binding `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {Object} Returns `object`. + */ + function baseMerge(object, source, customizer, stackA, stackB) { + if (!isObject(object)) { + return object; + } + var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), + props = isSrcArr ? undefined : keys(source); + + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } + if (isObjectLike(srcValue)) { + stackA || (stackA = []); + stackB || (stackB = []); + baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); + } + else { + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + } + if ((result !== undefined || (isSrcArr && !(key in object))) && + (isCommon || (result === result ? (result !== value) : (value === value)))) { + object[key] = result; + } + } + }); + return object; + } + + module.exports = baseMerge; + + +/***/ }, +/* 28 */ +/***/ function(module, exports) { + + /** + * A specialized version of `_.forEach` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + module.exports = arrayEach; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + var arrayCopy = __webpack_require__(30), + isArguments = __webpack_require__(31), + isArray = __webpack_require__(37), + isArrayLike = __webpack_require__(32), + isPlainObject = __webpack_require__(42), + isTypedArray = __webpack_require__(49), + toPlainObject = __webpack_require__(50); + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { + var length = stackA.length, + srcValue = source[key]; + + while (length--) { + if (stackA[length] == srcValue) { + object[key] = stackB[length]; + return; + } + } + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) { + result = isArray(value) + ? value + : (isArrayLike(value) ? arrayCopy(value) : []); + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + result = isArguments(value) + ? toPlainObject(value) + : (isPlainObject(value) ? value : {}); + } + else { + isCommon = false; + } + } + // Add the source value to the stack of traversed objects and associate + // it with its merged value. + stackA.push(srcValue); + stackB.push(result); + + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); + } else if (result === result ? (result !== value) : (value === value)) { + object[key] = result; + } + } + + module.exports = baseMergeDeep; + + +/***/ }, +/* 30 */ +/***/ function(module, exports) { + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function arrayCopy(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + module.exports = arrayCopy; + + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + var isArrayLike = __webpack_require__(32), + isObjectLike = __webpack_require__(36); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Native method references. */ + var propertyIsEnumerable = objectProto.propertyIsEnumerable; + + /** + * Checks if `value` is classified as an `arguments` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); + } + + module.exports = isArguments; + + +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { + + var getLength = __webpack_require__(33), + isLength = __webpack_require__(35); + + /** + * Checks if `value` is array-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + */ + function isArrayLike(value) { + return value != null && isLength(getLength(value)); + } + + module.exports = isArrayLike; + + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + var baseProperty = __webpack_require__(34); + + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * that affects Safari on at least iOS 8.1-8.3 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + module.exports = getLength; + + +/***/ }, +/* 34 */ +/***/ function(module, exports) { + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + module.exports = baseProperty; + + +/***/ }, +/* 35 */ +/***/ function(module, exports) { + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + module.exports = isLength; + + +/***/ }, +/* 36 */ +/***/ function(module, exports) { + + /** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + module.exports = isObjectLike; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + var getNative = __webpack_require__(38), + isLength = __webpack_require__(35), + isObjectLike = __webpack_require__(36); + + /** `Object#toString` result references. */ + var arrayTag = '[object Array]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeIsArray = getNative(Array, 'isArray'); + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */ + var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; + }; + + module.exports = isArray; + + +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { + + var isNative = __webpack_require__(39); + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; + } + + module.exports = getNative; + + +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { + + var isFunction = __webpack_require__(40), + isObjectLike = __webpack_require__(36); + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to resolve the decompiled source of functions. */ + var fnToString = Function.prototype.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); + } + + module.exports = isNative; + + +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(41); + + /** `Object#toString` result references. */ + var funcTag = '[object Function]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 which returns 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; + } + + module.exports = isFunction; + + +/***/ }, +/* 41 */ +/***/ function(module, exports) { + + /** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + module.exports = isObject; + + +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { + + var baseForIn = __webpack_require__(43), + isArguments = __webpack_require__(31), + isObjectLike = __webpack_require__(36); + + /** `Object#toString` result references. */ + var objectTag = '[object Object]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * **Note:** This method assumes objects created by the `Object` constructor + * have no inherited enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + var Ctor; + + // Exit early for non `Object` objects. + if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) || + (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + var result; + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + baseForIn(value, function(subValue, key) { + result = key; + }); + return result === undefined || hasOwnProperty.call(value, result); + } + + module.exports = isPlainObject; + + +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { + + var baseFor = __webpack_require__(44), + keysIn = __webpack_require__(47); + + /** + * The base implementation of `_.forIn` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForIn(object, iteratee) { + return baseFor(object, iteratee, keysIn); + } + + module.exports = baseForIn; + + +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { + + var createBaseFor = __webpack_require__(45); + + /** + * The base implementation of `baseForIn` and `baseForOwn` which iterates + * over `object` properties returned by `keysFunc` invoking `iteratee` for + * each property. Iteratee functions may exit iteration early by explicitly + * returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + module.exports = baseFor; + + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + var toObject = __webpack_require__(46); + + /** + * Creates a base function for `_.forIn` or `_.forInRight`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var iterable = toObject(object), + props = keysFunc(object), + length = props.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + var key = props[index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + module.exports = createBaseFor; + + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(41); + + /** + * Converts `value` to an object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Object} Returns the object. + */ + function toObject(value) { + return isObject(value) ? value : Object(value); + } + + module.exports = toObject; + + +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + var isArguments = __webpack_require__(31), + isArray = __webpack_require__(37), + isIndex = __webpack_require__(48), + isLength = __webpack_require__(35), + isObject = __webpack_require__(41); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + if (object == null) { + return []; + } + if (!isObject(object)) { + object = Object(object); + } + var length = object.length; + length = (length && isLength(length) && + (isArray(object) || isArguments(object)) && length) || 0; + + var Ctor = object.constructor, + index = -1, + isProto = typeof Ctor == 'function' && Ctor.prototype === object, + result = Array(length), + skipIndexes = length > 0; + + while (++index < length) { + result[index] = (index + ''); + } + for (var key in object) { + if (!(skipIndexes && isIndex(key, length)) && + !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + module.exports = keysIn; + + +/***/ }, +/* 48 */ +/***/ function(module, exports) { + + /** Used to detect unsigned integer values. */ + var reIsUint = /^\d+$/; + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; + length = length == null ? MAX_SAFE_INTEGER : length; + return value > -1 && value % 1 == 0 && value < length; + } + + module.exports = isIndex; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + var isLength = __webpack_require__(35), + isObjectLike = __webpack_require__(36); + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dateTag] = typedArrayTags[errorTag] = + typedArrayTags[funcTag] = typedArrayTags[mapTag] = + typedArrayTags[numberTag] = typedArrayTags[objectTag] = + typedArrayTags[regexpTag] = typedArrayTags[setTag] = + typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + function isTypedArray(value) { + return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; + } + + module.exports = isTypedArray; + + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + var baseCopy = __webpack_require__(51), + keysIn = __webpack_require__(47); + + /** + * Converts `value` to a plain object flattening inherited enumerable + * properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return baseCopy(value, keysIn(value)); + } + + module.exports = toPlainObject; + + +/***/ }, +/* 51 */ +/***/ function(module, exports) { + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. + * @returns {Object} Returns `object`. + */ + function baseCopy(source, props, object) { + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + object[key] = source[key]; + } + return object; + } + + module.exports = baseCopy; + + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + + var getNative = __webpack_require__(38), + isArrayLike = __webpack_require__(32), + isObject = __webpack_require__(41), + shimKeys = __webpack_require__(53); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeKeys = getNative(Object, 'keys'); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) + * for more details. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + var keys = !nativeKeys ? shimKeys : function(object) { + var Ctor = object == null ? undefined : object.constructor; + if ((typeof Ctor == 'function' && Ctor.prototype === object) || + (typeof object != 'function' && isArrayLike(object))) { + return shimKeys(object); + } + return isObject(object) ? nativeKeys(object) : []; + }; + + module.exports = keys; + + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + var isArguments = __webpack_require__(31), + isArray = __webpack_require__(37), + isIndex = __webpack_require__(48), + isLength = __webpack_require__(35), + keysIn = __webpack_require__(47); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * A fallback implementation of `Object.keys` which creates an array of the + * own enumerable property names of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function shimKeys(object) { + var props = keysIn(object), + propsLength = props.length, + length = propsLength && object.length; + + var allowIndexes = !!length && isLength(length) && + (isArray(object) || isArguments(object)); + + var index = -1, + result = []; + + while (++index < propsLength) { + var key = props[index]; + if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { + result.push(key); + } + } + return result; + } + + module.exports = shimKeys; + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + var bindCallback = __webpack_require__(55), + isIterateeCall = __webpack_require__(57), + restParam = __webpack_require__(58); + + /** + * Creates a `_.assign`, `_.defaults`, or `_.merge` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 ? sources[length - 2] : undefined, + guard = length > 2 ? sources[2] : undefined, + thisArg = length > 1 ? sources[length - 1] : undefined; + + if (typeof customizer == 'function') { + customizer = bindCallback(customizer, thisArg, 5); + length -= 2; + } else { + customizer = typeof thisArg == 'function' ? thisArg : undefined; + length -= (customizer ? 1 : 0); + } + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, customizer); + } + } + return object; + }); + } + + module.exports = createAssigner; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + var identity = __webpack_require__(56); + + /** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; + } + + module.exports = bindCallback; + + +/***/ }, +/* 56 */ +/***/ function(module, exports) { + + /** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'user': 'fred' }; + * + * _.identity(object) === object; + * // => true + */ + function identity(value) { + return value; + } + + module.exports = identity; + + +/***/ }, +/* 57 */ +/***/ function(module, exports, __webpack_require__) { + + var isArrayLike = __webpack_require__(32), + isIndex = __webpack_require__(48), + isObject = __webpack_require__(41); + + /** + * Checks if the provided arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object)) { + var other = object[index]; + return value === value ? (value === other) : (other !== other); + } + return false; + } + + module.exports = isIterateeCall; + + +/***/ }, +/* 58 */ +/***/ function(module, exports) { + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeMax = Math.max; + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function restParam(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + rest = Array(length); + + while (++index < length) { + rest[index] = args[start + index]; + } + switch (start) { + case 0: return func.call(this, rest); + case 1: return func.call(this, args[0], rest); + case 2: return func.call(this, args[0], args[1], rest); + } + var otherArgs = Array(start + 1); + index = -1; + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = rest; + return func.apply(this, otherArgs); + }; + } + + module.exports = restParam; + + +/***/ }, +/* 59 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports["default"] = { + inputStream: { + name: "Live", + type: "LiveStream", + constraints: { + width: 640, + height: 480, + minAspectRatio: 0, + maxAspectRatio: 100, + facing: "environment" // or user + }, + area: { + top: "0%", + right: "0%", + left: "0%", + bottom: "0%" + }, + singleChannel: false // true: only the red color-channel is read + }, + tracking: false, + debug: false, + controls: false, + locate: true, + numOfWorkers: 4, + visual: { + show: true + }, + decoder: { + drawBoundingBox: false, + showFrequency: false, + drawScanline: false, + showPattern: false, + readers: ['code_128_reader'] + }, + locator: { + halfSample: true, + patchSize: "medium", // x-small, small, medium, large, x-large + showCanvas: false, + showPatches: false, + showFoundPatches: false, + showSkeleton: false, + showLabels: false, + showPatchLabels: false, + showRemainingPatchLabels: false, + boxFromPatches: { + showTransformed: false, + showTransformedBox: false, + showBB: false + } + } + }; + module.exports = exports["default"]; + +/***/ }, +/* 60 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + exports["default"] = (function () { + var events = {}; + + function getEvent(eventName) { + if (!events[eventName]) { + events[eventName] = { + subscribers: [] + }; + } + return events[eventName]; + } + + function clearEvents() { + events = {}; + } + + function publishSubscription(subscription, data) { + if (subscription.async) { + setTimeout(function () { + subscription.callback(data); + }, 4); + } else { + subscription.callback(data); + } + } + + function _subscribe(event, callback, async) { + var subscription; + + if (typeof callback === "function") { + subscription = { + callback: callback, + async: async + }; + } else { + subscription = callback; + if (!subscription.callback) { + throw "Callback was not specified on options"; + } + } + + getEvent(event).subscribers.push(subscription); + } + + return { + subscribe: function subscribe(event, callback, async) { + return _subscribe(event, callback, async); + }, + publish: function publish(eventName, data) { + var event = getEvent(eventName), + subscribers = event.subscribers; + + event.subscribers = subscribers.filter(function (subscriber) { + publishSubscription(subscriber, data); + return !subscriber.once; + }); + }, + once: function once(event, callback, async) { + _subscribe(event, { + callback: callback, + async: async, + once: true + }); + }, + unsubscribe: function unsubscribe(eventName, callback) { + var event; + + if (eventName) { + event = getEvent(eventName); + if (event && callback) { + event.subscribers = event.subscribers.filter(function (subscriber) { + return subscriber.callback !== callback; + }); + } else { + event.subscribers = []; + } + } else { + clearEvents(); + } + } + }; + })(); + + module.exports = exports["default"]; + +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + var merge = __webpack_require__(26); + + var streamRef, loadedDataHandler; + + /** + * Wraps browser-specific getUserMedia + * @param {Object} constraints + * @param {Object} success Callback + * @param {Object} failure Callback + */ + function getUserMedia(constraints, success, failure) { + if (typeof navigator.getUserMedia !== 'undefined') { + navigator.getUserMedia(constraints, function (stream) { + streamRef = stream; + var videoSrc = window.URL && window.URL.createObjectURL(stream) || stream; + success.apply(null, [videoSrc]); + }, failure); + } else { + failure(new TypeError("getUserMedia not available")); + } + } + + function loadedData(video, callback) { + var attempts = 10; + + function checkVideo() { + if (attempts > 0) { + if (video.videoWidth > 0 && video.videoHeight > 0) { + console.log(video.videoWidth + "px x " + video.videoHeight + "px"); + callback(); + } else { + window.setTimeout(checkVideo, 500); + } + } else { + callback('Unable to play video stream. Is webcam working?'); + } + attempts--; + } + checkVideo(); + } + + /** + * Tries to attach the camera-stream to a given video-element + * and calls the callback function when the content is ready + * @param {Object} constraints + * @param {Object} video + * @param {Object} callback + */ + function initCamera(constraints, video, callback) { + getUserMedia(constraints, function (src) { + video.src = src; + if (loadedDataHandler) { + video.removeEventListener("loadeddata", loadedDataHandler, false); + } + loadedDataHandler = loadedData.bind(null, video, callback); + video.addEventListener('loadeddata', loadedDataHandler, false); + video.play(); + }, function (e) { + callback(e); + }); + } + + /** + * Normalizes the incoming constraints to satisfy the current browser + * @param config + * @param cb Callback which is called whenever constraints are created + * @returns {*} + */ + function normalizeConstraints(config, cb) { + var constraints = { + audio: false, + video: true + }, + videoConstraints = merge({ + width: 640, + height: 480, + minAspectRatio: 0, + maxAspectRatio: 100, + facing: "environment" + }, config); + + if (typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') { + MediaStreamTrack.getSources(function (sourceInfos) { + var videoSourceId; + for (var i = 0; i < sourceInfos.length; ++i) { + var sourceInfo = sourceInfos[i]; + if (sourceInfo.kind === "video" && sourceInfo.facing === videoConstraints.facing) { + videoSourceId = sourceInfo.id; + } + } + constraints.video = { + mandatory: { + minWidth: videoConstraints.width, + minHeight: videoConstraints.height, + minAspectRatio: videoConstraints.minAspectRatio, + maxAspectRatio: videoConstraints.maxAspectRatio + }, + optional: [{ + sourceId: videoSourceId + }] + }; + return cb(constraints); + }); + } else { + constraints.video = { + mediaSource: "camera", + width: { min: videoConstraints.width, max: videoConstraints.width }, + height: { min: videoConstraints.height, max: videoConstraints.height }, + require: ["width", "height"] + }; + return cb(constraints); + } + } + + /** + * Requests the back-facing camera of the user. The callback is called + * whenever the stream is ready to be consumed, or if an error occures. + * @param {Object} video + * @param {Object} callback + */ + function _request(video, videoConstraints, callback) { + normalizeConstraints(videoConstraints, function (constraints) { + initCamera(constraints, video, callback); + }); + } + + exports['default'] = { + request: function request(video, constraints, callback) { + _request(video, constraints, callback); + }, + release: function release() { + var tracks = streamRef && streamRef.getVideoTracks(); + if (tracks.length) { + tracks[0].stop(); + } + streamRef = null; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _image_debug = __webpack_require__(13); + + var _image_debug2 = _interopRequireDefault(_image_debug); + + function contains(codeResult, list) { + if (list) { + return list.some(function (item) { + return Object.keys(item).every(function (key) { + return item[key] === codeResult[key]; + }); + }); + } + return false; + } + + function passesFilter(codeResult, filter) { + if (typeof filter === 'function') { + return filter(codeResult); + } + return true; + } + + exports['default'] = { + create: function create(config) { + var canvas = document.createElement("canvas"), + ctx = canvas.getContext("2d"), + results = [], + capacity = config.capacity || 20, + capture = config.capture === true; + + function matchesConstraints(codeResult) { + return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter); + } + + return { + addResult: function addResult(data, imageSize, codeResult) { + var result = {}; + + if (matchesConstraints(codeResult)) { + capacity--; + result.codeResult = codeResult; + if (capture) { + canvas.width = imageSize.x; + canvas.height = imageSize.y; + _image_debug2['default'].drawImage(data, imageSize, ctx); + result.frame = canvas.toDataURL(); + } + results.push(result); + } + }, + getResults: function getResults() { + return results; + } + }; + } + }; + module.exports = exports['default']; + +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var GetPixels = __webpack_require__(64); + + var InputStream = {}; + + InputStream.createImageStream = function () { + var that = {}; + var _config = null; + + var width = 0, + height = 0, + frameIdx = 0, + paused = true, + loaded = false, + frame = null, + baseUrl, + ended = false, + size, + calculatedWidth, + calculatedHeight, + _eventNames = ['canrecord', 'ended'], + _eventHandlers = {}, + _topRight = { x: 0, y: 0 }, + _canvasSize = { x: 0, y: 0 }; + + function loadImages() { + loaded = false; + GetPixels(baseUrl, function (err, pixels) { + if (err) { + console.log(err); + exit(1); + } + loaded = true; + console.log(pixels.shape); + frame = pixels; + width = pixels.shape[0]; + height = pixels.shape[1]; + calculatedWidth = _config.size ? width / height > 1 ? _config.size : Math.floor(width / height * _config.size) : width; + calculatedHeight = _config.size ? width / height > 1 ? Math.floor(height / width * _config.size) : _config.size : height; + + _canvasSize.x = calculatedWidth; + _canvasSize.y = calculatedHeight; + + setTimeout(function () { + publishEvent("canrecord", []); + }, 0); + }); + } + + function publishEvent(eventName, args) { + var j, + handlers = _eventHandlers[eventName]; + + if (handlers && handlers.length > 0) { + for (j = 0; j < handlers.length; j++) { + handlers[j].apply(that, args); + } + } + } + + that.trigger = publishEvent; + + that.getWidth = function () { + return calculatedWidth; + }; + + that.getHeight = function () { + return calculatedHeight; + }; + + that.setWidth = function (width) { + calculatedWidth = width; + }; + + that.setHeight = function (height) { + calculatedHeight = height; + }; + + that.getRealWidth = function () { + return width; + }; + + that.getRealHeight = function () { + return height; + }; + + that.setInputStream = function (stream) { + _config = stream; + baseUrl = _config.src; + size = 1; + loadImages(); + }; + + that.ended = function () { + return ended; + }; + + that.setAttribute = function () {}; + + that.getConfig = function () { + return _config; + }; + + that.pause = function () { + paused = true; + }; + + that.play = function () { + paused = false; + }; + + that.setCurrentTime = function (time) { + frameIdx = time; + }; + + that.addEventListener = function (event, f) { + if (_eventNames.indexOf(event) !== -1) { + if (!_eventHandlers[event]) { + _eventHandlers[event] = []; + } + _eventHandlers[event].push(f); + } + }; + + that.setTopRight = function (topRight) { + _topRight.x = topRight.x; + _topRight.y = topRight.y; + }; + + that.getTopRight = function () { + return _topRight; + }; + + that.setCanvasSize = function (size) { + _canvasSize.x = size.x; + _canvasSize.y = size.y; + }; + + that.getCanvasSize = function () { + return _canvasSize; + }; + + that.getFrame = function () { + if (!loaded) { + return null; + } + return frame; + }; + + return that; + }; + + module.exports = InputStream; + +/***/ }, +/* 64 */ +/***/ function(module, exports) { + + module.exports = require("get-pixels"); + +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + var CVUtils = __webpack_require__(5), + Ndarray = __webpack_require__(66), + Interp2D = __webpack_require__(67).d2; + + var FrameGrabber = {}; + + FrameGrabber.create = function (inputStream) { + var _that = {}, + _streamConfig = inputStream.getConfig(), + _video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), + _canvasSize = inputStream.getCanvasSize(), + _size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), + _topRight = inputStream.getTopRight(), + _data = new Uint8Array(_size.x * _size.y), + _grayData = new Uint8Array(_video_size.x * _video_size.y), + _canvasData = new Uint8Array(_canvasSize.x * _canvasSize.y), + _grayImageArray = Ndarray(_grayData, [_video_size.y, _video_size.x]).transpose(1, 0), + _canvasImageArray = Ndarray(_canvasData, [_canvasSize.y, _canvasSize.x]).transpose(1, 0), + _targetImageArray = _canvasImageArray.hi(_topRight.x + _size.x, _topRight.y + _size.y).lo(_topRight.x, _topRight.y), + _stepSizeX = _video_size.x / _canvasSize.x, + _stepSizeY = _video_size.y / _canvasSize.y; + + console.log("FrameGrabber", JSON.stringify({ + videoSize: _grayImageArray.shape, + canvasSize: _canvasImageArray.shape, + stepSize: [_stepSizeX, _stepSizeY], + size: _targetImageArray.shape, + topRight: _topRight + })); + + /** + * Uses the given array as frame-buffer + */ + _that.attachData = function (data) { + _data = data; + }; + + /** + * Returns the used frame-buffer + */ + _that.getData = function () { + return _data; + }; + + /** + * Fetches a frame from the input-stream and puts into the frame-buffer. + * The image-data is converted to gray-scale and then half-sampled if configured. + */ + _that.grab = function () { + var frame = inputStream.getFrame(); + + if (frame) { + this.scaleAndCrop(frame); + return true; + } else { + return false; + } + }; + + _that.scaleAndCrop = function (frame) { + var x, y; + + // 1. compute full-sized gray image + CVUtils.computeGray(frame.data, _grayData); + + // 2. interpolate + for (y = 0; y < _canvasSize.y; y++) { + for (x = 0; x < _canvasSize.x; x++) { + _canvasImageArray.set(x, y, Interp2D(_grayImageArray, x * _stepSizeX, y * _stepSizeY) | 0); + } + } + + // targetImageArray must be equal to targetSize + if (_targetImageArray.shape[0] !== _size.x || _targetImageArray.shape[1] !== _size.y) { + throw new Error("Shapes do not match!"); + } + + // 3. crop + for (y = 0; y < _size.y; y++) { + for (x = 0; x < _size.x; x++) { + _data[y * _size.x + x] = _targetImageArray.get(x, y); + } + } + }, _that.getSize = function () { + return _size; + }; + + return _that; + }; + + module.exports = FrameGrabber; + +/***/ }, +/* 66 */ +/***/ function(module, exports) { + + module.exports = require("ndarray"); + +/***/ }, +/* 67 */ +/***/ function(module, exports) { + + module.exports = require("ndarray-linear-interpolate"); + +/***/ } +/******/ ]); +//# sourceMappingURL=quagga.map \ No newline at end of file diff --git a/package.json b/package.json index 5dd7a810..4f0eb058 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,23 @@ { "name": "quagga", - "version": "0.7.0", + "version": "0.8.0", "description": "An advanced barcode-scanner written in JavaScript", "main": "lib/quagga.js", - "browser": "dist/quagga.js", + "browser": "dist/quagga.min.js", "devDependencies": { "async": "^1.4.2", + "babel-core": "^5.8.25", + "babel-eslint": "^4.1.3", + "babel-loader": "^5.3.2", "chai": "^3.2.0", + "core-js": "^1.2.1", "grunt": "^0.4.5", "grunt-contrib-jshint": "^0.11.3", "grunt-contrib-nodeunit": "^0.4.1", "grunt-contrib-uglify": "^0.9.2", "grunt-karma": "^0.12.1", "grunt-requirejs": "^0.4.2", + "isparta-loader": "^1.0.0", "karma": "^0.13.9", "karma-chai": "0.1.0", "karma-chrome-launcher": "^0.2.0", @@ -22,14 +27,20 @@ "karma-requirejs": "~0.2.2", "karma-sinon": "^1.0.4", "karma-sinon-chai": "~0.2.0", + "karma-source-map-support": "^1.1.0", + "karma-webpack": "^1.7.0", "mocha": "^2.3.2", - "sinon": "^1.16.1" + "sinon": "^1.16.1", + "webpack": "^1.12.2" }, "directories": { "doc": "doc" }, "scripts": { - "test": "karma start" + "test": "grunt test", + "integrationtest": "grunt integrationtest", + "build": "webpack && webpack --config webpack.config.min.js && grunt uglyasm && webpack --config webpack.node.config.js", + "watch": "webpack --watch" }, "repository": { "type": "git", @@ -56,8 +67,8 @@ "dependencies": { "get-pixels": "^3.2.3", "gl-matrix": "^2.3.1", + "lodash": "^3.10.1", "ndarray": "^1.0.18", - "ndarray-linear-interpolate": "^1.0.0", - "requirejs": "^2.1.20" + "ndarray-linear-interpolate": "^1.0.0" } } diff --git a/plugins/umd.js b/plugins/umd.js new file mode 100644 index 00000000..8def396a --- /dev/null +++ b/plugins/umd.js @@ -0,0 +1,33 @@ +var ConcatSource = require("webpack-core/lib/ConcatSource"); +var OriginalSource = require("webpack-core/lib/OriginalSource"); + +function MyUmdPlugin(options) { + this.name = options.library; +} +module.exports = MyUmdPlugin; +MyUmdPlugin.prototype.apply = function(compiler) { + compiler.plugin("this-compilation", function(compilation) { + var mainTemplate = compilation.mainTemplate; + compilation.templatesPlugin("render-with-entry", function(source, chunk, hash) { + + var amdFactory = "factory"; + return new ConcatSource(new OriginalSource( + "(function webpackUniversalModuleDefinition(root, factory) {\n" + + " if(typeof exports === 'object' && typeof module === 'object')\n" + + " module.exports = factory(factory.toString());\n" + + " else if(typeof exports === 'object')\n" + + " exports[\"" + this.name + "\"] = factory(factory.toString());\n" + + " else\n" + + " root[\"" + this.name + "\"] = factory(factory.toString());\n" + + "})(this, function(__factorySource__) {\nreturn ", "webpack/myModuleDefinition"), source, "\n});\n"); + }.bind(this)); + mainTemplate.plugin("global-hash-paths", function(paths) { + if(this.name) paths = paths.concat(this.name); + return paths; + }.bind(this)); + mainTemplate.plugin("hash", function(hash) { + hash.update("umd"); + hash.update(this.name + ""); + }.bind(this)); + }.bind(this)); +}; diff --git a/spec/array_helper.spec.js b/spec/array_helper.spec.js deleted file mode 100644 index 8e10764e..00000000 --- a/spec/array_helper.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -define(['array_helper'], function(ArrayHelper){ - describe('init', function() { - it('initializes an array with the given value', function() { - var input = [0, 0, 0]; - ArrayHelper.init(input, 5); - expect(input).to.deep.equal([5, 5, 5]); - }); - }); - - describe('shuffle', function() { - before(function() { - sinon.stub(Math, 'random').returns(0.5); - }); - - after(function() { - sinon.restore(Math); - }); - it('shuffles the content of an array', function() { - var input = [1, 2, 3]; - expect(ArrayHelper.shuffle(input)).to.deep.equal([3, 1, 2]); - }); - }); - - describe('toPointList', function() { - it('converts an Array to a List of poitns', function() { - var input = [[1, 2], [2, 2], [3, 2]]; - expect(ArrayHelper.toPointList(input)).to.equal("[[1,2],\r\n[2,2],\r\n[3,2]]"); - }); - }); - - describe('threshold', function() { - it('returns all elements above the given threshold', function() { - var input = [1, 2, 3]; - expect(ArrayHelper.threshold(input, 2, function(score) { - return score * 1.5; - })).to.deep.equal([2, 3]); - }); - }); - - describe('maxIndex', function() { - it('gets the index of the biggest element in the array', function() { - var input = [1, 2, 3]; - expect(ArrayHelper.maxIndex(input)).to.equal(2); - }); - }); - - describe('max', function() { - it('gets the biggest element in the array', function() { - var input = [1, 3, 2]; - expect(ArrayHelper.max(input)).to.equal(3); - }); - }); -}); diff --git a/spec/barcode_locator.spec.js b/spec/barcode_locator.spec.js deleted file mode 100644 index 68f540ad..00000000 --- a/spec/barcode_locator.spec.js +++ /dev/null @@ -1,131 +0,0 @@ - -define(['barcode_locator', 'config', 'html_utils'], - function(BarcodeLocator, Config, HtmlUtils){ - - describe('checkImageConstraints', function() { - var config, - inputStream, - imageSize, - streamConfig = {}; - - beforeEach(function() { - imageSize = { - x: 640, y: 480 - }; - config = HtmlUtils.mergeObjects({}, Config); - inputStream = { - getWidth: function() { - return imageSize.x; - }, - getHeight: function() { - return imageSize.y; - }, - setWidth: function() {}, - setHeight: function() {}, - setTopRight: function() {}, - setCanvasSize: function() {}, - getConfig: function() { - return streamConfig; - } - }; - sinon.stub(inputStream, "setWidth", function(width) { - imageSize.x = width; - }); - sinon.stub(inputStream, "setHeight", function(height) { - imageSize.y = height; - }); - sinon.stub(inputStream, "setTopRight"); - sinon.stub(inputStream, "setCanvasSize"); - }); - - afterEach(function() { - inputStream.setWidth.restore(); - inputStream.setHeight.restore(); - }); - - it('should not adjust the image-size if not needed', function() { - var expected = {x: imageSize.x, y: imageSize.y}; - BarcodeLocator.checkImageConstraints(inputStream, config.locator); - expect(inputStream.getWidth()).to.be.equal(expected.x); - expect(inputStream.getHeight()).to.be.equal(expected.y); - }); - - it('should adjust the image-size', function() { - var expected = {x: imageSize.x, y: imageSize.y}; - - config.locator.halfSample = true; - imageSize.y += 1; - BarcodeLocator.checkImageConstraints(inputStream, config.locator); - expect(inputStream.getWidth()).to.be.equal(expected.x); - expect(inputStream.getHeight()).to.be.equal(expected.y); - }); - - it('should adjust the image-size', function() { - var expected = {x: imageSize.x, y: imageSize.y}; - - imageSize.y += 1; - config.locator.halfSample = false; - BarcodeLocator.checkImageConstraints(inputStream, config.locator); - expect(inputStream.getHeight()).to.be.equal(expected.y); - expect(inputStream.getWidth()).to.be.equal(expected.x); - }); - - it("should take the defined area into account", function() { - var expectedSize = { - x: 420, - y: 315 - }, - expectedTopRight = { - x: 115, - y: 52 - }, - expectedCanvasSize = { - x: 640, - y: 480 - }; - - streamConfig.area = { - top: "11%", - right: "15%", - bottom: "20%", - left: "18%" - }; - - config.locator.halfSample = false; - BarcodeLocator.checkImageConstraints(inputStream, config.locator); - expect(inputStream.getHeight()).to.be.equal(expectedSize.y); - expect(inputStream.getWidth()).to.be.equal(expectedSize.x); - expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight); - expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize); - }); - - it("should return the original size if set to full image", function() { - var expectedSize = { - x: 640, - y: 480 - }, - expectedTopRight = { - x: 0, - y: 0 - }, - expectedCanvasSize = { - x: 640, - y: 480 - }; - - streamConfig.area = { - top: "0%", - right: "0%", - bottom: "0%", - left: "0%" - }; - - config.locator.halfSample = false; - BarcodeLocator.checkImageConstraints(inputStream, config.locator); - expect(inputStream.getHeight()).to.be.equal(expectedSize.y); - expect(inputStream.getWidth()).to.be.equal(expectedSize.x); - expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight); - expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize); - }); - }); -}); diff --git a/spec/camera_access.spec.js b/spec/camera_access.spec.js deleted file mode 100644 index 13206063..00000000 --- a/spec/camera_access.spec.js +++ /dev/null @@ -1,123 +0,0 @@ -define(['camera_access'], function(CameraAccess){ - var originalURL, - originalMediaStreamTrack, - video, - stream; - - beforeEach(function() { - var tracks = [{ - stop: function() {} - }]; - - originalURL = window.URL; - originalMediaStreamTrack = window.MediaStreamTrack; - window.MediaStreamTrack = {}; - window.URL = null; - - - stream = { - getVideoTracks: function() { - return tracks; - } - }; - sinon.spy(tracks[0], "stop"); - - video = { - src: null, - addEventListener: function() { - - }, - removeEventListener: function() { - - }, - play: function() { - - }, - videoWidth: 320, - videoHeight: 480 - }; - sinon.stub(video, "addEventListener", function(event, cb) { - cb(); - }); - sinon.stub(video, "play"); - }); - - afterEach(function() { - window.URL = originalURL; - window.MediaStreamTrack = originalMediaStreamTrack; - }); - - describe('success', function() { - beforeEach(function() { - sinon.stub(navigator, "getUserMedia", function(constraints, success) { - success(stream); - }); - }); - - afterEach(function() { - navigator.getUserMedia.restore(); - }); - describe('request', function () { - it('should request the camera', function (done) { - CameraAccess.request(video, {}, function () { - expect(navigator.getUserMedia.calledOnce).to.equal(true); - expect(video.src).to.deep.equal(stream); - done(); - }); - }); - }); - - describe('release', function () { - it('should release the camera', function (done) { - CameraAccess.request(video, {}, function () { - expect(video.src).to.deep.equal(stream); - CameraAccess.release(); - expect(video.src.getVideoTracks()).to.have.length(1); - expect(video.src.getVideoTracks()[0].stop.calledOnce).to.equal(true); - done(); - }); - }); - }); - }); - - describe('failure', function() { - describe("permission denied", function(){ - before(function() { - sinon.stub(navigator, "getUserMedia", function(constraints, success, failure) { - failure(new Error()); - }); - }); - - after(function() { - navigator.getUserMedia.restore(); - }); - - it('should throw if getUserMedia not available', function(done) { - CameraAccess.request(video, {}, function(err) { - expect(err).to.be.defined; - done(); - }); - }); - }); - - describe("not available", function(){ - var originalGetUserMedia; - - before(function() { - originalGetUserMedia = navigator.getUserMedia; - navigator.getUserMedia = undefined; - }); - - after(function() { - navigator.getUserMedia = originalGetUserMedia; - }); - - it('should throw if getUserMedia not available', function(done) { - CameraAccess.request(video, {}, function(err) { - expect(err).to.be.defined; - done(); - }); - }); - }); - }); -}); diff --git a/spec/cv_utils.spec.js b/spec/cv_utils.spec.js deleted file mode 100644 index d490e9c5..00000000 --- a/spec/cv_utils.spec.js +++ /dev/null @@ -1,145 +0,0 @@ - -define(['cv_utils'], function(CVUtils){ - describe('imageRef', function() { - it('gets the image Reference for a coordinate', function() { - var res = CVUtils.imageRef(1, 2); - expect(res.x).to.equal(1); - expect(res.y).to.equal(2); - expect(res.toVec2()[0]).to.equal(1); - }); - }); - - describe('calculatePatchSize', function() { - it('should not throw an error in case of valid image size', function() { - var expected = {x: 32, y: 32}, - patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480}); - - expect(patchSize).to.be.deep.equal(expected); - }); - - it('should thow an error if image size it not valid', function() { - var expected = {x: 32, y: 32}, - patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480}); - - expect(patchSize).to.be.deep.equal(expected); - }); - }); - - describe('_parseCSSDimensionValues', function() { - it("should convert a percentual value correctly", function() { - var expected = { - value: 10, - unit: "%" - }, - result = CVUtils._parseCSSDimensionValues("10%"); - - expect(result).to.be.deep.equal(expected); - }); - - it("should convert a 0% value correctly", function() { - var expected = { - value: 100, - unit: "%" - }, - result = CVUtils._parseCSSDimensionValues("100%"); - - expect(result).to.be.deep.equal(expected); - }); - - it("should convert a 100% value correctly", function() { - var expected = { - value: 0, - unit: "%" - }, - result = CVUtils._parseCSSDimensionValues("0%"); - - expect(result).to.be.deep.equal(expected); - }); - - it("should convert a pixel value to percentage", function() { - var expected = { - value: 26.3, - unit: "%" - }, - result = CVUtils._parseCSSDimensionValues("26.3px"); - - console.log(result); - expect(result).to.be.deep.equal(expected); - }); - }); - - describe("_dimensionsConverters", function(){ - var context; - - beforeEach(function() { - context = { - width: 640, - height: 480 - }; - }); - - it("should convert a top-value correclty", function() { - var expected = 48, - result = CVUtils._dimensionsConverters.top({value: 10, unit: "%"}, context); - - expect(result).to.be.equal(expected); - }); - - it("should convert a right-value correclty", function() { - var expected = 640 - 128, - result = CVUtils._dimensionsConverters.right({value: 20, unit: "%"}, context); - - expect(result).to.be.equal(expected); - }); - - it("should convert a bottom-value correclty", function() { - var expected = 480 - 77, - result = CVUtils._dimensionsConverters.bottom({value: 16, unit: "%"}, context); - - expect(result).to.be.equal(expected); - }); - - it("should convert a left-value correclty", function() { - var expected = 57, - result = CVUtils._dimensionsConverters.left({value: 9, unit: "%"}, context); - - expect(result).to.be.equal(expected); - }); - }); - - describe("computeImageArea", function() { - it("should calculate an image-area", function() { - var expected = { - sx: 115, - sy: 48, - sw: 429, - sh: 336 - }, - result = CVUtils.computeImageArea(640, 480, { - top: "10%", - right: "15%", - bottom: "20%", - left: "18%" - }); - - expect(result).to.be.deep.equal(expected); - }); - - it("should calculate full image-area", function() { - var expected = { - sx: 0, - sy: 0, - sw: 640, - sh: 480 - }, - result = CVUtils.computeImageArea(640, 480, { - top: "0%", - right: "0%", - bottom: "0%", - left: "0%" - }); - - expect(result).to.be.deep.equal(expected); - }); - }); -}); diff --git a/spec/events.spec.js b/spec/events.spec.js deleted file mode 100644 index 861e4af9..00000000 --- a/spec/events.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -define(['events'], function(Events){ - beforeEach(function() { - Events.unsubscribe(); - }); - describe("subscribe", function() { - - - it("should call one callback for a single event", function() { - var callbackA = sinon.stub(), - callbackB = sinon.stub(); - - Events.subscribe("test", callbackA); - Events.subscribe("test", callbackB); - Events.publish("test"); - - expect(callbackA.calledOnce).to.be.equal(true); - expect(callbackB.calledOnce).to.be.equal(true); - }); - - it("should call one callback twice if published twice", function() { - var callback = sinon.stub(); - - Events.subscribe("test", callback); - - Events.publish("test"); - Events.publish("test"); - - expect(callback.calledTwice).to.be.equal(true); - }); - - it("should call the callback asynchronuously", function(done) { - var test = { - callback: function() { - - } - }; - - sinon.stub(test, "callback", function() { - expect(test.callback.calledOnce).to.be.true; - done(); - }); - Events.subscribe("test", test.callback, true); - Events.publish("test"); - expect(test.callback.called).to.be.false; - }); - }); - - describe("once", function() { - it("should call the callback once, even when published twice", function() { - var callbackA = sinon.stub(), - callbackB = sinon.stub(); - - Events.once("test", callbackA); - Events.subscribe("test", callbackB); - - Events.publish("test"); - Events.publish("test"); - - expect(callbackA.calledOnce).to.be.equal(true); - expect(callbackB.calledTwice).to.be.equal(true); - }); - }); - - describe("unsubscribe", function() { - it("should unsubscribe all callbacks from a single event", function() { - var callbackA = sinon.stub(), - callbackB = sinon.stub(), - callbackC = sinon.stub(); - - Events.subscribe("test", callbackA); - Events.subscribe("test", callbackB); - Events.subscribe("testC", callbackC); - - Events.publish("test"); - - expect(callbackC.called).to.be.equal(false); - expect(callbackA.calledOnce).to.be.equal(true); - expect(callbackB.calledOnce).to.be.equal(true); - - Events.publish("testC"); - - expect(callbackC.calledOnce).to.be.equal(true); - expect(callbackA.calledOnce).to.be.equal(true); - expect(callbackB.calledOnce).to.be.equal(true); - - Events.unsubscribe("test"); - Events.publish("test"); - - expect(callbackC.calledOnce).to.be.equal(true); - expect(callbackA.calledOnce).to.be.equal(true); - expect(callbackB.calledOnce).to.be.equal(true); - }); - - it("should unsubscribe a single callback from a single event", function() { - var callbackA = sinon.stub(), - callbackB = sinon.stub(); - - Events.subscribe("test", callbackA); - Events.subscribe("test", callbackB); - - Events.publish("test"); - - expect(callbackA.calledOnce).to.be.equal(true); - expect(callbackB.calledOnce).to.be.equal(true); - - - Events.unsubscribe("test", callbackB); - Events.publish("test"); - - expect(callbackA.calledTwice).to.be.equal(true); - expect(callbackB.calledOnce).to.be.equal(true); - }); - }); -}); \ No newline at end of file diff --git a/spec/integration.spec.js b/spec/integration.spec.js deleted file mode 100644 index 96f12884..00000000 --- a/spec/integration.spec.js +++ /dev/null @@ -1,248 +0,0 @@ - - -define(['quagga', 'async'], function(Quagga, async) { - describe('decodeSingle', function () { - var baseFolder = "base/test/fixtures/"; - - function generateConfig() { - return { - inputStream: { - size: 640 - }, - locator: { - patchSize: "medium", - halfSample: true - }, - numOfWorkers: 0, - decoder: { - readers: ["ean_reader"] - }, - locate: true, - src: null - }; - } - - this.timeout(10000); - - function _runTestSet(testSet, config) { - var readers = config.decoder.readers.slice(), - format, - folder; - - if (typeof readers[0] === 'string'){ - format = readers[0]; - } else { - format = readers[0].format; - } - - folder = baseFolder + format.split('_').slice(0, -1).join('_') + "/"; - - it('should decode ' + folder + " correctly", function(done) { - async.eachSeries(testSet, function (sample, callback) { - config.src = folder + sample.name; - config.readers = readers; - Quagga.decodeSingle(config, function(result) { - console.log(sample.name); - expect(result.codeResult.code).to.equal(sample.result); - expect(result.codeResult.format).to.equal(sample.format); - callback(); - }); - }, function() { - done(); - }); - }); - } - - describe("EAN", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "3574660239843"}, - {"name": "image-002.jpg", "result": "8032754490297"}, - {"name": "image-003.jpg", "result": "4006209700068"}, - /* {"name": "image-004.jpg", "result": "9002233139084"}, */ - /* {"name": "image-005.jpg", "result": "8004030044005"}, */ - {"name": "image-006.jpg", "result": "4003626011159"}, - {"name": "image-007.jpg", "result": "2111220009686"}, - {"name": "image-008.jpg", "result": "9000275609022"}, - {"name": "image-009.jpg", "result": "9004593978587"}, - {"name": "image-010.jpg", "result": "9002244845578"} - ]; - - testSet.forEach(function(sample) { - sample.format = "ean_13"; - }); - - config.decoder.readers = ['ean_reader']; - _runTestSet(testSet, config); - }); - - describe("Code128", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "0001285112001000040801"}, - // {"name": "image-002.jpg", "result": "FANAVF1461710"}, - // {"name": "image-003.jpg", "result": "673023"}, - // {"name": "image-004.jpg", "result": "010210150301625334"}, - {"name": "image-005.jpg", "result": "419055603900009001012999"}, - {"name": "image-006.jpg", "result": "419055603900009001012999"}, - {"name": "image-007.jpg", "result": "T 000003552345"}, - {"name": "image-008.jpg", "result": "FANAVF1461710"}, - {"name": "image-009.jpg", "result": "0001285112001000040801"}, - {"name": "image-010.jpg", "result": "673023"} - ]; - - testSet.forEach(function(sample) { - sample.format = "code_128"; - }); - - config.decoder.readers = ['code_128_reader']; - _runTestSet(testSet, config); - }); - - describe("Code39", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "B3% $DAD$"}, - {"name": "image-003.jpg", "result": "CODE39"}, - {"name": "image-004.jpg", "result": "QUAGGAJS"}, - /* {"name": "image-005.jpg", "result": "CODE39"}, */ - {"name": "image-006.jpg", "result": "2/4-8/16-32"}, - {"name": "image-007.jpg", "result": "2/4-8/16-32"}, - {"name": "image-008.jpg", "result": "CODE39"}, - {"name": "image-009.jpg", "result": "2/4-8/16-32"}, - {"name": "image-010.jpg", "result": "CODE39"} - ]; - - testSet.forEach(function(sample) { - sample.format = "code_39"; - }); - - config.decoder.readers = ['code_39_reader']; - _runTestSet(testSet, config); - }); - - describe("EAN-8", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "42191605"}, - {"name": "image-002.jpg", "result": "42191605"}, - {"name": "image-003.jpg", "result": "90311208"}, - {"name": "image-004.jpg", "result": "24057257"}, - {"name": "image-005.jpg", "result": "90162602"}, - {"name": "image-006.jpg", "result": "24036153"}, - {"name": "image-007.jpg", "result": "42176817"}, - {"name": "image-008.jpg", "result": "42191605"}, - {"name": "image-009.jpg", "result": "42242215"}, - {"name": "image-010.jpg", "result": "42184799"} - ]; - - testSet.forEach(function(sample) { - sample.format = "ean_8"; - }); - - config.decoder.readers = ['ean_8_reader']; - _runTestSet(testSet, config); - }); - - describe("UPC", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "882428015268"}, - {"name": "image-002.jpg", "result": "882428015268"}, - {"name": "image-003.jpg", "result": "882428015084"}, - {"name": "image-004.jpg", "result": "882428015343"}, - {"name": "image-005.jpg", "result": "882428015343"}, - /* {"name": "image-006.jpg", "result": "882428015046"}, */ - {"name": "image-007.jpg", "result": "882428015084"}, - {"name": "image-008.jpg", "result": "882428015046"}, - {"name": "image-009.jpg", "result": "039047013551"}, - {"name": "image-010.jpg", "result": "039047013551"} - ]; - - testSet.forEach(function(sample) { - sample.format = "upc_a"; - }); - - config.decoder.readers = ['upc_reader']; - _runTestSet(testSet, config); - }); - - describe("UPC-E", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "04965802"}, - {"name": "image-002.jpg", "result": "04965802"}, - {"name": "image-003.jpg", "result": "03897425"}, - {"name": "image-004.jpg", "result": "05096893"}, - {"name": "image-005.jpg", "result": "05096893"}, - {"name": "image-006.jpg", "result": "05096893"}, - {"name": "image-007.jpg", "result": "03897425"}, - {"name": "image-008.jpg", "result": "01264904"}, - /*{"name": "image-009.jpg", "result": "01264904"},*/ - {"name": "image-010.jpg", "result": "01264904"} - ]; - - testSet.forEach(function(sample) { - sample.format = "upc_e"; - }); - - config.decoder.readers = ['upc_e_reader']; - _runTestSet(testSet, config); - }); - - describe("Codabar", function() { - var config = generateConfig(), - testSet = [ - {"name": "image-001.jpg", "result": "A10/53+17-70D"}, - {"name": "image-002.jpg", "result": "B546745735B"}, - {"name": "image-003.jpg", "result": "C$399.95A"}, - {"name": "image-004.jpg", "result": "B546745735B"}, - {"name": "image-005.jpg", "result": "C$399.95A"}, - {"name": "image-006.jpg", "result": "B546745735B"}, - {"name": "image-007.jpg", "result": "C$399.95A"}, - {"name": "image-008.jpg", "result": "A16:9/4:3/3:2D"}, - {"name": "image-009.jpg", "result": "C$399.95A"}, - {"name": "image-010.jpg", "result": "C$399.95A"} - ]; - - testSet.forEach(function(sample) { - sample.format = "codabar"; - }); - - config.decoder.readers = ['codabar_reader']; - _runTestSet(testSet, config); - }); - - describe("I2of5 with localization", function() { - var config = { - inputStream: { - size: 800, - singleChannel: false - }, - locator: { - patchSize: "small", - halfSample: false - }, - numOfWorkers: 0, - decoder: { - readers: ["i2of5_reader"], - }, - locate: true, - src: null - }, testSet = [ - {"name": "image-001.jpg", "result": "2167361334"}, - {"name": "image-002.jpg", "result": "2167361334"}, - {"name": "image-003.jpg", "result": "2167361334"}, - {"name": "image-004.jpg", "result": "2167361334"}, - {"name": "image-005.jpg", "result": "2167361334"} - ]; - - testSet.forEach(function(sample) { - sample.format = "i2of5"; - }); - - _runTestSet(testSet, config); - - }); - }); -}); \ No newline at end of file diff --git a/spec/result_collector.spec.js b/spec/result_collector.spec.js deleted file mode 100644 index dcb927a0..00000000 --- a/spec/result_collector.spec.js +++ /dev/null @@ -1,103 +0,0 @@ - -define(['result_collector', 'image_debug'], function(ResultCollector, ImageDebug) { - var canvasMock, - imageSize, - config; - - beforeEach(function() { - imageSize = {x: 320, y: 240}; - config = { - capture: true, - capacity: 20, - blacklist: [{code: "3574660239843", format: "ean_13"}], - filter: function(codeResult) { - return true; - } - }; - canvasMock = { - getContext: function() { - return {}; - }, - toDataURL: sinon.spy(), - width: 0, - height: 0 - }; - sinon.stub(document, "createElement", function(type) { - if (type === "canvas") { - return canvasMock; - } - }); - }); - - afterEach(function() { - document.createElement.restore(); - }); - - - describe('create', function () { - it("should return a new collector", function() { - ResultCollector.create(config); - expect(document.createElement.calledOnce).to.be.equal(true); - expect(document.createElement.getCall(0).args[0]).to.equal("canvas"); - }); - }); - - describe('addResult', function() { - beforeEach(function() { - sinon.stub(ImageDebug, "drawImage", function() {}); - }); - - afterEach(function() { - ImageDebug.drawImage.restore(); - }); - - it("should not add result if capacity is full", function(){ - config.capacity = 1; - var collector = ResultCollector.create(config); - collector.addResult([], imageSize, {}); - collector.addResult([], imageSize, {}); - collector.addResult([], imageSize, {}); - expect(collector.getResults()).to.have.length(1); - }); - - it("should only add results which match constraints", function(){ - var collector = ResultCollector.create(config), - results; - - collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); - collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"}); - collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"}); - - results = collector.getResults(); - expect(results).to.have.length(2); - - results.forEach(function(result) { - expect(result).not.to.deep.equal(config.blacklist[0]); - }); - }); - - it("should add result if no filter is set", function() { - delete config.filter; - var collector = ResultCollector.create(config); - - collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); - expect(collector.getResults()).to.have.length(1); - }); - - it("should not add results if filter returns false", function() { - config.filter = function(){ return false }; - var collector = ResultCollector.create(config); - - collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); - expect(collector.getResults()).to.have.length(0); - }); - - it("should add result if no blacklist is set", function() { - delete config.blacklist; - var collector = ResultCollector.create(config); - - collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"}); - expect(collector.getResults()).to.have.length(1); - }); - }); -}); \ No newline at end of file diff --git a/src/array_helper.js b/src/array_helper.js index 398830dd..153fdaf1 100644 --- a/src/array_helper.js +++ b/src/array_helper.js @@ -1,86 +1,79 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(function() { - "use strict"; - - return { - init : function(arr, val) { - var l = arr.length; - while (l--) { - arr[l] = val; - } - }, +export default { + init: function(arr, val) { + var l = arr.length; + while (l--) { + arr[l] = val; + } + }, - /** - * Shuffles the content of an array - * @return {Array} the array itself shuffled - */ - shuffle : function(arr) { - var i = arr.length - 1, j, x; - for (i; i >= 0; i--) { - j = Math.floor(Math.random() * i); - x = arr[i]; - arr[i] = arr[j]; - arr[j] = x; - } - return arr; - }, + /** + * Shuffles the content of an array + * @return {Array} the array itself shuffled + */ + shuffle: function(arr) { + var i = arr.length - 1, j, x; + for (i; i >= 0; i--) { + j = Math.floor(Math.random() * i); + x = arr[i]; + arr[i] = arr[j]; + arr[j] = x; + } + return arr; + }, - toPointList : function(arr) { - var i, j, row = [], rows = []; - for ( i = 0; i < arr.length; i++) { - row = []; - for ( j = 0; j < arr[i].length; j++) { - row[j] = arr[i][j]; - } - rows[i] = "[" + row.join(",") + "]"; + toPointList: function(arr) { + var i, j, row = [], rows = []; + for ( i = 0; i < arr.length; i++) { + row = []; + for ( j = 0; j < arr[i].length; j++) { + row[j] = arr[i][j]; } - return "[" + rows.join(",\r\n") + "]"; - }, + rows[i] = "[" + row.join(",") + "]"; + } + return "[" + rows.join(",\r\n") + "]"; + }, - /** - * returns the elements which's score is bigger than the threshold - * @return {Array} the reduced array - */ - threshold : function(arr, threshold, scoreFunc) { - var i, queue = []; - for ( i = 0; i < arr.length; i++) { - if (scoreFunc.apply(arr, [arr[i]]) >= threshold) { - queue.push(arr[i]); - } + /** + * returns the elements which's score is bigger than the threshold + * @return {Array} the reduced array + */ + threshold: function(arr, threshold, scoreFunc) { + var i, queue = []; + for ( i = 0; i < arr.length; i++) { + if (scoreFunc.apply(arr, [arr[i]]) >= threshold) { + queue.push(arr[i]); } - return queue; - }, + } + return queue; + }, - maxIndex : function(arr) { - var i, max = 0; - for ( i = 0; i < arr.length; i++) { - if (arr[i] > arr[max]) { - max = i; - } + maxIndex: function(arr) { + var i, max = 0; + for ( i = 0; i < arr.length; i++) { + if (arr[i] > arr[max]) { + max = i; } - return max; - }, + } + return max; + }, - max : function(arr) { - var i, max = 0; - for ( i = 0; i < arr.length; i++) { - if (arr[i] > max) { - max = arr[i]; - } + max: function(arr) { + var i, max = 0; + for ( i = 0; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; } - return max; - }, + } + return max; + }, - sum: function(arr) { - var length = arr.length, - sum = 0; + sum: function(arr) { + var length = arr.length, + sum = 0; - while(length--) { - sum += arr[length]; - } - return sum; + while (length--) { + sum += arr[length]; } - }; -}); \ No newline at end of file + return sum; + } +}; diff --git a/src/barcode_decoder.js b/src/barcode_decoder.js index 2117d492..54d1e1f4 100644 --- a/src/barcode_decoder.js +++ b/src/barcode_decoder.js @@ -1,305 +1,286 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +import Bresenham from './bresenham'; +import ImageDebug from './image_debug'; +import Code128Reader from './code_128_reader'; +import EANReader from './ean_reader'; +import Code39Reader from './code_39_reader'; +import Code39VINReader from './code_39_vin_reader'; +import CodabarReader from './codabar_reader'; +import UPCReader from './upc_reader'; +import EAN8Reader from './ean_8_reader'; +import UPCEReader from './upc_e_reader'; +import I2of5Reader from './i2of5_reader'; -define([ - "bresenham", - "image_debug", - 'code_128_reader', - 'ean_reader', - 'code_39_reader', - 'code_39_vin_reader', - 'codabar_reader', - 'upc_reader', - 'ean_8_reader', - 'upc_e_reader', - 'i2of5_reader' -], function( - Bresenham, - ImageDebug, - Code128Reader, - EANReader, - Code39Reader, - Code39VINReader, - CodabarReader, - UPCReader, - EAN8Reader, - UPCEReader, - I2of5Reader) { - "use strict"; - - var readers = { - code_128_reader: Code128Reader, - ean_reader: EANReader, - ean_8_reader: EAN8Reader, - code_39_reader: Code39Reader, - code_39_vin_reader: Code39VINReader, - codabar_reader: CodabarReader, - upc_reader: UPCReader, - upc_e_reader: UPCEReader, - i2of5_reader: I2of5Reader - }; - var BarcodeDecoder = { - create : function(config, inputImageWrapper) { - var _canvas = { - ctx : { - frequency : null, - pattern : null, - overlay : null - }, - dom : { - frequency : null, - pattern : null, - overlay : null - } +const READERS = { + code_128_reader: Code128Reader, + ean_reader: EANReader, + ean_8_reader: EAN8Reader, + code_39_reader: Code39Reader, + code_39_vin_reader: Code39VINReader, + codabar_reader: CodabarReader, + upc_reader: UPCReader, + upc_e_reader: UPCEReader, + i2of5_reader: I2of5Reader +}; +export default { + create: function(config, inputImageWrapper) { + var _canvas = { + ctx: { + frequency: null, + pattern: null, + overlay: null }, - _barcodeReaders = []; + dom: { + frequency: null, + pattern: null, + overlay: null + } + }, + _barcodeReaders = []; - initCanvas(); - initReaders(); - initConfig(); + initCanvas(); + initReaders(); + initConfig(); - function initCanvas() { - if (typeof document !== 'undefined') { - var $debug = document.querySelector("#debug.detection"); - _canvas.dom.frequency = document.querySelector("canvas.frequency"); - if (!_canvas.dom.frequency) { - _canvas.dom.frequency = document.createElement("canvas"); - _canvas.dom.frequency.className = "frequency"; - if($debug) { - $debug.appendChild(_canvas.dom.frequency); - } + function initCanvas() { + if (typeof document !== 'undefined') { + var $debug = document.querySelector("#debug.detection"); + _canvas.dom.frequency = document.querySelector("canvas.frequency"); + if (!_canvas.dom.frequency) { + _canvas.dom.frequency = document.createElement("canvas"); + _canvas.dom.frequency.className = "frequency"; + if ($debug) { + $debug.appendChild(_canvas.dom.frequency); } - _canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d"); + } + _canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d"); - _canvas.dom.pattern = document.querySelector("canvas.patternBuffer"); - if (!_canvas.dom.pattern) { - _canvas.dom.pattern = document.createElement("canvas"); - _canvas.dom.pattern.className = "patternBuffer"; - if($debug) { - $debug.appendChild(_canvas.dom.pattern); - } + _canvas.dom.pattern = document.querySelector("canvas.patternBuffer"); + if (!_canvas.dom.pattern) { + _canvas.dom.pattern = document.createElement("canvas"); + _canvas.dom.pattern.className = "patternBuffer"; + if ($debug) { + $debug.appendChild(_canvas.dom.pattern); } - _canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d"); + } + _canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d"); - _canvas.dom.overlay = document.querySelector("canvas.drawingBuffer"); - if (_canvas.dom.overlay) { - _canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d"); - } + _canvas.dom.overlay = document.querySelector("canvas.drawingBuffer"); + if (_canvas.dom.overlay) { + _canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d"); } } + } - function initReaders() { - config.readers.forEach(function(readerConfig) { - var reader, - config = {}; + function initReaders() { + config.readers.forEach(function(readerConfig) { + var reader, + configuration = {}; - if (typeof readerConfig === 'object') { - reader = readerConfig.format; - config = readerConfig.config; - } else if (typeof readerConfig === 'string') { - reader = readerConfig; - } - _barcodeReaders.push(new readers[reader](config)); - }); - console.log("Registered Readers: " + _barcodeReaders - .map(function(reader) {return JSON.stringify({format: reader.FORMAT, config: reader.config});}) - .join(', ')); - } + if (typeof readerConfig === 'object') { + reader = readerConfig.format; + configuration = readerConfig.config; + } else if (typeof readerConfig === 'string') { + reader = readerConfig; + } + console.log("Before registering reader: ", reader); + _barcodeReaders.push(new READERS[reader](configuration)); + }); + console.log("Registered Readers: " + _barcodeReaders + .map((reader) => JSON.stringify({format: reader.FORMAT, config: reader.config})) + .join(', ')); + } - function initConfig() { - if (typeof document !== 'undefined') { - var i, - vis = [{ - node : _canvas.dom.frequency, - prop : config.showFrequency - }, { - node : _canvas.dom.pattern, - prop : config.showPattern - }]; + function initConfig() { + if (typeof document !== 'undefined') { + var i, + vis = [{ + node: _canvas.dom.frequency, + prop: config.showFrequency + }, { + node: _canvas.dom.pattern, + prop: config.showPattern + }]; - for (i = 0; i < vis.length; i++) { - if (vis[i].prop === true) { - vis[i].node.style.display = "block"; - } else { - vis[i].node.style.display = "none"; - } + for (i = 0; i < vis.length; i++) { + if (vis[i].prop === true) { + vis[i].node.style.display = "block"; + } else { + vis[i].node.style.display = "none"; } } } + } - /** - * extend the line on both ends - * @param {Array} line - * @param {Number} angle - */ - function getExtendedLine(line, angle, ext) { - function extendLine(amount) { - var extension = { - y : amount * Math.sin(angle), - x : amount * Math.cos(angle) - }; - - line[0].y -= extension.y; - line[0].x -= extension.x; - line[1].y += extension.y; - line[1].x += extension.x; - } + /** + * extend the line on both ends + * @param {Array} line + * @param {Number} angle + */ + function getExtendedLine(line, angle, ext) { + function extendLine(amount) { + var extension = { + y: amount * Math.sin(angle), + x: amount * Math.cos(angle) + }; - // check if inside image - extendLine(ext); - while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0))) { - ext -= Math.ceil(ext/2); - extendLine(-ext); - } - return line; + line[0].y -= extension.y; + line[0].x -= extension.x; + line[1].y += extension.y; + line[1].x += extension.x; } - function getLine(box) { - return [{ - x : (box[1][0] - box[0][0]) / 2 + box[0][0], - y : (box[1][1] - box[0][1]) / 2 + box[0][1] - }, { - x : (box[3][0] - box[2][0]) / 2 + box[2][0], - y : (box[3][1] - box[2][1]) / 2 + box[2][1] - }]; + // check if inside image + extendLine(ext); + while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0) + || !inputImageWrapper.inImageWithBorder(line[1], 0))) { + ext -= Math.ceil(ext / 2); + extendLine(-ext); } + return line; + } - function tryDecode(line) { - var result = null, - i, - barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, line[0], line[1]); + function getLine(box) { + return [{ + x: (box[1][0] - box[0][0]) / 2 + box[0][0], + y: (box[1][1] - box[0][1]) / 2 + box[0][1] + }, { + x: (box[3][0] - box[2][0]) / 2 + box[2][0], + y: (box[3][1] - box[2][1]) / 2 + box[2][1] + }]; + } - if (config.showFrequency) { - ImageDebug.drawPath(line, {x: 'x', y: 'y'}, _canvas.ctx.overlay, {color: 'red', lineWidth: 3}); - Bresenham.debug.printFrequency(barcodeLine.line, _canvas.dom.frequency); - } - Bresenham.toBinaryLine(barcodeLine); - if (config.showPattern) { - Bresenham.debug.printPattern(barcodeLine.line, _canvas.dom.pattern); - } - - for ( i = 0; i < _barcodeReaders.length && result === null; i++) { - result = _barcodeReaders[i].decodePattern(barcodeLine.line); - } - if(result === null){ - return null; - } - return { - codeResult: result, - barcodeLine: barcodeLine - }; + function tryDecode(line) { + var result = null, + i, + barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, line[0], line[1]); + if (config.showFrequency) { + ImageDebug.drawPath(line, {x: 'x', y: 'y'}, _canvas.ctx.overlay, {color: 'red', lineWidth: 3}); + Bresenham.debug.printFrequency(barcodeLine.line, _canvas.dom.frequency); + } + Bresenham.toBinaryLine(barcodeLine); + if (config.showPattern) { + Bresenham.debug.printPattern(barcodeLine.line, _canvas.dom.pattern); } - /** - * This method slices the given area apart and tries to detect a barcode-pattern - * for each slice. It returns the decoded barcode, or null if nothing was found - * @param {Array} box - * @param {Array} line - * @param {Number} lineAngle - */ - function tryDecodeBruteForce(box, line, lineAngle) { - var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)), - i, - slices = 16, - result = null, - dir, - extension, - xdir = Math.sin(lineAngle), - ydir = Math.cos(lineAngle); + for ( i = 0; i < _barcodeReaders.length && result === null; i++) { + result = _barcodeReaders[i].decodePattern(barcodeLine.line); + } + if (result === null){ + return null; + } + return { + codeResult: result, + barcodeLine: barcodeLine + }; + } - for ( i = 1; i < slices && result === null; i++) { - // move line perpendicular to angle - dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1); - extension = { - y : dir * xdir, - x : dir * ydir - }; - line[0].y += extension.x; - line[0].x -= extension.y; - line[1].y += extension.x; - line[1].x -= extension.y; + /** + * This method slices the given area apart and tries to detect a barcode-pattern + * for each slice. It returns the decoded barcode, or null if nothing was found + * @param {Array} box + * @param {Array} line + * @param {Number} lineAngle + */ + function tryDecodeBruteForce(box, line, lineAngle) { + var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)), + i, + slices = 16, + result = null, + dir, + extension, + xdir = Math.sin(lineAngle), + ydir = Math.cos(lineAngle); - result = tryDecode(line); - } - return result; - } + for ( i = 1; i < slices && result === null; i++) { + // move line perpendicular to angle + dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1); + extension = { + y: dir * xdir, + x: dir * ydir + }; + line[0].y += extension.x; + line[0].x -= extension.y; + line[1].y += extension.x; + line[1].x -= extension.y; - function getLineLength(line) { - return Math.sqrt( - Math.pow(Math.abs(line[1].y - line[0].y), 2) + - Math.pow(Math.abs(line[1].x - line[0].x), 2)); + result = tryDecode(line); } + return result; + } - /** - * With the help of the configured readers (Code128 or EAN) this function tries to detect a - * valid barcode pattern within the given area. - * @param {Object} box The area to search in - * @returns {Object} the result {codeResult, line, angle, pattern, threshold} - */ - function decodeFromBoundingBox(box) { - var line, - lineAngle, - ctx = _canvas.ctx.overlay, - result, - lineLength; + function getLineLength(line) { + return Math.sqrt( + Math.pow(Math.abs(line[1].y - line[0].y), 2) + + Math.pow(Math.abs(line[1].x - line[0].x), 2)); + } - if (config.drawBoundingBox && ctx) { - ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2}); - } + /** + * With the help of the configured readers (Code128 or EAN) this function tries to detect a + * valid barcode pattern within the given area. + * @param {Object} box The area to search in + * @returns {Object} the result {codeResult, line, angle, pattern, threshold} + */ + function decodeFromBoundingBox(box) { + var line, + lineAngle, + ctx = _canvas.ctx.overlay, + result, + lineLength; - line = getLine(box); - lineLength = getLineLength(line); - lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); - line = getExtendedLine(line, lineAngle, Math.floor(lineLength*0.1)); - if(line === null){ - return null; - } + if (config.drawBoundingBox && ctx) { + ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2}); + } - result = tryDecode(line); - if(result === null) { - result = tryDecodeBruteForce(box, line, lineAngle); - } + line = getLine(box); + lineLength = getLineLength(line); + lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x); + line = getExtendedLine(line, lineAngle, Math.floor(lineLength * 0.1)); + if (line === null){ + return null; + } - if(result === null) { - return null; - } + result = tryDecode(line); + if (result === null) { + result = tryDecodeBruteForce(box, line, lineAngle); + } - if (result && config.drawScanline && ctx) { - ImageDebug.drawPath(line, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 3}); - } + if (result === null) { + return null; + } - return { - codeResult : result.codeResult, - line : line, - angle : lineAngle, - pattern : result.barcodeLine.line, - threshold : result.barcodeLine.threshold - }; + if (result && config.drawScanline && ctx) { + ImageDebug.drawPath(line, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 3}); } return { - decodeFromBoundingBox : function(box) { - return decodeFromBoundingBox(box); - }, - decodeFromBoundingBoxes : function(boxes) { - var i, result; - for ( i = 0; i < boxes.length; i++) { - result = decodeFromBoundingBox(boxes[i]); - if (result && result.codeResult) { - result.box = boxes[i]; - return result; - } - } - }, - setReaders: function(readers) { - config.readers = readers; - _barcodeReaders.length = 0; - initReaders(); - } + codeResult: result.codeResult, + line: line, + angle: lineAngle, + pattern: result.barcodeLine.line, + threshold: result.barcodeLine.threshold }; } - }; - return (BarcodeDecoder); -}); + return { + decodeFromBoundingBox: function(box) { + return decodeFromBoundingBox(box); + }, + decodeFromBoundingBoxes: function(boxes) { + var i, result; + for ( i = 0; i < boxes.length; i++) { + result = decodeFromBoundingBox(boxes[i]); + if (result && result.codeResult) { + result.box = boxes[i]; + return result; + } + } + }, + setReaders: function(readers) { + config.readers = readers; + _barcodeReaders.length = 0; + initReaders(); + } + }; + } +}; diff --git a/src/barcode_locator.js b/src/barcode_locator.js index 96a5628c..6fece952 100644 --- a/src/barcode_locator.js +++ b/src/barcode_locator.js @@ -1,561 +1,588 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define("barcode_locator", ["image_wrapper", "cv_utils", "rasterizer", "tracer", "skeletonizer", "array_helper", "image_debug", "gl-matrix"], -function(ImageWrapper, CVUtils, Rasterizer, Tracer, skeletonizer, ArrayHelper, ImageDebug, glMatrix) { - - var _config, - _currentImageWrapper, - _skelImageWrapper, - _subImageWrapper, - _labelImageWrapper, - _patchGrid, - _patchLabelGrid, - _imageToPatchGrid, - _binaryImageWrapper, - _patchSize, - _canvasContainer = { - ctx : { - binary : null - }, - dom : { - binary : null - } +import ImageWrapper from './image_wrapper'; +import CVUtils from './cv_utils'; +import Rasterizer from './rasterizer'; +import Tracer from './tracer'; +import skeletonizer from './skeletonizer'; +import ArrayHelper from './array_helper'; +import ImageDebug from './image_debug'; +import glMatrix from 'gl-matrix'; + +var _config, + _currentImageWrapper, + _skelImageWrapper, + _subImageWrapper, + _labelImageWrapper, + _patchGrid, + _patchLabelGrid, + _imageToPatchGrid, + _binaryImageWrapper, + _patchSize, + _canvasContainer = { + ctx: { + binary: null }, - _numPatches = {x: 0, y: 0}, - _inputImageWrapper, - _skeletonizer, - vec2 = glMatrix.vec2, - mat2 = glMatrix.mat2, - self = this; - - function initBuffers() { - var skeletonImageData; - - if (_config.halfSample) { - _currentImageWrapper = new ImageWrapper({ - x : _inputImageWrapper.size.x / 2 | 0, - y : _inputImageWrapper.size.y / 2 | 0 - }); - } else { - _currentImageWrapper = _inputImageWrapper; + dom: { + binary: null } + }, + _numPatches = {x: 0, y: 0}, + _inputImageWrapper, + _skeletonizer, + vec2 = glMatrix.vec2, + mat2 = glMatrix.mat2; + +function initBuffers() { + var skeletonImageData; + + if (_config.halfSample) { + _currentImageWrapper = new ImageWrapper({ + x: _inputImageWrapper.size.x / 2 | 0, + y: _inputImageWrapper.size.y / 2 | 0 + }); + } else { + _currentImageWrapper = _inputImageWrapper; + } - _patchSize = CVUtils.calculatePatchSize(_config.patchSize, _currentImageWrapper.size); + _patchSize = CVUtils.calculatePatchSize(_config.patchSize, _currentImageWrapper.size); - _numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0; - _numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0; + _numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0; + _numPatches.y = _currentImageWrapper.size.y / _patchSize.y | 0; - _binaryImageWrapper = new ImageWrapper(_currentImageWrapper.size, undefined, Uint8Array, false); + _binaryImageWrapper = new ImageWrapper(_currentImageWrapper.size, undefined, Uint8Array, false); - _labelImageWrapper = new ImageWrapper(_patchSize, undefined, Array, true); + _labelImageWrapper = new ImageWrapper(_patchSize, undefined, Array, true); - skeletonImageData = new ArrayBuffer(64*1024); - _subImageWrapper = new ImageWrapper(_patchSize, new Uint8Array(skeletonImageData, 0, _patchSize.x * _patchSize.y)); - _skelImageWrapper = new ImageWrapper(_patchSize, new Uint8Array(skeletonImageData, _patchSize.x * _patchSize.y * 3, _patchSize.x * _patchSize.y), undefined, true); - _skeletonizer = skeletonizer(self, { - size : _patchSize.x - }, skeletonImageData); + skeletonImageData = new ArrayBuffer(64 * 1024); + _subImageWrapper = new ImageWrapper(_patchSize, + new Uint8Array(skeletonImageData, 0, _patchSize.x * _patchSize.y)); + _skelImageWrapper = new ImageWrapper(_patchSize, + new Uint8Array(skeletonImageData, _patchSize.x * _patchSize.y * 3, _patchSize.x * _patchSize.y), + undefined, true); + _skeletonizer = skeletonizer((typeof window !== 'undefined') ? window : (typeof self !== 'undefined') ? self : global, { + size: _patchSize.x + }, skeletonImageData); - _imageToPatchGrid = new ImageWrapper({ - x : (_currentImageWrapper.size.x / _subImageWrapper.size.x) | 0, - y : (_currentImageWrapper.size.y / _subImageWrapper.size.y) | 0 - }, undefined, Array, true); - _patchGrid = new ImageWrapper(_imageToPatchGrid.size, undefined, undefined, true); - _patchLabelGrid = new ImageWrapper(_imageToPatchGrid.size, undefined, Int32Array, true); - } + _imageToPatchGrid = new ImageWrapper({ + x: (_currentImageWrapper.size.x / _subImageWrapper.size.x) | 0, + y: (_currentImageWrapper.size.y / _subImageWrapper.size.y) | 0 + }, undefined, Array, true); + _patchGrid = new ImageWrapper(_imageToPatchGrid.size, undefined, undefined, true); + _patchLabelGrid = new ImageWrapper(_imageToPatchGrid.size, undefined, Int32Array, true); +} - function initCanvas() { - if (_config.useWorker || typeof document === 'undefined') { - return; - } - _canvasContainer.dom.binary = document.createElement("canvas"); - _canvasContainer.dom.binary.className = "binaryBuffer"; - if (_config.showCanvas === true) { - document.querySelector("#debug").appendChild(_canvasContainer.dom.binary); +function initCanvas() { + if (_config.useWorker || typeof document === 'undefined') { + return; + } + _canvasContainer.dom.binary = document.createElement("canvas"); + _canvasContainer.dom.binary.className = "binaryBuffer"; + if (_config.showCanvas === true) { + document.querySelector("#debug").appendChild(_canvasContainer.dom.binary); + } + _canvasContainer.ctx.binary = _canvasContainer.dom.binary.getContext("2d"); + _canvasContainer.dom.binary.width = _binaryImageWrapper.size.x; + _canvasContainer.dom.binary.height = _binaryImageWrapper.size.y; +} + +/** + * Creates a bounding box which encloses all the given patches + * @returns {Array} The minimal bounding box + */ +function boxFromPatches(patches) { + var overAvg, + i, + j, + patch, + transMat, + minx = + _binaryImageWrapper.size.x, + miny = _binaryImageWrapper.size.y, + maxx = -_binaryImageWrapper.size.x, + maxy = -_binaryImageWrapper.size.y, + box, + scale; + + // draw all patches which are to be taken into consideration + overAvg = 0; + for ( i = 0; i < patches.length; i++) { + patch = patches[i]; + overAvg += patch.rad; + if (_config.showPatches) { + ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "red"}); } - _canvasContainer.ctx.binary = _canvasContainer.dom.binary.getContext("2d"); - _canvasContainer.dom.binary.width = _binaryImageWrapper.size.x; - _canvasContainer.dom.binary.height = _binaryImageWrapper.size.y; } - /** - * Creates a bounding box which encloses all the given patches - * @returns {Array} The minimal bounding box - */ - function boxFromPatches(patches) { - var overAvg, i, j, patch, transMat, minx = _binaryImageWrapper.size.x, miny = _binaryImageWrapper.size.y, maxx = -_binaryImageWrapper.size.x, maxy = -_binaryImageWrapper.size.y, box, scale; - - // draw all patches which are to be taken into consideration - overAvg = 0; - for ( i = 0; i < patches.length; i++) { - patch = patches[i]; - overAvg += patch.rad; - if (_config.showPatches) { - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "red"}); - } - } + overAvg /= patches.length; + overAvg = (overAvg * 180 / Math.PI + 90) % 180 - 90; + if (overAvg < 0) { + overAvg += 180; + } + + overAvg = (180 - overAvg) * Math.PI / 180; + transMat = mat2.clone([Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]); - overAvg /= patches.length; - overAvg = (overAvg * 180 / Math.PI + 90) % 180 - 90; - if (overAvg < 0) { - overAvg += 180; + // iterate over patches and rotate by angle + for ( i = 0; i < patches.length; i++) { + patch = patches[i]; + for ( j = 0; j < 4; j++) { + vec2.transformMat2(patch.box[j], patch.box[j], transMat); } - overAvg = (180 - overAvg) * Math.PI / 180; - transMat = mat2.clone([Math.cos(overAvg), Math.sin(overAvg), -Math.sin(overAvg), Math.cos(overAvg)]); + if (_config.boxFromPatches.showTransformed) { + ImageDebug.drawPath(patch.box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#99ff00', lineWidth: 2}); + } + } - // iterate over patches and rotate by angle - for ( i = 0; i < patches.length; i++) { - patch = patches[i]; - for ( j = 0; j < 4; j++) { - vec2.transformMat2(patch.box[j], patch.box[j], transMat); + // find bounding box + for ( i = 0; i < patches.length; i++) { + patch = patches[i]; + for ( j = 0; j < 4; j++) { + if (patch.box[j][0] < minx) { + minx = patch.box[j][0]; } - - if (_config.boxFromPatches.showTransformed) { - ImageDebug.drawPath(patch.box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#99ff00', lineWidth: 2}); + if (patch.box[j][0] > maxx) { + maxx = patch.box[j][0]; } - } - - // find bounding box - for ( i = 0; i < patches.length; i++) { - patch = patches[i]; - for ( j = 0; j < 4; j++) { - if (patch.box[j][0] < minx) { - minx = patch.box[j][0]; - } - if (patch.box[j][0] > maxx) { - maxx = patch.box[j][0]; - } - if (patch.box[j][1] < miny) { - miny = patch.box[j][1]; - } - if (patch.box[j][1] > maxy) { - maxy = patch.box[j][1]; - } + if (patch.box[j][1] < miny) { + miny = patch.box[j][1]; + } + if (patch.box[j][1] > maxy) { + maxy = patch.box[j][1]; } } + } - box = [[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy]]; + box = [[minx, miny], [maxx, miny], [maxx, maxy], [minx, maxy]]; - if (_config.boxFromPatches.showTransformedBox) { - ImageDebug.drawPath(box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#ff0000', lineWidth: 2}); - } + if (_config.boxFromPatches.showTransformedBox) { + ImageDebug.drawPath(box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#ff0000', lineWidth: 2}); + } - scale = _config.halfSample ? 2 : 1; - // reverse rotation; - transMat = mat2.invert(transMat, transMat); - for ( j = 0; j < 4; j++) { - vec2.transformMat2(box[j], box[j], transMat); - } + scale = _config.halfSample ? 2 : 1; + // reverse rotation; + transMat = mat2.invert(transMat, transMat); + for ( j = 0; j < 4; j++) { + vec2.transformMat2(box[j], box[j], transMat); + } - if (_config.boxFromPatches.showBB) { - ImageDebug.drawPath(box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#ff0000', lineWidth: 2}); - } - - for ( j = 0; j < 4; j++) { - vec2.scale(box[j], box[j], scale); - } + if (_config.boxFromPatches.showBB) { + ImageDebug.drawPath(box, {x: 0, y: 1}, _canvasContainer.ctx.binary, {color: '#ff0000', lineWidth: 2}); + } - return box; + for ( j = 0; j < 4; j++) { + vec2.scale(box[j], box[j], scale); } - /** - * Creates a binary image of the current image - */ - function binarizeImage() { - CVUtils.otsuThreshold(_currentImageWrapper, _binaryImageWrapper); - _binaryImageWrapper.zeroBorder(); - if (_config.showCanvas) { - _binaryImageWrapper.show(_canvasContainer.dom.binary, 255); - } + return box; +} + +/** + * Creates a binary image of the current image + */ +function binarizeImage() { + CVUtils.otsuThreshold(_currentImageWrapper, _binaryImageWrapper); + _binaryImageWrapper.zeroBorder(); + if (_config.showCanvas) { + _binaryImageWrapper.show(_canvasContainer.dom.binary, 255); } - - /** - * Iterate over the entire image - * extract patches - */ - function findPatches() { - var i, - j, - x, - y, - moments, - patchesFound = [], - rasterizer, - rasterResult, - patch; - for ( i = 0; i < _numPatches.x; i++) { - for ( j = 0; j < _numPatches.y; j++) { - - x = _subImageWrapper.size.x * i; - y = _subImageWrapper.size.y * j; - - // seperate parts - skeletonize(x, y); - - // Rasterize, find individual bars - _skelImageWrapper.zeroBorder(); - ArrayHelper.init(_labelImageWrapper.data, 0); - rasterizer = Rasterizer.create(_skelImageWrapper, _labelImageWrapper); - rasterResult = rasterizer.rasterize(0); - - if (_config.showLabels) { - _labelImageWrapper.overlay(_canvasContainer.dom.binary, Math.floor(360 / rasterResult.count), {x : x, y : y}); - } +} + +/** + * Iterate over the entire image + * extract patches + */ +function findPatches() { + var i, + j, + x, + y, + moments, + patchesFound = [], + rasterizer, + rasterResult, + patch; + for (i = 0; i < _numPatches.x; i++) { + for (j = 0; j < _numPatches.y; j++) { + x = _subImageWrapper.size.x * i; + y = _subImageWrapper.size.y * j; + + // seperate parts + skeletonize(x, y); + + // Rasterize, find individual bars + _skelImageWrapper.zeroBorder(); + ArrayHelper.init(_labelImageWrapper.data, 0); + rasterizer = Rasterizer.create(_skelImageWrapper, _labelImageWrapper); + rasterResult = rasterizer.rasterize(0); + + if (_config.showLabels) { + _labelImageWrapper.overlay(_canvasContainer.dom.binary, Math.floor(360 / rasterResult.count), + {x: x, y: y}); + } - // calculate moments from the skeletonized patch - moments = _labelImageWrapper.moments(rasterResult.count); + // calculate moments from the skeletonized patch + moments = _labelImageWrapper.moments(rasterResult.count); - // extract eligible patches - patchesFound = patchesFound.concat(describePatch(moments, [i, j], x, y)); - } + // extract eligible patches + patchesFound = patchesFound.concat(describePatch(moments, [i, j], x, y)); } - - if (_config.showFoundPatches) { - for ( i = 0; i < patchesFound.length; i++) { - patch = patchesFound[i]; - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "#99ff00", lineWidth: 2}); - } + } + + if (_config.showFoundPatches) { + for ( i = 0; i < patchesFound.length; i++) { + patch = patchesFound[i]; + ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, + {color: "#99ff00", lineWidth: 2}); } - - return patchesFound; } - - /** - * Finds those connected areas which contain at least 6 patches - * and returns them ordered DESC by the number of contained patches - * @param {Number} maxLabel - */ - function findBiggestConnectedAreas(maxLabel){ - var i, - sum, - labelHist = [], - topLabels = []; - - for ( i = 0; i < maxLabel; i++) { - labelHist.push(0); + + return patchesFound; +} + +/** + * Finds those connected areas which contain at least 6 patches + * and returns them ordered DESC by the number of contained patches + * @param {Number} maxLabel + */ +function findBiggestConnectedAreas(maxLabel){ + var i, + sum, + labelHist = [], + topLabels = []; + + for ( i = 0; i < maxLabel; i++) { + labelHist.push(0); + } + sum = _patchLabelGrid.data.length; + while (sum--) { + if (_patchLabelGrid.data[sum] > 0) { + labelHist[_patchLabelGrid.data[sum] - 1]++; } + } + + labelHist = labelHist.map(function(val, idx) { + return { + val: val, + label: idx + 1 + }; + }); + + labelHist.sort(function(a, b) { + return b.val - a.val; + }); + + // extract top areas with at least 6 patches present + topLabels = labelHist.filter(function(el) { + return el.val >= 5; + }); + + return topLabels; +} + +/** + * + */ +function findBoxes(topLabels, maxLabel) { + var i, + j, + sum, + patches = [], + patch, + box, + boxes = [], + hsv = [0, 1, 1], + rgb = [0, 0, 0]; + + for ( i = 0; i < topLabels.length; i++) { sum = _patchLabelGrid.data.length; + patches.length = 0; while (sum--) { - if (_patchLabelGrid.data[sum] > 0) { - labelHist[_patchLabelGrid.data[sum] - 1]++; + if (_patchLabelGrid.data[sum] === topLabels[i].label) { + patch = _imageToPatchGrid.data[sum]; + patches.push(patch); } } - - labelHist = labelHist.map(function(val, idx) { - return { - val : val, - label : idx + 1 - }; - }); - - labelHist.sort(function(a, b) { - return b.val - a.val; - }); - - // extract top areas with at least 6 patches present - topLabels = labelHist.filter(function(el) { - return el.val >= 5; - }); - - return topLabels; - } - - /** - * - */ - function findBoxes(topLabels, maxLabel) { - var i, - j, - sum, - patches = [], - patch, - box, - boxes = [], - hsv = [0, 1, 1], - rgb = [0, 0, 0]; - - for ( i = 0; i < topLabels.length; i++) { - sum = _patchLabelGrid.data.length; - patches.length = 0; - while (sum--) { - if (_patchLabelGrid.data[sum] === topLabels[i].label) { - patch = _imageToPatchGrid.data[sum]; - patches.push(patch); - } - } - box = boxFromPatches(patches); - if (box) { - boxes.push(box); - - // draw patch-labels if requested - if (_config.showRemainingPatchLabels) { - for ( j = 0; j < patches.length; j++) { - patch = patches[j]; - hsv[0] = (topLabels[i].label / (maxLabel + 1)) * 360; - CVUtils.hsv2rgb(hsv, rgb); - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "rgb(" + rgb.join(",") + ")", lineWidth: 2}); - } + box = boxFromPatches(patches); + if (box) { + boxes.push(box); + + // draw patch-labels if requested + if (_config.showRemainingPatchLabels) { + for ( j = 0; j < patches.length; j++) { + patch = patches[j]; + hsv[0] = (topLabels[i].label / (maxLabel + 1)) * 360; + CVUtils.hsv2rgb(hsv, rgb); + ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, + {color: "rgb(" + rgb.join(",") + ")", lineWidth: 2}); } } } - return boxes; } - - /** - * Find similar moments (via cluster) - * @param {Object} moments - */ - function similarMoments(moments) { - var clusters = CVUtils.cluster(moments, 0.90); - var topCluster = CVUtils.topGeneric(clusters, 1, function(e) { - return e.getPoints().length; - }); - var points = [], result = []; - if (topCluster.length === 1) { - points = topCluster[0].item.getPoints(); - for (var i = 0; i < points.length; i++) { - result.push(points[i].point); - } + return boxes; +} + +/** + * Find similar moments (via cluster) + * @param {Object} moments + */ +function similarMoments(moments) { + var clusters = CVUtils.cluster(moments, 0.90); + var topCluster = CVUtils.topGeneric(clusters, 1, function(e) { + return e.getPoints().length; + }); + var points = [], result = []; + if (topCluster.length === 1) { + points = topCluster[0].item.getPoints(); + for (var i = 0; i < points.length; i++) { + result.push(points[i].point); } - return result; } + return result; +} - function skeletonize(x, y) { - _binaryImageWrapper.subImageAsCopy(_subImageWrapper, CVUtils.imageRef(x, y)); - _skeletonizer.skeletonize(); - - // Show skeleton if requested - if (_config.showSkeleton) { - _skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, CVUtils.imageRef(x, y)); - } - } +function skeletonize(x, y) { + _binaryImageWrapper.subImageAsCopy(_subImageWrapper, CVUtils.imageRef(x, y)); + _skeletonizer.skeletonize(); - /** - * Extracts and describes those patches which seem to contain a barcode pattern - * @param {Array} moments - * @param {Object} patchPos, - * @param {Number} x - * @param {Number} y - * @returns {Array} list of patches - */ - function describePatch(moments, patchPos, x, y) { - var k, - avg, - sum = 0, - eligibleMoments = [], - matchingMoments, - patch, - patchesFound = [], - minComponentWeight = Math.ceil(_patchSize.x/3); - - if (moments.length >= 2) { - // only collect moments which's area covers at least minComponentWeight pixels. - for ( k = 0; k < moments.length; k++) { - if (moments[k].m00 > minComponentWeight) { - eligibleMoments.push(moments[k]); - } + // Show skeleton if requested + if (_config.showSkeleton) { + _skelImageWrapper.overlay(_canvasContainer.dom.binary, 360, CVUtils.imageRef(x, y)); + } +} + +/** + * Extracts and describes those patches which seem to contain a barcode pattern + * @param {Array} moments + * @param {Object} patchPos, + * @param {Number} x + * @param {Number} y + * @returns {Array} list of patches + */ +function describePatch(moments, patchPos, x, y) { + var k, + avg, + eligibleMoments = [], + matchingMoments, + patch, + patchesFound = [], + minComponentWeight = Math.ceil(_patchSize.x / 3); + + if (moments.length >= 2) { + // only collect moments which's area covers at least minComponentWeight pixels. + for ( k = 0; k < moments.length; k++) { + if (moments[k].m00 > minComponentWeight) { + eligibleMoments.push(moments[k]); } + } - // if at least 2 moments are found which have at least minComponentWeights covered - if (eligibleMoments.length >= 2) { - sum = eligibleMoments.length; - matchingMoments = similarMoments(eligibleMoments); - avg = 0; - // determine the similarity of the moments - for ( k = 0; k < matchingMoments.length; k++) { - avg += matchingMoments[k].rad; - } + // if at least 2 moments are found which have at least minComponentWeights covered + if (eligibleMoments.length >= 2) { + matchingMoments = similarMoments(eligibleMoments); + avg = 0; + // determine the similarity of the moments + for ( k = 0; k < matchingMoments.length; k++) { + avg += matchingMoments[k].rad; + } - // Only two of the moments are allowed not to fit into the equation - // add the patch to the set - if (matchingMoments.length > 1 && matchingMoments.length >= (eligibleMoments.length / 4) * 3 && matchingMoments.length > moments.length / 4) { - avg /= matchingMoments.length; - patch = { - index : patchPos[1] * _numPatches.x + patchPos[0], - pos : { - x : x, - y : y - }, - box : [vec2.clone([x, y]), vec2.clone([x + _subImageWrapper.size.x, y]), vec2.clone([x + _subImageWrapper.size.x, y + _subImageWrapper.size.y]), vec2.clone([x, y + _subImageWrapper.size.y])], - moments : matchingMoments, - rad : avg, - vec : vec2.clone([Math.cos(avg), Math.sin(avg)]) - }; - patchesFound.push(patch); - } + // Only two of the moments are allowed not to fit into the equation + // add the patch to the set + if (matchingMoments.length > 1 + && matchingMoments.length >= (eligibleMoments.length / 4) * 3 + && matchingMoments.length > moments.length / 4) { + avg /= matchingMoments.length; + patch = { + index: patchPos[1] * _numPatches.x + patchPos[0], + pos: { + x: x, + y: y + }, + box: [ + vec2.clone([x, y]), + vec2.clone([x + _subImageWrapper.size.x, y]), + vec2.clone([x + _subImageWrapper.size.x, y + _subImageWrapper.size.y]), + vec2.clone([x, y + _subImageWrapper.size.y]) + ], + moments: matchingMoments, + rad: avg, + vec: vec2.clone([Math.cos(avg), Math.sin(avg)]) + }; + patchesFound.push(patch); } } - return patchesFound; } - - /** - * finds patches which are connected and share the same orientation - * @param {Object} patchesFound - */ - function rasterizeAngularSimilarity(patchesFound) { - var label = 0, - threshold = 0.95, - currIdx = 0, - j, - patch, - hsv = [0, 1, 1], - rgb = [0, 0, 0]; - - function notYetProcessed() { - var i; - for ( i = 0; i < _patchLabelGrid.data.length; i++) { - if (_patchLabelGrid.data[i] === 0 && _patchGrid.data[i] === 1) { - return i; - } + return patchesFound; +} + +/** + * finds patches which are connected and share the same orientation + * @param {Object} patchesFound + */ +function rasterizeAngularSimilarity(patchesFound) { + var label = 0, + threshold = 0.95, + currIdx = 0, + j, + patch, + hsv = [0, 1, 1], + rgb = [0, 0, 0]; + + function notYetProcessed() { + var i; + for ( i = 0; i < _patchLabelGrid.data.length; i++) { + if (_patchLabelGrid.data[i] === 0 && _patchGrid.data[i] === 1) { + return i; } - return _patchLabelGrid.length; } + return _patchLabelGrid.length; + } - function trace(currentIdx) { - var x, y, currentPatch, patch, idx, dir, current = { - x : currentIdx % _patchLabelGrid.size.x, - y : (currentIdx / _patchLabelGrid.size.x) | 0 - }, similarity; - - if (currentIdx < _patchLabelGrid.data.length) { - currentPatch = _imageToPatchGrid.data[currentIdx]; - // assign label - _patchLabelGrid.data[currentIdx] = label; - for ( dir = 0; dir < Tracer.searchDirections.length; dir++) { - y = current.y + Tracer.searchDirections[dir][0]; - x = current.x + Tracer.searchDirections[dir][1]; - idx = y * _patchLabelGrid.size.x + x; - - // continue if patch empty - if (_patchGrid.data[idx] === 0) { - _patchLabelGrid.data[idx] = Number.MAX_VALUE; - continue; - } + function trace(currentIdx) { + var x, + y, + currentPatch, + idx, + dir, + current = { + x: currentIdx % _patchLabelGrid.size.x, + y: (currentIdx / _patchLabelGrid.size.x) | 0 + }, + similarity; + + if (currentIdx < _patchLabelGrid.data.length) { + currentPatch = _imageToPatchGrid.data[currentIdx]; + // assign label + _patchLabelGrid.data[currentIdx] = label; + for ( dir = 0; dir < Tracer.searchDirections.length; dir++) { + y = current.y + Tracer.searchDirections[dir][0]; + x = current.x + Tracer.searchDirections[dir][1]; + idx = y * _patchLabelGrid.size.x + x; + + // continue if patch empty + if (_patchGrid.data[idx] === 0) { + _patchLabelGrid.data[idx] = Number.MAX_VALUE; + continue; + } - patch = _imageToPatchGrid.data[idx]; - if (_patchLabelGrid.data[idx] === 0) { - similarity = Math.abs(vec2.dot(patch.vec, currentPatch.vec)); - if (similarity > threshold) { - trace(idx); - } + if (_patchLabelGrid.data[idx] === 0) { + similarity = Math.abs(vec2.dot(_imageToPatchGrid.data[idx].vec, currentPatch.vec)); + if (similarity > threshold) { + trace(idx); } } } } - - // prepare for finding the right patches - ArrayHelper.init(_patchGrid.data, 0); - ArrayHelper.init(_patchLabelGrid.data, 0); - ArrayHelper.init(_imageToPatchGrid.data, null); - - for ( j = 0; j < patchesFound.length; j++) { - patch = patchesFound[j]; - _imageToPatchGrid.data[patch.index] = patch; - _patchGrid.data[patch.index] = 1; - } + } - // rasterize the patches found to determine area - _patchGrid.zeroBorder(); + // prepare for finding the right patches + ArrayHelper.init(_patchGrid.data, 0); + ArrayHelper.init(_patchLabelGrid.data, 0); + ArrayHelper.init(_imageToPatchGrid.data, null); - while (( currIdx = notYetProcessed()) < _patchLabelGrid.data.length) { - label++; - trace(currIdx); - } - - // draw patch-labels if requested - if (_config.showPatchLabels) { - for ( j = 0; j < _patchLabelGrid.data.length; j++) { - if (_patchLabelGrid.data[j] > 0 && _patchLabelGrid.data[j] <= label) { - patch = _imageToPatchGrid.data[j]; - hsv[0] = (_patchLabelGrid.data[j] / (label + 1)) * 360; - CVUtils.hsv2rgb(hsv, rgb); - ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, {color: "rgb(" + rgb.join(",") + ")", lineWidth: 2}); - } + for ( j = 0; j < patchesFound.length; j++) { + patch = patchesFound[j]; + _imageToPatchGrid.data[patch.index] = patch; + _patchGrid.data[patch.index] = 1; + } + + // rasterize the patches found to determine area + _patchGrid.zeroBorder(); + + while (( currIdx = notYetProcessed()) < _patchLabelGrid.data.length) { + label++; + trace(currIdx); + } + + // draw patch-labels if requested + if (_config.showPatchLabels) { + for ( j = 0; j < _patchLabelGrid.data.length; j++) { + if (_patchLabelGrid.data[j] > 0 && _patchLabelGrid.data[j] <= label) { + patch = _imageToPatchGrid.data[j]; + hsv[0] = (_patchLabelGrid.data[j] / (label + 1)) * 360; + CVUtils.hsv2rgb(hsv, rgb); + ImageDebug.drawRect(patch.pos, _subImageWrapper.size, _canvasContainer.ctx.binary, + {color: "rgb(" + rgb.join(",") + ")", lineWidth: 2}); } } - - return label; } - return { - init : function(inputImageWrapper, config) { - _config = config; - _inputImageWrapper = inputImageWrapper; + return label; +} - initBuffers(); - initCanvas(); - }, +export default { + init: function(inputImageWrapper, config) { + _config = config; + _inputImageWrapper = inputImageWrapper; + + initBuffers(); + initCanvas(); + }, - locate : function() { - var patchesFound, + locate: function() { + var patchesFound, topLabels, boxes; - if (_config.halfSample) { - CVUtils.halfSample(_inputImageWrapper, _currentImageWrapper); - } - - binarizeImage(); - patchesFound = findPatches(); - // return unless 5% or more patches are found - if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) { - return null; - } - - // rasterrize area by comparing angular similarity; - var maxLabel = rasterizeAngularSimilarity(patchesFound); - if (maxLabel < 1) { - return null; - } + if (_config.halfSample) { + CVUtils.halfSample(_inputImageWrapper, _currentImageWrapper); + } - // search for area with the most patches (biggest connected area) - topLabels = findBiggestConnectedAreas(maxLabel); - if (topLabels.length === 0) { - return null; - } + binarizeImage(); + patchesFound = findPatches(); + // return unless 5% or more patches are found + if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) { + return null; + } - boxes = findBoxes(topLabels, maxLabel); - return boxes; - }, + // rasterrize area by comparing angular similarity; + var maxLabel = rasterizeAngularSimilarity(patchesFound); + if (maxLabel < 1) { + return null; + } - checkImageConstraints: function(inputStream, config) { - var patchSize, - width = inputStream.getWidth(), - height = inputStream.getHeight(), - halfSample = config.halfSample ? 0.5 : 1, - size, - area; - - // calculate width and height based on area - if (inputStream.getConfig().area) { - area = CVUtils.computeImageArea(width, height, inputStream.getConfig().area); - inputStream.setTopRight({x: area.sx, y: area.sy}); - inputStream.setCanvasSize({x: width, y: height}); - width = area.sw; - height = area.sh; - } + // search for area with the most patches (biggest connected area) + topLabels = findBiggestConnectedAreas(maxLabel); + if (topLabels.length === 0) { + return null; + } - size = { - x: Math.floor(width * halfSample), - y: Math.floor(height * halfSample) - }; + boxes = findBoxes(topLabels, maxLabel); + return boxes; + }, + + checkImageConstraints: function(inputStream, config) { + var patchSize, + width = inputStream.getWidth(), + height = inputStream.getHeight(), + halfSample = config.halfSample ? 0.5 : 1, + size, + area; + + // calculate width and height based on area + if (inputStream.getConfig().area) { + area = CVUtils.computeImageArea(width, height, inputStream.getConfig().area); + inputStream.setTopRight({x: area.sx, y: area.sy}); + inputStream.setCanvasSize({x: width, y: height}); + width = area.sw; + height = area.sh; + } - patchSize = CVUtils.calculatePatchSize(config.patchSize, size); - console.log("Patch-Size: " + JSON.stringify(patchSize)); + size = { + x: Math.floor(width * halfSample), + y: Math.floor(height * halfSample) + }; - inputStream.setWidth(Math.floor(Math.floor(size.x/patchSize.x)*(1/halfSample)*patchSize.x)); - inputStream.setHeight(Math.floor(Math.floor(size.y/patchSize.y)*(1/halfSample)*patchSize.y)); + patchSize = CVUtils.calculatePatchSize(config.patchSize, size); + console.log("Patch-Size: " + JSON.stringify(patchSize)); - if ((inputStream.getWidth() % patchSize.x) === 0 && (inputStream.getHeight() % patchSize.y) === 0) { - return true; - } + inputStream.setWidth(Math.floor(Math.floor(size.x / patchSize.x) * (1 / halfSample) * patchSize.x)); + inputStream.setHeight(Math.floor(Math.floor(size.y / patchSize.y) * (1 / halfSample) * patchSize.y)); - throw new Error("Image dimensions do not comply with the current settings: Width (" + - width + " )and height (" + height + - ") must a multiple of " + patchSize.x); + if ((inputStream.getWidth() % patchSize.x) === 0 && (inputStream.getHeight() % patchSize.y) === 0) { + return true; } - }; -}); + throw new Error("Image dimensions do not comply with the current settings: Width (" + + width + " )and height (" + height + + ") must a multiple of " + patchSize.x); + } +}; diff --git a/src/barcode_reader.js b/src/barcode_reader.js index ec2f5f87..ac2da266 100644 --- a/src/barcode_reader.js +++ b/src/barcode_reader.js @@ -1,231 +1,222 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - function() { - "use strict"; - - function BarcodeReader(config) { - this._row = []; - this.config = config || {}; - return this; +function BarcodeReader(config) { + this._row = []; + this.config = config || {}; + return this; +} + +BarcodeReader.prototype._nextUnset = function(line, start) { + var i; + + if (start === undefined) { + start = 0; + } + for (i = start; i < line.length; i++) { + if (!line[i]) { + return i; } - - BarcodeReader.prototype._nextUnset = function(line, start) { - var i; - - if (start === undefined) { - start = 0; - } - for (i = start; i < line.length; i++) { - if (!line[i]) { - return i; - } - } - return line.length; - }; - - BarcodeReader.prototype._matchPattern = function(counter, code) { - var i, - error = 0, - singleError = 0, - modulo = this.MODULO, - maxSingleError = this.SINGLE_CODE_ERROR || 1; - - for (i = 0; i < counter.length; i++) { - singleError = Math.abs(code[i] - counter[i]); - if (singleError > maxSingleError) { - return Number.MAX_VALUE; - } - error += singleError; - } - return error/modulo; - }; + } + return line.length; +}; - BarcodeReader.prototype._nextSet = function(line, offset) { - var i; +BarcodeReader.prototype._matchPattern = function(counter, code) { + var i, + error = 0, + singleError = 0, + modulo = this.MODULO, + maxSingleError = this.SINGLE_CODE_ERROR || 1; - offset = offset || 0; - for (i = offset; i < line.length; i++) { - if (line[i]) { - return i; - } - } - return line.length; - }; - - BarcodeReader.prototype._normalize = function(counter, modulo) { - var i, - self = this, - sum = 0, - ratio, - numOnes = 0, - normalized = [], - norm = 0; - - if (!modulo) { - modulo = self.MODULO; - } - for (i = 0; i < counter.length; i++) { - if (counter[i] === 1) { - numOnes++; - } else { - sum += counter[i]; - } - } - ratio = sum / (modulo - numOnes); - if (ratio > 1.0) { - for (i = 0; i < counter.length; i++) { - norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; - normalized.push(norm); - } + for (i = 0; i < counter.length; i++) { + singleError = Math.abs(code[i] - counter[i]); + if (singleError > maxSingleError) { + return Number.MAX_VALUE; + } + error += singleError; + } + return error / modulo; +}; + +BarcodeReader.prototype._nextSet = function(line, offset) { + var i; + + offset = offset || 0; + for (i = offset; i < line.length; i++) { + if (line[i]) { + return i; + } + } + return line.length; +}; + +BarcodeReader.prototype._normalize = function(counter, modulo) { + var i, + self = this, + sum = 0, + ratio, + numOnes = 0, + normalized = [], + norm = 0; + + if (!modulo) { + modulo = self.MODULO; + } + for (i = 0; i < counter.length; i++) { + if (counter[i] === 1) { + numOnes++; + } else { + sum += counter[i]; + } + } + ratio = sum / (modulo - numOnes); + if (ratio > 1.0) { + for (i = 0; i < counter.length; i++) { + norm = counter[i] === 1 ? counter[i] : counter[i] / ratio; + normalized.push(norm); + } + } else { + ratio = (sum + numOnes) / modulo; + for (i = 0; i < counter.length; i++) { + norm = counter[i] / ratio; + normalized.push(norm); + } + } + return normalized; +}; + +BarcodeReader.prototype._matchTrace = function(cmpCounter, epsilon) { + var counter = [], + i, + self = this, + offset = self._nextSet(self._row), + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0 + }, + error; + + if (cmpCounter) { + for ( i = 0; i < cmpCounter.length; i++) { + counter.push(0); + } + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; } else { - ratio = (sum + numOnes)/modulo; - for (i = 0; i < counter.length; i++) { - norm = counter[i] / ratio; - normalized.push(norm); - } - } - return normalized; - }; - - BarcodeReader.prototype._matchTrace = function(cmpCounter, epsilon) { - var counter = [], - i, - self = this, - offset = self._nextSet(self._row), - isWhite = !self._row[offset], - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0 - }, - error; - - if (cmpCounter) { - for ( i = 0; i < cmpCounter.length; i++) { - counter.push(0); - } - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; + if (counterPos === counter.length - 1) { + error = self._matchPattern(counter, cmpCounter); + + if (error < epsilon) { + bestMatch.start = i - offset; + bestMatch.end = i; + bestMatch.counter = counter; + return bestMatch; } else { - if (counterPos === counter.length - 1) { - error = self._matchPattern(counter, cmpCounter); - - if (error < epsilon) { - bestMatch.start = i - offset; - bestMatch.end = i; - bestMatch.counter = counter; - return bestMatch; - } else { - return null; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; + return null; } + } else { + counterPos++; } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } else { + counter.push(0); + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; } else { + counterPos++; counter.push(0); - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - counterPos++; - counter.push(0); - counter[counterPos] = 1; - isWhite = !isWhite; - } - } + counter[counterPos] = 1; + isWhite = !isWhite; } + } + } - // if cmpCounter was not given - bestMatch.start = offset; - bestMatch.end = self._row.length - 1; - bestMatch.counter = counter; - return bestMatch; - }; - - BarcodeReader.prototype.decodePattern = function(pattern) { - var self = this, - result; - - self._row = pattern; - result = self._decode(); - if (result === null) { - self._row.reverse(); - result = self._decode(); - if (result) { - result.direction = BarcodeReader.DIRECTION.REVERSE; - result.start = self._row.length - result.start; - result.end = self._row.length - result.end; - } - } else { - result.direction = BarcodeReader.DIRECTION.FORWARD; - } - if (result) { - result.format = self.FORMAT; - } - return result; - }; + // if cmpCounter was not given + bestMatch.start = offset; + bestMatch.end = self._row.length - 1; + bestMatch.counter = counter; + return bestMatch; +}; - BarcodeReader.prototype._matchRange = function(start, end, value) { - var i; +BarcodeReader.prototype.decodePattern = function(pattern) { + var self = this, + result; - start = start < 0 ? 0 : start; - for (i = start; i < end; i++) { - if (this._row[i] !== value) { - return false; - } - } - return true; - }; - - BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) { - var self = this, - counterPos = 0, - i, - counters = []; - - isWhite = (typeof isWhite !== 'undefined') ? isWhite : true; - offset = (typeof offset !== 'undefined') ? offset : self._nextUnset(self._row); - end = end || self._row.length; - - counters[counterPos] = 0; - for (i = offset; i < end; i++) { - if (self._row[i] ^ isWhite) { - counters[counterPos]++; - } else { - counterPos++; - counters[counterPos] = 1; - isWhite = !isWhite; - } - } - return counters; - }; - - Object.defineProperty(BarcodeReader.prototype, "FORMAT", { - value: 'unknown', - writeable: false - }); - - BarcodeReader.DIRECTION = { - FORWARD : 1, - REVERSE : -1 - }; - - BarcodeReader.Exception = { - StartNotFoundException : "Start-Info was not found!", - CodeNotFoundException : "Code could not be found!", - PatternNotFoundException : "Pattern could not be found!" - }; - - BarcodeReader.CONFIG_KEYS = {}; - - return (BarcodeReader); + self._row = pattern; + result = self._decode(); + if (result === null) { + self._row.reverse(); + result = self._decode(); + if (result) { + result.direction = BarcodeReader.DIRECTION.REVERSE; + result.start = self._row.length - result.start; + result.end = self._row.length - result.end; + } + } else { + result.direction = BarcodeReader.DIRECTION.FORWARD; + } + if (result) { + result.format = self.FORMAT; + } + return result; +}; + +BarcodeReader.prototype._matchRange = function(start, end, value) { + var i; + + start = start < 0 ? 0 : start; + for (i = start; i < end; i++) { + if (this._row[i] !== value) { + return false; + } + } + return true; +}; + +BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) { + var self = this, + counterPos = 0, + i, + counters = []; + + isWhite = (typeof isWhite !== 'undefined') ? isWhite : true; + offset = (typeof offset !== 'undefined') ? offset : self._nextUnset(self._row); + end = end || self._row.length; + + counters[counterPos] = 0; + for (i = offset; i < end; i++) { + if (self._row[i] ^ isWhite) { + counters[counterPos]++; + } else { + counterPos++; + counters[counterPos] = 1; + isWhite = !isWhite; + } } -); + return counters; +}; + +Object.defineProperty(BarcodeReader.prototype, "FORMAT", { + value: 'unknown', + writeable: false +}); + +BarcodeReader.DIRECTION = { + FORWARD: 1, + REVERSE: -1 +}; + +BarcodeReader.Exception = { + StartNotFoundException: "Start-Info was not found!", + CodeNotFoundException: "Code could not be found!", + PatternNotFoundException: "Pattern could not be found!" +}; + +BarcodeReader.CONFIG_KEYS = {}; + +export default BarcodeReader; diff --git a/src/bresenham.js b/src/bresenham.js index a59c1636..8afe4573 100644 --- a/src/bresenham.js +++ b/src/bresenham.js @@ -1,217 +1,213 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(["cv_utils", "image_wrapper"], function(CVUtils, ImageWrapper) { - "use strict"; - var Bresenham = {}; - - var Slope = { - DIR : { - UP : 1, - DOWN : -1 - } - }; - /** - * Scans a line of the given image from point p1 to p2 and returns a result object containing - * gray-scale values (0-255) of the underlying pixels in addition to the min - * and max values. - * @param {Object} imageWrapper - * @param {Object} p1 The start point {x,y} - * @param {Object} p2 The end point {x,y} - * @returns {line, min, max} - */ - Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) { - var x0 = p1.x | 0, - y0 = p1.y | 0, - x1 = p2.x | 0, - y1 = p2.y | 0, - steep = Math.abs(y1 - y0) > Math.abs(x1 - x0), - deltax, - deltay, - error, - ystep, - y, - tmp, - x, - line = [], - imageData = imageWrapper.data, - width = imageWrapper.size.x, - sum = 0, - val, - min = 255, - max = 0; - - function read(a, b) { - val = imageData[b * width + a]; - sum += val; - min = val < min ? val : min; - max = val > max ? val : max; - line.push(val); - } - - if (steep) { - tmp = x0; - x0 = y0; - y0 = tmp; - - tmp = x1; - x1 = y1; - y1 = tmp; - } - if (x0 > x1) { - tmp = x0; - x0 = x1; - x1 = tmp; - - tmp = y0; - y0 = y1; - y1 = tmp; +import CVUtils from './cv_utils'; +import ImageWrapper from './image_wrapper'; + +var Bresenham = {}; + +var Slope = { + DIR: { + UP: 1, + DOWN: -1 + } +}; +/** + * Scans a line of the given image from point p1 to p2 and returns a result object containing + * gray-scale values (0-255) of the underlying pixels in addition to the min + * and max values. + * @param {Object} imageWrapper + * @param {Object} p1 The start point {x,y} + * @param {Object} p2 The end point {x,y} + * @returns {line, min, max} + */ +Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) { + var x0 = p1.x | 0, + y0 = p1.y | 0, + x1 = p2.x | 0, + y1 = p2.y | 0, + steep = Math.abs(y1 - y0) > Math.abs(x1 - x0), + deltax, + deltay, + error, + ystep, + y, + tmp, + x, + line = [], + imageData = imageWrapper.data, + width = imageWrapper.size.x, + sum = 0, + val, + min = 255, + max = 0; + + function read(a, b) { + val = imageData[b * width + a]; + sum += val; + min = val < min ? val : min; + max = val > max ? val : max; + line.push(val); + } + + if (steep) { + tmp = x0; + x0 = y0; + y0 = tmp; + + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + + tmp = y0; + y0 = y1; + y1 = tmp; + } + deltax = x1 - x0; + deltay = Math.abs(y1 - y0); + error = (deltax / 2) | 0; + y = y0; + ystep = y0 < y1 ? 1 : -1; + for ( x = x0; x < x1; x++) { + if (steep){ + read(y, x); + } else { + read(x, y); } - deltax = x1 - x0; - deltay = Math.abs(y1 - y0); - error = (deltax / 2) | 0; - y = y0; - ystep = y0 < y1 ? 1 : -1; - for ( x = x0; x < x1; x++) { - if(steep){ - read(y, x); - } else { - read(x, y); - } - error = error - deltay; - if (error < 0) { - y = y + ystep; - error = error + deltax; - } + error = error - deltay; + if (error < 0) { + y = y + ystep; + error = error + deltax; } + } - return { - line : line, - min : min, - max : max - }; + return { + line: line, + min: min, + max: max }; +}; - Bresenham.toOtsuBinaryLine = function(result) { - var line = result.line, - image = new ImageWrapper({x: line.length - 1, y: 1}, line), - threshold = CVUtils.determineOtsuThreshold(image, 5); +Bresenham.toOtsuBinaryLine = function(result) { + var line = result.line, + image = new ImageWrapper({x: line.length - 1, y: 1}, line), + threshold = CVUtils.determineOtsuThreshold(image, 5); - line = CVUtils.sharpenLine(line); - CVUtils.thresholdImage(image, threshold); + line = CVUtils.sharpenLine(line); + CVUtils.thresholdImage(image, threshold); - return { - line: line, - threshold: threshold - }; + return { + line: line, + threshold: threshold }; - - /** - * Converts the result from getBarcodeLine into a binary representation - * also considering the frequency and slope of the signal for more robust results - * @param {Object} result {line, min, max} - */ - Bresenham.toBinaryLine = function(result) { - - var min = result.min, - max = result.max, - line = result.line, - slope, - slope2, - center = min + (max - min) / 2, - extrema = [], - currentDir, - dir, - threshold = (max - min) / 12, - rThreshold = -threshold, - i, - j; - - // 1. find extrema - currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN; - extrema.push({ - pos : 0, - val : line[0] - }); - for ( i = 0; i < line.length - 2; i++) { - slope = (line[i + 1] - line[i]); - slope2 = (line[i + 2] - line[i + 1]); - if ((slope + slope2) < rThreshold && line[i + 1] < (center*1.5)) { - dir = Slope.DIR.DOWN; - } else if ((slope + slope2) > threshold && line[i + 1] > (center*0.5)) { - dir = Slope.DIR.UP; - } else { - dir = currentDir; - } - - if (currentDir !== dir) { - extrema.push({ - pos : i, - val : line[i] - }); - currentDir = dir; - } +}; + +/** + * Converts the result from getBarcodeLine into a binary representation + * also considering the frequency and slope of the signal for more robust results + * @param {Object} result {line, min, max} + */ +Bresenham.toBinaryLine = function(result) { + var min = result.min, + max = result.max, + line = result.line, + slope, + slope2, + center = min + (max - min) / 2, + extrema = [], + currentDir, + dir, + threshold = (max - min) / 12, + rThreshold = -threshold, + i, + j; + + // 1. find extrema + currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN; + extrema.push({ + pos: 0, + val: line[0] + }); + for ( i = 0; i < line.length - 2; i++) { + slope = (line[i + 1] - line[i]); + slope2 = (line[i + 2] - line[i + 1]); + if ((slope + slope2) < rThreshold && line[i + 1] < (center * 1.5)) { + dir = Slope.DIR.DOWN; + } else if ((slope + slope2) > threshold && line[i + 1] > (center * 0.5)) { + dir = Slope.DIR.UP; + } else { + dir = currentDir; } - extrema.push({ - pos : line.length, - val : line[line.length - 1] - }); - for ( j = extrema[0].pos; j < extrema[1].pos; j++) { - line[j] = line[j] > center ? 0 : 1; + if (currentDir !== dir) { + extrema.push({ + pos: i, + val: line[i] + }); + currentDir = dir; + } + } + extrema.push({ + pos: line.length, + val: line[line.length - 1] + }); + + for ( j = extrema[0].pos; j < extrema[1].pos; j++) { + line[j] = line[j] > center ? 0 : 1; + } + + // iterate over extrema and convert to binary based on avg between minmax + for ( i = 1; i < extrema.length - 1; i++) { + if (extrema[i + 1].val > extrema[i].val) { + threshold = (extrema[i].val + ((extrema[i + 1].val - extrema[i].val) / 3) * 2) | 0; + } else { + threshold = (extrema[i + 1].val + ((extrema[i].val - extrema[i + 1].val) / 3)) | 0; } - // iterate over extrema and convert to binary based on avg between minmax - for ( i = 1; i < extrema.length - 1; i++) { - if (extrema[i + 1].val > extrema[i].val) { - threshold = (extrema[i].val + ((extrema[i + 1].val - extrema[i].val) / 3) * 2) | 0; - } else { - threshold = (extrema[i + 1].val + ((extrema[i].val - extrema[i + 1].val) / 3)) | 0; - } - - for ( j = extrema[i].pos; j < extrema[i + 1].pos; j++) { - line[j] = line[j] > threshold ? 0 : 1; - } + for ( j = extrema[i].pos; j < extrema[i + 1].pos; j++) { + line[j] = line[j] > threshold ? 0 : 1; } + } - return { - line : line, - threshold : threshold - }; + return { + line: line, + threshold: threshold }; - - /** - * Used for development only - */ - Bresenham.debug = { - printFrequency: function(line, canvas) { - var i, - ctx = canvas.getContext("2d"); - canvas.width = line.length; - canvas.height = 256; - - ctx.beginPath(); - ctx.strokeStyle = "blue"; - for ( i = 0; i < line.length; i++) { - ctx.moveTo(i, 255); - ctx.lineTo(i, 255 - line[i]); - } - ctx.stroke(); - ctx.closePath(); - }, - - printPattern: function(line, canvas) { - var ctx = canvas.getContext("2d"), i; - - canvas.width = line.length; - ctx.fillColor = "black"; - for ( i = 0; i < line.length; i++) { - if (line[i] === 1) { - ctx.fillRect(i, 0, 1, 100); - } +}; + +/** + * Used for development only + */ +Bresenham.debug = { + printFrequency: function(line, canvas) { + var i, + ctx = canvas.getContext("2d"); + canvas.width = line.length; + canvas.height = 256; + + ctx.beginPath(); + ctx.strokeStyle = "blue"; + for ( i = 0; i < line.length; i++) { + ctx.moveTo(i, 255); + ctx.lineTo(i, 255 - line[i]); + } + ctx.stroke(); + ctx.closePath(); + }, + + printPattern: function(line, canvas) { + var ctx = canvas.getContext("2d"), i; + + canvas.width = line.length; + ctx.fillColor = "black"; + for ( i = 0; i < line.length; i++) { + if (line[i] === 1) { + ctx.fillRect(i, 0, 1, 100); } } - }; + } +}; - return (Bresenham); -}); \ No newline at end of file +export default Bresenham; diff --git a/src/camera_access.js b/src/camera_access.js index bf4e831f..addcb7d8 100644 --- a/src/camera_access.js +++ b/src/camera_access.js @@ -1,143 +1,139 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define, MediaStreamTrack */ +const merge = require('lodash/object/merge'); -define(["html_utils"], function(HtmlUtils) { - "use strict"; - var streamRef, - loadedDataHandler; +var streamRef, + loadedDataHandler; - /** - * Wraps browser-specific getUserMedia - * @param {Object} constraints - * @param {Object} success Callback - * @param {Object} failure Callback - */ - function getUserMedia(constraints, success, failure) { - if (typeof navigator.getUserMedia !== 'undefined') { - navigator.getUserMedia(constraints, function (stream) { - streamRef = stream; - var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream; - success.apply(null, [videoSrc]); - }, failure); - } else { - failure(new TypeError("getUserMedia not available")); - } +/** + * Wraps browser-specific getUserMedia + * @param {Object} constraints + * @param {Object} success Callback + * @param {Object} failure Callback + */ +function getUserMedia(constraints, success, failure) { + if (typeof navigator.getUserMedia !== 'undefined') { + navigator.getUserMedia(constraints, function (stream) { + streamRef = stream; + var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream; + success.apply(null, [videoSrc]); + }, failure); + } else { + failure(new TypeError("getUserMedia not available")); } +} - function loadedData(video, callback) { - var attempts = 10; +function loadedData(video, callback) { + var attempts = 10; - function checkVideo() { - if (attempts > 0) { - if (video.videoWidth > 0 && video.videoHeight > 0) { - console.log(video.videoWidth + "px x " + video.videoHeight + "px"); - callback(); - } else { - window.setTimeout(checkVideo, 500); - } + function checkVideo() { + if (attempts > 0) { + if (video.videoWidth > 0 && video.videoHeight > 0) { + console.log(video.videoWidth + "px x " + video.videoHeight + "px"); + callback(); } else { - callback('Unable to play video stream. Is webcam working?'); + window.setTimeout(checkVideo, 500); } - attempts--; + } else { + callback('Unable to play video stream. Is webcam working?'); } - checkVideo(); + attempts--; } + checkVideo(); +} - /** - * Tries to attach the camera-stream to a given video-element - * and calls the callback function when the content is ready - * @param {Object} constraints - * @param {Object} video - * @param {Object} callback - */ - function initCamera(constraints, video, callback) { - getUserMedia(constraints, function(src) { - video.src = src; - if (loadedDataHandler) { - video.removeEventListener("loadeddata", loadedDataHandler, false); - } - loadedDataHandler = loadedData.bind(null, video, callback); - video.addEventListener('loadeddata', loadedDataHandler, false); - video.play(); - }, function(e) { - callback(e); - }); - } +/** + * Tries to attach the camera-stream to a given video-element + * and calls the callback function when the content is ready + * @param {Object} constraints + * @param {Object} video + * @param {Object} callback + */ +function initCamera(constraints, video, callback) { + getUserMedia(constraints, function(src) { + video.src = src; + if (loadedDataHandler) { + video.removeEventListener("loadeddata", loadedDataHandler, false); + } + loadedDataHandler = loadedData.bind(null, video, callback); + video.addEventListener('loadeddata', loadedDataHandler, false); + video.play(); + }, function(e) { + callback(e); + }); +} - /** - * Normalizes the incoming constraints to satisfy the current browser - * @param config - * @param cb Callback which is called whenever constraints are created - * @returns {*} - */ - function normalizeConstraints(config, cb) { - var constraints = { - audio: false, - video: true - }, - videoConstraints = HtmlUtils.mergeObjects({ - width: 640, - height: 480, - minAspectRatio: 0, - maxAspectRatio: 100, - facing: "environment" - }, config); +/** + * Normalizes the incoming constraints to satisfy the current browser + * @param config + * @param cb Callback which is called whenever constraints are created + * @returns {*} + */ +function normalizeConstraints(config, cb) { + var constraints = { + audio: false, + video: true + }, + videoConstraints = merge({ + width: 640, + height: 480, + minAspectRatio: 0, + maxAspectRatio: 100, + facing: "environment" + }, config); - if ( typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') { - MediaStreamTrack.getSources(function(sourceInfos) { - var videoSourceId; - for (var i = 0; i != sourceInfos.length; ++i) { - var sourceInfo = sourceInfos[i]; - if (sourceInfo.kind == "video" && sourceInfo.facing == videoConstraints.facing) { - videoSourceId = sourceInfo.id; - } + if ( typeof MediaStreamTrack !== 'undefined' && typeof MediaStreamTrack.getSources !== 'undefined') { + MediaStreamTrack.getSources(function(sourceInfos) { + var videoSourceId; + for (var i = 0; i < sourceInfos.length; ++i) { + var sourceInfo = sourceInfos[i]; + if (sourceInfo.kind === "video" && sourceInfo.facing === videoConstraints.facing) { + videoSourceId = sourceInfo.id; } - constraints.video = { - mandatory: { - minWidth: videoConstraints.width, - minHeight: videoConstraints.height, - minAspectRatio: videoConstraints.minAspectRatio, - maxAspectRatio: videoConstraints.maxAspectRatio - }, - optional: [{ - sourceId: videoSourceId - }] - }; - return cb(constraints); - }); - } else { + } constraints.video = { - mediaSource: "camera", - width: { min: videoConstraints.width, max: videoConstraints.width }, - height: { min: videoConstraints.height, max: videoConstraints.height }, - require: ["width", "height"] + mandatory: { + minWidth: videoConstraints.width, + minHeight: videoConstraints.height, + minAspectRatio: videoConstraints.minAspectRatio, + maxAspectRatio: videoConstraints.maxAspectRatio + }, + optional: [{ + sourceId: videoSourceId + }] }; return cb(constraints); - } - } - - /** - * Requests the back-facing camera of the user. The callback is called - * whenever the stream is ready to be consumed, or if an error occures. - * @param {Object} video - * @param {Object} callback - */ - function request(video, videoConstraints, callback) { - normalizeConstraints(videoConstraints, function(constraints) { - initCamera(constraints, video, callback); }); + } else { + constraints.video = { + mediaSource: "camera", + width: { min: videoConstraints.width, max: videoConstraints.width }, + height: { min: videoConstraints.height, max: videoConstraints.height }, + require: ["width", "height"] + }; + return cb(constraints); } +} - return { - request : function(video, constraints, callback) { - request(video, constraints, callback); - }, - release : function() { - var tracks = streamRef && streamRef.getVideoTracks(); - if (tracks.length) { - tracks[0].stop(); - } - streamRef = null; +/** + * Requests the back-facing camera of the user. The callback is called + * whenever the stream is ready to be consumed, or if an error occures. + * @param {Object} video + * @param {Object} callback + */ +function request(video, videoConstraints, callback) { + normalizeConstraints(videoConstraints, function(constraints) { + initCamera(constraints, video, callback); + }); +} + +export default { + request: function(video, constraints, callback) { + request(video, constraints, callback); + }, + release: function() { + var tracks = streamRef && streamRef.getVideoTracks(); + if (tracks.length) { + tracks[0].stop(); } - }; -}); + streamRef = null; + } +}; diff --git a/src/cluster.js b/src/cluster.js index 650a2c8a..de4d1418 100644 --- a/src/cluster.js +++ b/src/cluster.js @@ -1,72 +1,65 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(["gl-matrix"], function(glMatrix) { - "use strict"; - - var vec2 = glMatrix.vec2; +import {vec2} from 'gl-matrix'; /** - * Creates a cluster for grouping similar orientations of datapoints + * Creates a cluster for grouping similar orientations of datapoints */ - var Cluster = { - create : function(point, threshold) { - var points = [], center = { - rad : 0, - vec : vec2.clone([0, 0]) - }, pointMap = {}; +export default { + create: function(point, threshold) { + var points = [], + center = { + rad: 0, + vec: vec2.clone([0, 0]) + }, + pointMap = {}; - function init() { - add(point); - updateCenter(); - } + function init() { + add(point); + updateCenter(); + } - function add(point) { - pointMap[point.id] = point; - points.push(point); - } + function add(pointToAdd) { + pointMap[pointToAdd.id] = pointToAdd; + points.push(pointToAdd); + } - function updateCenter() { - var i, sum = 0; - for ( i = 0; i < points.length; i++) { - sum += points[i].rad; - } - center.rad = sum / points.length; - center.vec = vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]); + function updateCenter() { + var i, sum = 0; + for ( i = 0; i < points.length; i++) { + sum += points[i].rad; } + center.rad = sum / points.length; + center.vec = vec2.clone([Math.cos(center.rad), Math.sin(center.rad)]); + } - init(); + init(); - return { - add : function(point) { - if (!pointMap[point.id]) { - add(point); - updateCenter(); - } - }, - fits : function(point) { - // check cosine similarity to center-angle - var similarity = Math.abs(vec2.dot(point.point.vec, center.vec)); - if (similarity > threshold) { - return true; - } - return false; - }, - getPoints : function() { - return points; - }, - getCenter : function() { - return center; + return { + add: function(pointToAdd) { + if (!pointMap[pointToAdd.id]) { + add(pointToAdd); + updateCenter(); } - }; - }, - createPoint : function(point, id, property) { - return { - rad : point[property], - point : point, - id : id - }; - } - }; - - return (Cluster); -}); + }, + fits: function(otherPoint) { + // check cosine similarity to center-angle + var similarity = Math.abs(vec2.dot(otherPoint.point.vec, center.vec)); + if (similarity > threshold) { + return true; + } + return false; + }, + getPoints: function() { + return points; + }, + getCenter: function() { + return center; + } + }; + }, + createPoint: function(newPoint, id, property) { + return { + rad: newPoint[property], + point: newPoint, + id: id + }; + } +}; diff --git a/src/codabar_reader.js b/src/codabar_reader.js index 087fb35f..25e749bc 100644 --- a/src/codabar_reader.js +++ b/src/codabar_reader.js @@ -1,294 +1,288 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./barcode_reader" - ], - function(BarcodeReader) { - "use strict"; - - function CodabarReader() { - BarcodeReader.call(this); - this._counters = []; - } - - var properties = { - ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"}, - ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68]}, - CHARACTER_ENCODINGS: {value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E]}, - START_END: {value: [0x01A, 0x029, 0x00B, 0x00E]}, - MIN_ENCODED_CHARS: {value: 4}, - MAX_ACCEPTABLE: {value: 2.0}, - PADDING: {value: 1.5}, - FORMAT: {value: "codabar", writeable: false} - }; - - CodabarReader.prototype = Object.create(BarcodeReader.prototype, properties); - CodabarReader.prototype.constructor = CodabarReader; - - CodabarReader.prototype._decode = function() { - var self = this, - result = [], - start, - decodedChar, - pattern, - nextStart, - end; - - this._counters = self._fillCounters(); - start = self._findStart(); - if (!start) { - return null; - } - nextStart = start.startCounter; - - do { - pattern = self._toPattern(nextStart); - if (pattern < 0) { - return null; - } - decodedChar = self._patternToChar(pattern); - if (decodedChar < 0){ - return null; - } - result.push(decodedChar); - nextStart += 8; - if (result.length > 1 && self._isStartEnd(pattern)) { - break; - } - } while(nextStart < self._counters.length); - - // verify end - if ((result.length - 2) < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) { - return null; - } +import BarcodeReader from './barcode_reader'; + +function CodabarReader() { + BarcodeReader.call(this); + this._counters = []; +} + +var properties = { + ALPHABETH_STRING: {value: "0123456789-$:/.+ABCD"}, + ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 36, 58, 47, 46, 43, 65, 66, 67, 68]}, + CHARACTER_ENCODINGS: {value: [0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, 0x00c, 0x018, + 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E]}, + START_END: {value: [0x01A, 0x029, 0x00B, 0x00E]}, + MIN_ENCODED_CHARS: {value: 4}, + MAX_ACCEPTABLE: {value: 2.0}, + PADDING: {value: 1.5}, + FORMAT: {value: "codabar", writeable: false} +}; + +CodabarReader.prototype = Object.create(BarcodeReader.prototype, properties); +CodabarReader.prototype.constructor = CodabarReader; + +CodabarReader.prototype._decode = function() { + var self = this, + result = [], + start, + decodedChar, + pattern, + nextStart, + end; + + this._counters = self._fillCounters(); + start = self._findStart(); + if (!start) { + return null; + } + nextStart = start.startCounter; - // verify end white space - if (!self._verifyWhitespace(start.startCounter, nextStart - 8)){ - return null; - } + do { + pattern = self._toPattern(nextStart); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + if (decodedChar < 0){ + return null; + } + result.push(decodedChar); + nextStart += 8; + if (result.length > 1 && self._isStartEnd(pattern)) { + break; + } + } while (nextStart < self._counters.length); - if (!self._validateResult(result, start.startCounter)){ - return null; - } + // verify end + if ((result.length - 2) < self.MIN_ENCODED_CHARS || !self._isStartEnd(pattern)) { + return null; + } - nextStart = nextStart > self._counters.length ? self._counters.length : nextStart; - end = start.start + self._sumCounters(start.startCounter, nextStart - 8); + // verify end white space + if (!self._verifyWhitespace(start.startCounter, nextStart - 8)){ + return null; + } - return { - code : result.join(""), - start : start.start, - end : end, - startInfo : start, - decodedCodes : result - }; - }; + if (!self._validateResult(result, start.startCounter)){ + return null; + } - CodabarReader.prototype._verifyWhitespace = function(startCounter, endCounter) { - if ((startCounter - 1 <= 0) || this._counters[startCounter-1] >= (this._calculatePatternLength(startCounter) / 2.0)) { - if ((endCounter + 8 >= this._counters.length) || this._counters[endCounter+7] >= (this._calculatePatternLength(endCounter) / 2.0)) { - return true; - } - } - return false; - }; + nextStart = nextStart > self._counters.length ? self._counters.length : nextStart; + end = start.start + self._sumCounters(start.startCounter, nextStart - 8); + + return { + code: result.join(""), + start: start.start, + end: end, + startInfo: start, + decodedCodes: result + }; +}; + +CodabarReader.prototype._verifyWhitespace = function(startCounter, endCounter) { + if ((startCounter - 1 <= 0) + || this._counters[startCounter - 1] >= (this._calculatePatternLength(startCounter) / 2.0)) { + if ((endCounter + 8 >= this._counters.length) + || this._counters[endCounter + 7] >= (this._calculatePatternLength(endCounter) / 2.0)) { + return true; + } + } + return false; +}; - CodabarReader.prototype._calculatePatternLength = function(offset) { - var i, - sum = 0; +CodabarReader.prototype._calculatePatternLength = function(offset) { + var i, + sum = 0; - for (i = offset; i < offset + 7; i++) { - sum += this._counters[i]; - } + for (i = offset; i < offset + 7; i++) { + sum += this._counters[i]; + } - return sum; - }; - - CodabarReader.prototype._thresholdResultPattern = function(result, startCounter){ - var self = this, - categorization = { - space: { - narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}, - wide: {size: 0, counts: 0, min: 0, max: Number.MAX_VALUE} - }, - bar: { - narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}, - wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE} - } - }, - kind, - cat, - i, - j, - pos = startCounter, - pattern; - - for (i = 0; i < result.length; i++){ - pattern = self._charToPattern(result[i]); - for (j = 6; j >= 0; j--) { - kind = (j & 1) === 2 ? categorization.bar : categorization.space; - cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; - cat.size += self._counters[pos + j]; - cat.counts++; - pattern >>= 1; - } - pos += 8; + return sum; +}; + +CodabarReader.prototype._thresholdResultPattern = function(result, startCounter){ + var self = this, + categorization = { + space: { + narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}, + wide: {size: 0, counts: 0, min: 0, max: Number.MAX_VALUE} + }, + bar: { + narrow: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE}, + wide: { size: 0, counts: 0, min: 0, max: Number.MAX_VALUE} } + }, + kind, + cat, + i, + j, + pos = startCounter, + pattern; + + for (i = 0; i < result.length; i++){ + pattern = self._charToPattern(result[i]); + for (j = 6; j >= 0; j--) { + kind = (j & 1) === 2 ? categorization.bar : categorization.space; + cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; + cat.size += self._counters[pos + j]; + cat.counts++; + pattern >>= 1; + } + pos += 8; + } - ["space", "bar"].forEach(function(key) { - var kind = categorization[key]; - kind.wide.min = Math.floor((kind.narrow.size/kind.narrow.counts + kind.wide.size / kind.wide.counts) / 2); - kind.narrow.max = Math.ceil(kind.wide.min); - kind.wide.max = Math.ceil((kind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / kind.wide.counts); - }); - - return categorization; - }; - - CodabarReader.prototype._charToPattern = function(char) { - var self = this, - charCode = char.charCodeAt(0), - i; - - for (i = 0; i < self.ALPHABET.length; i++) { - if (self.ALPHABET[i] === charCode){ - return self.CHARACTER_ENCODINGS[i]; - } - } - return 0x0; - }; - - CodabarReader.prototype._validateResult = function(result, startCounter) { - var self = this, - thresholds = self._thresholdResultPattern(result, startCounter), - i, - j, - kind, - cat, - size, - pos = startCounter, - pattern; - - for (i = 0; i < result.length; i++) { - pattern = self._charToPattern(result[i]); - for (j = 6; j >= 0; j--) { - kind = (j & 1) === 0 ? thresholds.bar : thresholds.space; - cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; - size = self._counters[pos + j]; - if (size < cat.min || size > cat.max) { - return false; - } - pattern >>= 1; - } - pos += 8; + ["space", "bar"].forEach(function(key) { + var newkind = categorization[key]; + newkind.wide.min = + Math.floor((newkind.narrow.size / newkind.narrow.counts + newkind.wide.size / newkind.wide.counts) / 2); + newkind.narrow.max = Math.ceil(newkind.wide.min); + newkind.wide.max = Math.ceil((newkind.wide.size * self.MAX_ACCEPTABLE + self.PADDING) / newkind.wide.counts); + }); + + return categorization; +}; + +CodabarReader.prototype._charToPattern = function(char) { + var self = this, + charCode = char.charCodeAt(0), + i; + + for (i = 0; i < self.ALPHABET.length; i++) { + if (self.ALPHABET[i] === charCode){ + return self.CHARACTER_ENCODINGS[i]; + } + } + return 0x0; +}; + +CodabarReader.prototype._validateResult = function(result, startCounter) { + var self = this, + thresholds = self._thresholdResultPattern(result, startCounter), + i, + j, + kind, + cat, + size, + pos = startCounter, + pattern; + + for (i = 0; i < result.length; i++) { + pattern = self._charToPattern(result[i]); + for (j = 6; j >= 0; j--) { + kind = (j & 1) === 0 ? thresholds.bar : thresholds.space; + cat = (pattern & 1) === 1 ? kind.wide : kind.narrow; + size = self._counters[pos + j]; + if (size < cat.min || size > cat.max) { + return false; } - return true; - }; + pattern >>= 1; + } + pos += 8; + } + return true; +}; - CodabarReader.prototype._patternToChar = function(pattern) { - var i, - self = this; +CodabarReader.prototype._patternToChar = function(pattern) { + var i, + self = this; - for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { - if (self.CHARACTER_ENCODINGS[i] === pattern) { - return String.fromCharCode(self.ALPHABET[i]); - } - } - return -1; - }; - - CodabarReader.prototype._computeAlternatingThreshold = function(offset, end) { - var i, - min = Number.MAX_VALUE, - max = 0, - counter; - - for (i = offset; i < end; i += 2){ - counter = this._counters[i]; - if (counter > max) { - max = counter; - } - if (counter < min) { - min = counter; - } - } - - return ((min + max) / 2.0) | 0; - }; - - CodabarReader.prototype._toPattern = function(offset) { - var numCounters = 7, - end = offset + numCounters, - barThreshold, - spaceThreshold, - bitmask = 1 << (numCounters - 1), - pattern = 0, - i, - threshold; - - if (end > this._counters.length) { - return -1; - } + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + return -1; +}; + +CodabarReader.prototype._computeAlternatingThreshold = function(offset, end) { + var i, + min = Number.MAX_VALUE, + max = 0, + counter; + + for (i = offset; i < end; i += 2){ + counter = this._counters[i]; + if (counter > max) { + max = counter; + } + if (counter < min) { + min = counter; + } + } - barThreshold = this._computeAlternatingThreshold(offset, end); - spaceThreshold = this._computeAlternatingThreshold(offset + 1, end); + return ((min + max) / 2.0) | 0; +}; + +CodabarReader.prototype._toPattern = function(offset) { + var numCounters = 7, + end = offset + numCounters, + barThreshold, + spaceThreshold, + bitmask = 1 << (numCounters - 1), + pattern = 0, + i, + threshold; + + if (end > this._counters.length) { + return -1; + } - for (i = 0; i < numCounters; i++){ - threshold = (i & 1) === 0 ? barThreshold : spaceThreshold; - if (this._counters[offset + i] > threshold) { - pattern |= bitmask; - } - bitmask >>= 1; - } + barThreshold = this._computeAlternatingThreshold(offset, end); + spaceThreshold = this._computeAlternatingThreshold(offset + 1, end); - return pattern; - }; + for (i = 0; i < numCounters; i++){ + threshold = (i & 1) === 0 ? barThreshold : spaceThreshold; + if (this._counters[offset + i] > threshold) { + pattern |= bitmask; + } + bitmask >>= 1; + } - CodabarReader.prototype._isStartEnd = function(pattern) { - var i; + return pattern; +}; - for (i = 0; i < this.START_END.length; i++) { - if (this.START_END[i] === pattern) { - return true; - } - } - return false; - }; +CodabarReader.prototype._isStartEnd = function(pattern) { + var i; - CodabarReader.prototype._sumCounters = function(start, end) { - var i, - sum = 0; + for (i = 0; i < this.START_END.length; i++) { + if (this.START_END[i] === pattern) { + return true; + } + } + return false; +}; - for (i = start; i < end; i++) { - sum += this._counters[i]; - } - return sum; - }; - - CodabarReader.prototype._findStart = function() { - var self = this, - i, - pattern, - start = self._nextUnset(self._row), - end; - - for (i = 1; i < this._counters.length; i++) { - pattern = self._toPattern(i); - if (pattern !== -1 && self._isStartEnd(pattern)) { - // TODO: Look for whitespace ahead - start += self._sumCounters(0, i); - end = start + self._sumCounters(i, i + 8); - return { - start: start, - end: end, - startCounter: i, - endCounter: i + 8 - }; - } - } - }; +CodabarReader.prototype._sumCounters = function(start, end) { + var i, + sum = 0; - return (CodabarReader); + for (i = start; i < end; i++) { + sum += this._counters[i]; + } + return sum; +}; + +CodabarReader.prototype._findStart = function() { + var self = this, + i, + pattern, + start = self._nextUnset(self._row), + end; + + for (i = 1; i < this._counters.length; i++) { + pattern = self._toPattern(i); + if (pattern !== -1 && self._isStartEnd(pattern)) { + // TODO: Look for whitespace ahead + start += self._sumCounters(0, i); + end = start + self._sumCounters(i, i + 8); + return { + start: start, + end: end, + startCounter: i, + endCounter: i + 8 + }; + } } -); \ No newline at end of file +}; + +export default CodabarReader; diff --git a/src/code_128_reader.js b/src/code_128_reader.js index 53275924..20980187 100644 --- a/src/code_128_reader.js +++ b/src/code_128_reader.js @@ -1,423 +1,407 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +import BarcodeReader from './barcode_reader'; -define( - [ - "./barcode_reader" - ], - function(BarcodeReader) { - "use strict"; - - function Code128Reader() { - BarcodeReader.call(this); - } - - var properties = { - CODE_SHIFT : {value: 98}, - CODE_C : {value: 99}, - CODE_B : {value: 100}, - CODE_A : {value: 101}, - START_CODE_A : {value: 103}, - START_CODE_B : {value: 104}, - START_CODE_C : {value: 105}, - STOP_CODE : {value: 106}, - MODULO : {value: 11}, - CODE_PATTERN : {value: [ - [2, 1, 2, 2, 2, 2], - [2, 2, 2, 1, 2, 2], - [2, 2, 2, 2, 2, 1], - [1, 2, 1, 2, 2, 3], - [1, 2, 1, 3, 2, 2], - [1, 3, 1, 2, 2, 2], - [1, 2, 2, 2, 1, 3], - [1, 2, 2, 3, 1, 2], - [1, 3, 2, 2, 1, 2], - [2, 2, 1, 2, 1, 3], - [2, 2, 1, 3, 1, 2], - [2, 3, 1, 2, 1, 2], - [1, 1, 2, 2, 3, 2], - [1, 2, 2, 1, 3, 2], - [1, 2, 2, 2, 3, 1], - [1, 1, 3, 2, 2, 2], - [1, 2, 3, 1, 2, 2], - [1, 2, 3, 2, 2, 1], - [2, 2, 3, 2, 1, 1], - [2, 2, 1, 1, 3, 2], - [2, 2, 1, 2, 3, 1], - [2, 1, 3, 2, 1, 2], - [2, 2, 3, 1, 1, 2], - [3, 1, 2, 1, 3, 1], - [3, 1, 1, 2, 2, 2], - [3, 2, 1, 1, 2, 2], - [3, 2, 1, 2, 2, 1], - [3, 1, 2, 2, 1, 2], - [3, 2, 2, 1, 1, 2], - [3, 2, 2, 2, 1, 1], - [2, 1, 2, 1, 2, 3], - [2, 1, 2, 3, 2, 1], - [2, 3, 2, 1, 2, 1], - [1, 1, 1, 3, 2, 3], - [1, 3, 1, 1, 2, 3], - [1, 3, 1, 3, 2, 1], - [1, 1, 2, 3, 1, 3], - [1, 3, 2, 1, 1, 3], - [1, 3, 2, 3, 1, 1], - [2, 1, 1, 3, 1, 3], - [2, 3, 1, 1, 1, 3], - [2, 3, 1, 3, 1, 1], - [1, 1, 2, 1, 3, 3], - [1, 1, 2, 3, 3, 1], - [1, 3, 2, 1, 3, 1], - [1, 1, 3, 1, 2, 3], - [1, 1, 3, 3, 2, 1], - [1, 3, 3, 1, 2, 1], - [3, 1, 3, 1, 2, 1], - [2, 1, 1, 3, 3, 1], - [2, 3, 1, 1, 3, 1], - [2, 1, 3, 1, 1, 3], - [2, 1, 3, 3, 1, 1], - [2, 1, 3, 1, 3, 1], - [3, 1, 1, 1, 2, 3], - [3, 1, 1, 3, 2, 1], - [3, 3, 1, 1, 2, 1], - [3, 1, 2, 1, 1, 3], - [3, 1, 2, 3, 1, 1], - [3, 3, 2, 1, 1, 1], - [3, 1, 4, 1, 1, 1], - [2, 2, 1, 4, 1, 1], - [4, 3, 1, 1, 1, 1], - [1, 1, 1, 2, 2, 4], - [1, 1, 1, 4, 2, 2], - [1, 2, 1, 1, 2, 4], - [1, 2, 1, 4, 2, 1], - [1, 4, 1, 1, 2, 2], - [1, 4, 1, 2, 2, 1], - [1, 1, 2, 2, 1, 4], - [1, 1, 2, 4, 1, 2], - [1, 2, 2, 1, 1, 4], - [1, 2, 2, 4, 1, 1], - [1, 4, 2, 1, 1, 2], - [1, 4, 2, 2, 1, 1], - [2, 4, 1, 2, 1, 1], - [2, 2, 1, 1, 1, 4], - [4, 1, 3, 1, 1, 1], - [2, 4, 1, 1, 1, 2], - [1, 3, 4, 1, 1, 1], - [1, 1, 1, 2, 4, 2], - [1, 2, 1, 1, 4, 2], - [1, 2, 1, 2, 4, 1], - [1, 1, 4, 2, 1, 2], - [1, 2, 4, 1, 1, 2], - [1, 2, 4, 2, 1, 1], - [4, 1, 1, 2, 1, 2], - [4, 2, 1, 1, 1, 2], - [4, 2, 1, 2, 1, 1], - [2, 1, 2, 1, 4, 1], - [2, 1, 4, 1, 2, 1], - [4, 1, 2, 1, 2, 1], - [1, 1, 1, 1, 4, 3], - [1, 1, 1, 3, 4, 1], - [1, 3, 1, 1, 4, 1], - [1, 1, 4, 1, 1, 3], - [1, 1, 4, 3, 1, 1], - [4, 1, 1, 1, 1, 3], - [4, 1, 1, 3, 1, 1], - [1, 1, 3, 1, 4, 1], - [1, 1, 4, 1, 3, 1], - [3, 1, 1, 1, 4, 1], - [4, 1, 1, 1, 3, 1], - [2, 1, 1, 4, 1, 2], - [2, 1, 1, 2, 1, 4], - [2, 1, 1, 2, 3, 2], - [2, 3, 3, 1, 1, 1, 2] - ]}, - SINGLE_CODE_ERROR: {value: 1}, - AVG_CODE_ERROR: {value: 0.5}, - FORMAT: {value: "code_128", writeable: false} - }; - - Code128Reader.prototype = Object.create(BarcodeReader.prototype, properties); - Code128Reader.prototype.constructor = Code128Reader; - - Code128Reader.prototype._decodeCode = function(start) { - var counter = [0, 0, 0, 0, 0, 0], - i, - self = this, - offset = start, - isWhite = !self._row[offset], - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : start, - end : start - }, - code, - error, - normalized; +function Code128Reader() { + BarcodeReader.call(this); +} - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < self.CODE_PATTERN.length; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - bestMatch.end = i; - return bestMatch; +var properties = { + CODE_SHIFT: {value: 98}, + CODE_C: {value: 99}, + CODE_B: {value: 100}, + CODE_A: {value: 101}, + START_CODE_A: {value: 103}, + START_CODE_B: {value: 104}, + START_CODE_C: {value: 105}, + STOP_CODE: {value: 106}, + MODULO: {value: 11}, + CODE_PATTERN: {value: [ + [2, 1, 2, 2, 2, 2], + [2, 2, 2, 1, 2, 2], + [2, 2, 2, 2, 2, 1], + [1, 2, 1, 2, 2, 3], + [1, 2, 1, 3, 2, 2], + [1, 3, 1, 2, 2, 2], + [1, 2, 2, 2, 1, 3], + [1, 2, 2, 3, 1, 2], + [1, 3, 2, 2, 1, 2], + [2, 2, 1, 2, 1, 3], + [2, 2, 1, 3, 1, 2], + [2, 3, 1, 2, 1, 2], + [1, 1, 2, 2, 3, 2], + [1, 2, 2, 1, 3, 2], + [1, 2, 2, 2, 3, 1], + [1, 1, 3, 2, 2, 2], + [1, 2, 3, 1, 2, 2], + [1, 2, 3, 2, 2, 1], + [2, 2, 3, 2, 1, 1], + [2, 2, 1, 1, 3, 2], + [2, 2, 1, 2, 3, 1], + [2, 1, 3, 2, 1, 2], + [2, 2, 3, 1, 1, 2], + [3, 1, 2, 1, 3, 1], + [3, 1, 1, 2, 2, 2], + [3, 2, 1, 1, 2, 2], + [3, 2, 1, 2, 2, 1], + [3, 1, 2, 2, 1, 2], + [3, 2, 2, 1, 1, 2], + [3, 2, 2, 2, 1, 1], + [2, 1, 2, 1, 2, 3], + [2, 1, 2, 3, 2, 1], + [2, 3, 2, 1, 2, 1], + [1, 1, 1, 3, 2, 3], + [1, 3, 1, 1, 2, 3], + [1, 3, 1, 3, 2, 1], + [1, 1, 2, 3, 1, 3], + [1, 3, 2, 1, 1, 3], + [1, 3, 2, 3, 1, 1], + [2, 1, 1, 3, 1, 3], + [2, 3, 1, 1, 1, 3], + [2, 3, 1, 3, 1, 1], + [1, 1, 2, 1, 3, 3], + [1, 1, 2, 3, 3, 1], + [1, 3, 2, 1, 3, 1], + [1, 1, 3, 1, 2, 3], + [1, 1, 3, 3, 2, 1], + [1, 3, 3, 1, 2, 1], + [3, 1, 3, 1, 2, 1], + [2, 1, 1, 3, 3, 1], + [2, 3, 1, 1, 3, 1], + [2, 1, 3, 1, 1, 3], + [2, 1, 3, 3, 1, 1], + [2, 1, 3, 1, 3, 1], + [3, 1, 1, 1, 2, 3], + [3, 1, 1, 3, 2, 1], + [3, 3, 1, 1, 2, 1], + [3, 1, 2, 1, 1, 3], + [3, 1, 2, 3, 1, 1], + [3, 3, 2, 1, 1, 1], + [3, 1, 4, 1, 1, 1], + [2, 2, 1, 4, 1, 1], + [4, 3, 1, 1, 1, 1], + [1, 1, 1, 2, 2, 4], + [1, 1, 1, 4, 2, 2], + [1, 2, 1, 1, 2, 4], + [1, 2, 1, 4, 2, 1], + [1, 4, 1, 1, 2, 2], + [1, 4, 1, 2, 2, 1], + [1, 1, 2, 2, 1, 4], + [1, 1, 2, 4, 1, 2], + [1, 2, 2, 1, 1, 4], + [1, 2, 2, 4, 1, 1], + [1, 4, 2, 1, 1, 2], + [1, 4, 2, 2, 1, 1], + [2, 4, 1, 2, 1, 1], + [2, 2, 1, 1, 1, 4], + [4, 1, 3, 1, 1, 1], + [2, 4, 1, 1, 1, 2], + [1, 3, 4, 1, 1, 1], + [1, 1, 1, 2, 4, 2], + [1, 2, 1, 1, 4, 2], + [1, 2, 1, 2, 4, 1], + [1, 1, 4, 2, 1, 2], + [1, 2, 4, 1, 1, 2], + [1, 2, 4, 2, 1, 1], + [4, 1, 1, 2, 1, 2], + [4, 2, 1, 1, 1, 2], + [4, 2, 1, 2, 1, 1], + [2, 1, 2, 1, 4, 1], + [2, 1, 4, 1, 2, 1], + [4, 1, 2, 1, 2, 1], + [1, 1, 1, 1, 4, 3], + [1, 1, 1, 3, 4, 1], + [1, 3, 1, 1, 4, 1], + [1, 1, 4, 1, 1, 3], + [1, 1, 4, 3, 1, 1], + [4, 1, 1, 1, 1, 3], + [4, 1, 1, 3, 1, 1], + [1, 1, 3, 1, 4, 1], + [1, 1, 4, 1, 3, 1], + [3, 1, 1, 1, 4, 1], + [4, 1, 1, 1, 3, 1], + [2, 1, 1, 4, 1, 2], + [2, 1, 1, 2, 1, 4], + [2, 1, 1, 2, 3, 2], + [2, 3, 3, 1, 1, 1, 2] + ]}, + SINGLE_CODE_ERROR: {value: 1}, + AVG_CODE_ERROR: {value: 0.5}, + FORMAT: {value: "code_128", writeable: false} +}; + +Code128Reader.prototype = Object.create(BarcodeReader.prototype, properties); +Code128Reader.prototype.constructor = Code128Reader; + +Code128Reader.prototype._decodeCode = function(start) { + var counter = [0, 0, 0, 0, 0, 0], + i, + self = this, + offset = start, + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: start, + end: start + }, + code, + error, + normalized; + + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; } - } else { - counterPos++; } - counter[counterPos] = 1; - isWhite = !isWhite; + bestMatch.end = i; + return bestMatch; } + } else { + counterPos++; } - return null; - }; + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; +}; - Code128Reader.prototype._findStart = function() { - var counter = [0, 0, 0, 0, 0, 0], - i, - self = this, - offset = self._nextSet(self._row), - isWhite = false, - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }, - code, - error, - j, - sum, - normalized; - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - sum = 0; - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - if (bestMatch.error < self.AVG_CODE_ERROR) { - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } - } +Code128Reader.prototype._findStart = function() { + var counter = [0, 0, 0, 0, 0, 0], + i, + self = this, + offset = self._nextSet(self._row), + isWhite = false, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + code, + error, + j, + sum, + normalized; - for ( j = 0; j < 4; j++) { - counter[j] = counter[j + 2]; + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for ( j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; } - counter[4] = 0; - counter[5] = 0; - counterPos--; - } else { - counterPos++; } - counter[counterPos] = 1; - isWhite = !isWhite; + if (bestMatch.error < self.AVG_CODE_ERROR) { + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } } + + for ( j = 0; j < 4; j++) { + counter[j] = counter[j + 2]; + } + counter[4] = 0; + counter[5] = 0; + counterPos--; + } else { + counterPos++; } - return null; - }; + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; +}; - Code128Reader.prototype._decode = function() { - var self = this, - startInfo = self._findStart(), - code = null, - done = false, - result = [], - multiplier = 0, - checksum = 0, - codeset, - rawResult = [], - decodedCodes = [], - shiftNext = false, - unshift, - lastCharacterWasPrintable; +Code128Reader.prototype._decode = function() { + var self = this, + startInfo = self._findStart(), + code = null, + done = false, + result = [], + multiplier = 0, + checksum = 0, + codeset, + rawResult = [], + decodedCodes = [], + shiftNext = false, + unshift; - if (startInfo === null) { - return null; + if (startInfo === null) { + return null; + } + code = { + code: startInfo.code, + start: startInfo.start, + end: startInfo.end + }; + decodedCodes.push(code); + checksum = code.code; + switch (code.code) { + case self.START_CODE_A: + codeset = self.CODE_A; + break; + case self.START_CODE_B: + codeset = self.CODE_B; + break; + case self.START_CODE_C: + codeset = self.CODE_C; + break; + default: + return null; + } + + while (!done) { + unshift = shiftNext; + shiftNext = false; + code = self._decodeCode(code.end); + if (code !== null) { + if (code.code !== self.STOP_CODE) { + rawResult.push(code.code); + multiplier++; + checksum += multiplier * code.code; } - code = { - code : startInfo.code, - start : startInfo.start, - end : startInfo.end - }; decodedCodes.push(code); - checksum = code.code; - switch(code.code) { - case self.START_CODE_A: - codeset = self.CODE_A; - break; - case self.START_CODE_B: - codeset = self.CODE_B; - break; - case self.START_CODE_C: - codeset = self.CODE_C; - break; - default: - return null; - } - - while (!done) { - unshift = shiftNext; - shiftNext = false; - code = self._decodeCode(code.end); - if (code !== null) { - if (code.code !== self.STOP_CODE) { - rawResult.push(code.code); - multiplier++; - checksum += multiplier * code.code; - } - decodedCodes.push(code); - switch(codeset) { - case self.CODE_A: - if (code.code < 64) { - result.push(String.fromCharCode(32 + code.code)); - } else if (code.code < 96) { - result.push(String.fromCharCode(code.code - 64)); - } else { - switch (code.code) { - case self.CODE_SHIFT: - shiftNext = true; - codeset = self.CODE_B; - break; - case self.CODE_B: - codeset = self.CODE_B; - break; - case self.CODE_C: - codeset = self.CODE_C; - break; - case self.STOP_CODE: - done = true; - break; - } - } + switch (codeset) { + case self.CODE_A: + if (code.code < 64) { + result.push(String.fromCharCode(32 + code.code)); + } else if (code.code < 96) { + result.push(String.fromCharCode(code.code - 64)); + } else { + switch (code.code) { + case self.CODE_SHIFT: + shiftNext = true; + codeset = self.CODE_B; break; case self.CODE_B: - if (code.code < 96) { - result.push(String.fromCharCode(32 + code.code)); - } else { - if (code.code != self.STOP_CODE) { - lastCharacterWasPrintable = false; - } - switch (code.code) { - case self.CODE_SHIFT: - shiftNext = true; - codeset = self.CODE_A; - break; - case self.CODE_A: - codeset = self.CODE_A; - break; - case self.CODE_C: - codeset = self.CODE_C; - break; - case self.STOP_CODE: - done = true; - break; - } - } + codeset = self.CODE_B; break; case self.CODE_C: - if (code.code < 100) { - result.push(code.code < 10 ? "0" + code.code : code.code); - } - switch (code.code) { - case self.CODE_A: - codeset = self.CODE_A; - break; - case self.CODE_B: - codeset = self.CODE_B; - break; - case self.STOP_CODE: - done = true; - break; - } + codeset = self.CODE_C; + break; + case self.STOP_CODE: + done = true; break; } + } + break; + case self.CODE_B: + if (code.code < 96) { + result.push(String.fromCharCode(32 + code.code)); } else { - done = true; + switch (code.code) { + case self.CODE_SHIFT: + shiftNext = true; + codeset = self.CODE_A; + break; + case self.CODE_A: + codeset = self.CODE_A; + break; + case self.CODE_C: + codeset = self.CODE_C; + break; + case self.STOP_CODE: + done = true; + break; + } } - if (unshift) { - codeset = codeset == self.CODE_A ? self.CODE_B : self.CODE_A; + break; + case self.CODE_C: + if (code.code < 100) { + result.push(code.code < 10 ? "0" + code.code : code.code); } + switch (code.code) { + case self.CODE_A: + codeset = self.CODE_A; + break; + case self.CODE_B: + codeset = self.CODE_B; + break; + case self.STOP_CODE: + done = true; + break; + } + break; } + } else { + done = true; + } + if (unshift) { + codeset = codeset === self.CODE_A ? self.CODE_B : self.CODE_A; + } + } - if (code === null) { - return null; - } - - // find end bar - code.end = self._nextUnset(self._row, code.end); - if(!self._verifyTrailingWhitespace(code)){ - return null; - } - - // checksum - // Does not work correctly yet!!! startcode - endcode? - checksum -= multiplier * rawResult[rawResult.length - 1]; - if (checksum % 103 != rawResult[rawResult.length - 1]) { - return null; - } + if (code === null) { + return null; + } - if (!result.length) { - return null; - } + // find end bar + code.end = self._nextUnset(self._row, code.end); + if (!self._verifyTrailingWhitespace(code)){ + return null; + } - // remove last code from result (checksum) - result.splice(result.length - 1, 1); + // checksum + // Does not work correctly yet!!! startcode - endcode? + checksum -= multiplier * rawResult[rawResult.length - 1]; + if (checksum % 103 !== rawResult[rawResult.length - 1]) { + return null; + } + if (!result.length) { + return null; + } + // remove last code from result (checksum) + result.splice(result.length - 1, 1); - return { - code : result.join(""), - start : startInfo.start, - end : code.end, - codeset : codeset, - startInfo : startInfo, - decodedCodes : decodedCodes, - endInfo : code - }; - }; + return { + code: result.join(""), + start: startInfo.start, + end: code.end, + codeset: codeset, + startInfo: startInfo, + decodedCodes: decodedCodes, + endInfo: code + }; +}; - BarcodeReader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; +BarcodeReader.prototype._verifyTrailingWhitespace = function(endInfo) { + var self = this, + trailingWhitespaceEnd; - trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } - } - return null; - }; - - return (Code128Reader); + trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } } -); \ No newline at end of file + return null; +}; + +export default Code128Reader; diff --git a/src/code_39_reader.js b/src/code_39_reader.js index a02fee53..03ba5288 100644 --- a/src/code_39_reader.js +++ b/src/code_39_reader.js @@ -1,221 +1,214 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./barcode_reader", - "./array_helper" - ], - function(BarcodeReader, ArrayHelper) { - "use strict"; - - function Code39Reader() { - BarcodeReader.call(this); +import BarcodeReader from './barcode_reader'; +import ArrayHelper from './array_helper'; + +function Code39Reader() { + BarcodeReader.call(this); +} + +var properties = { + ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, + ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]}, + CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, + 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, + 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A + ]}, + ASTERISK: {value: 0x094}, + FORMAT: {value: "code_39", writeable: false} +}; + +Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); +Code39Reader.prototype.constructor = Code39Reader; + +Code39Reader.prototype._toCounters = function(start, counter) { + var self = this, + numCounters = counter.length, + end = self._row.length, + isWhite = !self._row[start], + i, + counterPos = 0; + + ArrayHelper.init(counter, 0); + + for ( i = start; i < end; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + if (counterPos === numCounters) { + break; + } else { + counter[counterPos] = 1; + isWhite = !isWhite; + } } + } - var properties = { - ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, - ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]}, - CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A]}, - ASTERISK: {value: 0x094}, - FORMAT: {value: "code_39", writeable: false} - }; - - Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); - Code39Reader.prototype.constructor = Code39Reader; - - Code39Reader.prototype._toCounters = function(start, counter) { - var self = this, - numCounters = counter.length, - end = self._row.length, - isWhite = !self._row[start], - i, - counterPos = 0; - - ArrayHelper.init(counter, 0); - - for ( i = start; i < end; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - counterPos++; - if (counterPos === numCounters) { - break; - } else { - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - } + return counter; +}; + +Code39Reader.prototype._decode = function() { + var self = this, + counters = [0, 0, 0, 0, 0, 0, 0, 0, 0], + result = [], + start = self._findStart(), + decodedChar, + lastStart, + pattern, + nextStart; + + if (!start) { + return null; + } + nextStart = self._nextSet(self._row, start.end); - return counter; - }; - - Code39Reader.prototype._decode = function() { - var self = this, - counters = [0,0,0,0,0,0,0,0,0], - result = [], - start = self._findStart(), - decodedChar, - lastStart, - pattern, - nextStart; - - if (!start) { - return null; - } - nextStart = self._nextSet(self._row, start.end); + do { + counters = self._toCounters(nextStart, counters); + pattern = self._toPattern(counters); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + if (decodedChar < 0){ + return null; + } + result.push(decodedChar); + lastStart = nextStart; + nextStart += ArrayHelper.sum(counters); + nextStart = self._nextSet(self._row, nextStart); + } while (decodedChar !== '*'); + result.pop(); + + if (!result.length) { + return null; + } - do { - counters = self._toCounters(nextStart, counters); - pattern = self._toPattern(counters); - if (pattern < 0) { - return null; - } - decodedChar = self._patternToChar(pattern); - if (decodedChar < 0){ - return null; - } - result.push(decodedChar); - lastStart = nextStart; - nextStart += ArrayHelper.sum(counters); - nextStart = self._nextSet(self._row, nextStart); - } while(decodedChar !== '*'); - result.pop(); - - if (!result.length) { - return null; - } + if (!self._verifyTrailingWhitespace(lastStart, nextStart, counters)) { + return null; + } - if(!self._verifyTrailingWhitespace(lastStart, nextStart, counters)) { - return null; - } + return { + code: result.join(""), + start: start.start, + end: nextStart, + startInfo: start, + decodedCodes: result + }; +}; + +Code39Reader.prototype._verifyTrailingWhitespace = function(lastStart, nextStart, counters) { + var trailingWhitespaceEnd, + patternSize = ArrayHelper.sum(counters); + + trailingWhitespaceEnd = nextStart - lastStart - patternSize; + if ((trailingWhitespaceEnd * 3) >= patternSize) { + return true; + } + return false; +}; - return { - code : result.join(""), - start : start.start, - end : nextStart, - startInfo : start, - decodedCodes : result - }; - }; - - Code39Reader.prototype._verifyTrailingWhitespace = function(lastStart, nextStart, counters) { - var trailingWhitespaceEnd, - patternSize = ArrayHelper.sum(counters); - - trailingWhitespaceEnd = nextStart - lastStart - patternSize; - if ((trailingWhitespaceEnd * 3) >= patternSize) { - return true; - } - return false; - }; +Code39Reader.prototype._patternToChar = function(pattern) { + var i, + self = this; - Code39Reader.prototype._patternToChar = function(pattern) { - var i, - self = this; + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } +}; - for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { - if (self.CHARACTER_ENCODINGS[i] === pattern) { - return String.fromCharCode(self.ALPHABET[i]); - } - } - }; +Code39Reader.prototype._findNextWidth = function(counters, current) { + var i, + minWidth = Number.MAX_VALUE; - Code39Reader.prototype._findNextWidth = function(counters, current) { - var i, - minWidth = Number.MAX_VALUE; + for (i = 0; i < counters.length; i++) { + if (counters[i] < minWidth && counters[i] > current) { + minWidth = counters[i]; + } + } - for (i = 0; i < counters.length; i++) { - if (counters[i] < minWidth && counters[i] > current) { - minWidth = counters[i]; - } + return minWidth; +}; + +Code39Reader.prototype._toPattern = function(counters) { + var numCounters = counters.length, + maxNarrowWidth = 0, + numWideBars = numCounters, + wideBarWidth = 0, + self = this, + pattern, + i; + + while (numWideBars > 3) { + maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); + numWideBars = 0; + pattern = 0; + for (i = 0; i < numCounters; i++) { + if (counters[i] > maxNarrowWidth) { + pattern |= 1 << (numCounters - 1 - i); + numWideBars++; + wideBarWidth += counters[i]; } + } - return minWidth; - }; - - Code39Reader.prototype._toPattern = function(counters) { - var numCounters = counters.length, - maxNarrowWidth = 0, - numWideBars = numCounters, - wideBarWidth = 0, - self = this, - pattern, - i; - - while(numWideBars > 3) { - maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); - numWideBars = 0; - pattern = 0; - for (i = 0; i < numCounters; i++) { - if (counters[i] > maxNarrowWidth) { - pattern |= 1 << (numCounters - 1 - i); - numWideBars++; - wideBarWidth += counters[i]; - } - } - - if (numWideBars === 3) { - for (i = 0; i < numCounters && numWideBars > 0; i++) { - if (counters[i] > maxNarrowWidth) { - numWideBars--; - if ((counters[i] * 2) >= wideBarWidth) { - return -1; - } - } + if (numWideBars === 3) { + for (i = 0; i < numCounters && numWideBars > 0; i++) { + if (counters[i] > maxNarrowWidth) { + numWideBars--; + if ((counters[i] * 2) >= wideBarWidth) { + return -1; } - return pattern; } } - return -1; - }; - - Code39Reader.prototype._findStart = function() { - var self = this, - offset = self._nextSet(self._row), - patternStart = offset, - counter = [0,0,0,0,0,0,0,0,0], - counterPos = 0, - isWhite = false, - i, - j, - whiteSpaceMustStart; - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - - // find start pattern - if (self._toPattern(counter) === self.ASTERISK) { - whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4))); - if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { - return { - start: patternStart, - end: i - }; - } - } - - patternStart += counter[0] + counter[1]; - for ( j = 0; j < 7; j++) { - counter[j] = counter[j + 2]; - } - counter[7] = 0; - counter[8] = 0; - counterPos--; - } else { - counterPos++; + return pattern; + } + } + return -1; +}; + +Code39Reader.prototype._findStart = function() { + var self = this, + offset = self._nextSet(self._row), + patternStart = offset, + counter = [0, 0, 0, 0, 0, 0, 0, 0, 0], + counterPos = 0, + isWhite = false, + i, + j, + whiteSpaceMustStart; + + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + // find start pattern + if (self._toPattern(counter) === self.ASTERISK) { + whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4))); + if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { + return { + start: patternStart, + end: i + }; } - counter[counterPos] = 1; - isWhite = !isWhite; } - } - return null; - }; - return (Code39Reader); + patternStart += counter[0] + counter[1]; + for ( j = 0; j < 7; j++) { + counter[j] = counter[j + 2]; + } + counter[7] = 0; + counter[8] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } } -); \ No newline at end of file + return null; +}; + +export default Code39Reader; diff --git a/src/code_39_vin_reader.js b/src/code_39_vin_reader.js index 8dca2598..ae61bd6a 100644 --- a/src/code_39_vin_reader.js +++ b/src/code_39_vin_reader.js @@ -1,59 +1,49 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./code_39_reader" - ], - function(Code39Reader) { - "use strict"; - - function Code39VINReader() { - Code39Reader.call(this); - } - - var patterns = { - IOQ: /[IOQ]/g, - AZ09: /[A-Z0-9]{17}/ - }; - - Code39VINReader.prototype = Object.create(Code39Reader.prototype); - Code39VINReader.prototype.constructor = Code39VINReader; - - // Cribbed from: - // https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java - Code39VINReader.prototype._decode = function() { - var result = Code39Reader.prototype._decode.apply(this); - if (!result) { - return null; - } - - var code = result.code; - - if (!code) { - return; - } - - code = code.replace(patterns.IOQ, ''); - - if (!code.match(patterns.AZ09)) { - console.log('Failed AZ09 pattern code:', code); - return null; - } - - if (!this._checkChecksum(code)) { - return null; - } - - result.code = code; - return result; - }; - - Code39VINReader.prototype._checkChecksum = function(code) { - // TODO - return !!code; - }; - - return (Code39VINReader); +import Code39Reader from './code_39_reader'; + +function Code39VINReader() { + Code39Reader.call(this); +} + +var patterns = { + IOQ: /[IOQ]/g, + AZ09: /[A-Z0-9]{17}/ +}; + +Code39VINReader.prototype = Object.create(Code39Reader.prototype); +Code39VINReader.prototype.constructor = Code39VINReader; + +// Cribbed from: +// https://github.com/zxing/zxing/blob/master/core/src/main/java/com/google/zxing/client/result/VINResultParser.java +Code39VINReader.prototype._decode = function() { + var result = Code39Reader.prototype._decode.apply(this); + if (!result) { + return null; } -); + + var code = result.code; + + if (!code) { + return null; + } + + code = code.replace(patterns.IOQ, ''); + + if (!code.match(patterns.AZ09)) { + console.log('Failed AZ09 pattern code:', code); + return null; + } + + if (!this._checkChecksum(code)) { + return null; + } + + result.code = code; + return result; +}; + +Code39VINReader.prototype._checkChecksum = function(code) { + // TODO + return !!code; +}; + +export default Code39VINReader; diff --git a/src/config.js b/src/config.js index 72361805..1212877c 100644 --- a/src/config.js +++ b/src/config.js @@ -1,44 +1,40 @@ -/** - * The basic configuration - */ - -define(function(){ - var config = { - inputStream: { name: "Live", - type: "LiveStream", - constraints: { - width: 640, - height: 480, - minAspectRatio: 0, - maxAspectRatio: 100, - facing: "environment" // or user - }, - area: { - top: "0%", - right: "0%", - left: "0%", - bottom: "0%" - }, - singleChannel: false // true: only the red color-channel is read - }, - tracking: false, - debug: false, - controls: false, - locate: true, - numOfWorkers: 4, - visual: { +export default { + inputStream: { + name: "Live", + type: "LiveStream", + constraints: { + width: 640, + height: 480, + minAspectRatio: 0, + maxAspectRatio: 100, + facing: "environment" // or user + }, + area: { + top: "0%", + right: "0%", + left: "0%", + bottom: "0%" + }, + singleChannel: false // true: only the red color-channel is read + }, + tracking: false, + debug: false, + controls: false, + locate: true, + numOfWorkers: 4, + visual: { show: true - }, - decoder:{ + }, + decoder: { drawBoundingBox: false, showFrequency: false, drawScanline: false, showPattern: false, readers: [ - 'code_128_reader' + 'code_128_reader' ] - }, - locator: { + }, + locator: { halfSample: true, patchSize: "medium", // x-small, small, medium, large, x-large showCanvas: false, @@ -49,12 +45,9 @@ define(function(){ showPatchLabels: false, showRemainingPatchLabels: false, boxFromPatches: { - showTransformed: false, - showTransformedBox: false, - showBB: false + showTransformed: false, + showTransformedBox: false, + showBB: false } - } - }; - - return config; -}); + } +}; diff --git a/src/cv_utils.js b/src/cv_utils.js index 01155d53..18485fd6 100644 --- a/src/cv_utils.js +++ b/src/cv_utils.js @@ -1,719 +1,750 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(['cluster', "array_helper", "gl-matrix"], function(Cluster2, ArrayHelper, glMatrix) { - - "use strict"; - /* - * cv_utils.js - * Collection of CV functions and libraries - */ - - /** - * Namespace for various CV alorithms - * @class Represents a collection of useful CV algorithms/functions - */ - - var CVUtils = {}, - vec2 = glMatrix.vec2, - vec3 = glMatrix.vec3; - - /** - * @param x x-coordinate - * @param y y-coordinate - * @return ImageReference {x,y} Coordinate - */ - CVUtils.imageRef = function(x, y) { - var that = { - x : x, - y : y, - toVec2 : function() { - return vec2.clone([this.x, this.y]); - }, - toVec3 : function() { - return vec3.clone([this.x, this.y, 1]); - }, - round : function() { - this.x = this.x > 0.0 ? Math.floor(this.x + 0.5) : Math.floor(this.x - 0.5); - this.y = this.y > 0.0 ? Math.floor(this.y + 0.5) : Math.floor(this.y - 0.5); - return this; - } - }; - return that; - }; - - /** - * Computes an integral image of a given grayscale image. - * @param imageDataContainer {ImageDataContainer} the image to be integrated - */ - CVUtils.computeIntegralImage2 = function(imageWrapper, integralWrapper) { - var imageData = imageWrapper.data; - var width = imageWrapper.size.x; - var height = imageWrapper.size.y; - var integralImageData = integralWrapper.data; - var sum = 0, posA = 0, posB = 0, posC = 0, posD = 0, x, y; - - // sum up first column - posB = width; - sum = 0; - for ( y = 1; y < height; y++) { - sum += imageData[posA]; - integralImageData[posB] += sum; - posA += width; - posB += width; +import Cluster2 from './cluster'; +import ArrayHelper from './array_helper'; +import {vec2, vec3} from 'gl-matrix'; + +var CVUtils = {}; + +/** + * @param x x-coordinate + * @param y y-coordinate + * @return ImageReference {x,y} Coordinate + */ +CVUtils.imageRef = function(x, y) { + var that = { + x: x, + y: y, + toVec2: function() { + return vec2.clone([this.x, this.y]); + }, + toVec3: function() { + return vec3.clone([this.x, this.y, 1]); + }, + round: function() { + this.x = this.x > 0.0 ? Math.floor(this.x + 0.5) : Math.floor(this.x - 0.5); + this.y = this.y > 0.0 ? Math.floor(this.y + 0.5) : Math.floor(this.y - 0.5); + return this; } - - posA = 0; - posB = 1; - sum = 0; + }; + return that; +}; + +/** + * Computes an integral image of a given grayscale image. + * @param imageDataContainer {ImageDataContainer} the image to be integrated + */ +CVUtils.computeIntegralImage2 = function(imageWrapper, integralWrapper) { + var imageData = imageWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0, posA = 0, posB = 0, posC = 0, posD = 0, x, y; + + // sum up first column + posB = width; + sum = 0; + for ( y = 1; y < height; y++) { + sum += imageData[posA]; + integralImageData[posB] += sum; + posA += width; + posB += width; + } + + posA = 0; + posB = 1; + sum = 0; + for ( x = 1; x < width; x++) { + sum += imageData[posA]; + integralImageData[posB] += sum; + posA++; + posB++; + } + + for ( y = 1; y < height; y++) { + posA = y * width + 1; + posB = (y - 1) * width + 1; + posC = y * width; + posD = (y - 1) * width; for ( x = 1; x < width; x++) { - sum += imageData[posA]; - integralImageData[posB] += sum; + integralImageData[posA] += + imageData[posA] + integralImageData[posB] + integralImageData[posC] - integralImageData[posD]; posA++; posB++; + posC++; + posD++; + } + } +}; + +CVUtils.computeIntegralImage = function(imageWrapper, integralWrapper) { + var imageData = imageWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0; + + // sum up first row + for (var i = 0; i < width; i++) { + sum += imageData[i]; + integralImageData[i] = sum; + } + + for (var v = 1; v < height; v++) { + sum = 0; + for (var u = 0; u < width; u++) { + sum += imageData[v * width + u]; + integralImageData[((v) * width) + u] = sum + integralImageData[(v - 1) * width + u]; + } + } +}; + +CVUtils.thresholdImage = function(imageWrapper, threshold, targetWrapper) { + if (!targetWrapper) { + targetWrapper = imageWrapper; + } + var imageData = imageWrapper.data, length = imageData.length, targetData = targetWrapper.data; + + while (length--) { + targetData[length] = imageData[length] < threshold ? 1 : 0; + } +}; + +CVUtils.computeHistogram = function(imageWrapper, bitsPerPixel) { + if (!bitsPerPixel) { + bitsPerPixel = 8; + } + var imageData = imageWrapper.data, + length = imageData.length, + bitShift = 8 - bitsPerPixel, + bucketCnt = 1 << bitsPerPixel, + hist = new Int32Array(bucketCnt); + + while (length--) { + hist[imageData[length] >> bitShift]++; + } + return hist; +}; + +CVUtils.sharpenLine = function(line) { + var i, + length = line.length, + left = line[0], + center = line[1], + right; + + for (i = 1; i < length - 1; i++) { + right = line[i + 1]; + // -1 4 -1 kernel + line[i - 1] = (((center * 2) - left - right)) & 255; + left = center; + center = right; + } + return line; +}; + +CVUtils.determineOtsuThreshold = function(imageWrapper, bitsPerPixel) { + if (!bitsPerPixel) { + bitsPerPixel = 8; + } + var hist, + threshold, + bitShift = 8 - bitsPerPixel; + + function px(init, end) { + var sum = 0, i; + for ( i = init; i <= end; i++) { + sum += hist[i]; } + return sum; + } - for ( y = 1; y < height; y++) { - posA = y * width + 1; - posB = (y - 1) * width + 1; - posC = y * width; - posD = (y - 1) * width; - for ( x = 1; x < width; x++) { - integralImageData[posA] += imageData[posA] + integralImageData[posB] + integralImageData[posC] - integralImageData[posD]; - posA++; - posB++; - posC++; - posD++; - } - } - }; - - CVUtils.computeIntegralImage = function(imageWrapper, integralWrapper) { - var imageData = imageWrapper.data; - var width = imageWrapper.size.x; - var height = imageWrapper.size.y; - var integralImageData = integralWrapper.data; - var sum = 0; - - // sum up first row - for (var i = 0; i < width; i++) { - sum += imageData[i]; - integralImageData[i] = sum; - } - - for (var v = 1; v < height; v++) { - sum = 0; - for (var u = 0; u < width; u++) { - sum += imageData[v * width + u]; - integralImageData[((v) * width) + u] = sum + integralImageData[(v - 1) * width + u]; - } - } - }; - - CVUtils.thresholdImage = function(imageWrapper, threshold, targetWrapper) { - if (!targetWrapper) { - targetWrapper = imageWrapper; - } - var imageData = imageWrapper.data, length = imageData.length, targetData = targetWrapper.data; - - while (length--) { - targetData[length] = imageData[length] < threshold ? 1 : 0; - } - }; + function mx(init, end) { + var i, sum = 0; - CVUtils.computeHistogram = function(imageWrapper, bitsPerPixel) { - if (!bitsPerPixel) { - bitsPerPixel = 8; - } - var imageData = imageWrapper.data, - length = imageData.length, - bitShift = 8 - bitsPerPixel, - bucketCnt = 1 << bitsPerPixel, - hist = new Int32Array(bucketCnt); - - while (length--) { - hist[imageData[length] >> bitShift]++; + for ( i = init; i <= end; i++) { + sum += i * hist[i]; } - return hist; - }; - CVUtils.sharpenLine = function(line) { - var i, - length = line.length, - left = line[0], - center = line[1], - right; - - for (i = 1; i < length - 1; i++) { - right = line[i + 1]; - // -1 4 -1 kernel - line[i-1] = (((center * 2) - left - right)) & 255; - left = center; - center = right; - } - return line; - }; - - CVUtils.determineOtsuThreshold = function(imageWrapper, bitsPerPixel) { - if (!bitsPerPixel) { - bitsPerPixel = 8; - } - var hist, - threshold, - bitShift = 8 - bitsPerPixel; - - function px(init, end) { - var sum = 0, i; - for ( i = init; i <= end; i++) { - sum += hist[i]; + return sum; + } + + function determineThreshold() { + var vet = [0], p1, p2, p12, k, m1, m2, m12, + max = (1 << bitsPerPixel) - 1; + + hist = CVUtils.computeHistogram(imageWrapper, bitsPerPixel); + for ( k = 1; k < max; k++) { + p1 = px(0, k); + p2 = px(k + 1, max); + p12 = p1 * p2; + if (p12 === 0) { + p12 = 1; } - return sum; - } - - function mx(init, end) { - var i, sum = 0; - - for ( i = init; i <= end; i++) { - sum += i * hist[i]; + m1 = mx(0, k) * p2; + m2 = mx(k + 1, max) * p1; + m12 = m1 - m2; + vet[k] = m12 * m12 / p12; + } + return ArrayHelper.maxIndex(vet); + } + + threshold = determineThreshold(); + return threshold << bitShift; +}; + +CVUtils.otsuThreshold = function(imageWrapper, targetWrapper) { + var threshold = CVUtils.determineOtsuThreshold(imageWrapper); + + CVUtils.thresholdImage(imageWrapper, threshold, targetWrapper); + return threshold; +}; + +// local thresholding +CVUtils.computeBinaryImage = function(imageWrapper, integralWrapper, targetWrapper) { + CVUtils.computeIntegralImage(imageWrapper, integralWrapper); + + if (!targetWrapper) { + targetWrapper = imageWrapper; + } + var imageData = imageWrapper.data; + var targetData = targetWrapper.data; + var width = imageWrapper.size.x; + var height = imageWrapper.size.y; + var integralImageData = integralWrapper.data; + var sum = 0, v, u, kernel = 3, A, B, C, D, avg, size = (kernel * 2 + 1) * (kernel * 2 + 1); + + // clear out top & bottom-border + for ( v = 0; v <= kernel; v++) { + for ( u = 0; u < width; u++) { + targetData[((v) * width) + u] = 0; + targetData[(((height - 1) - v) * width) + u] = 0; + } + } + + // clear out left & right border + for ( v = kernel; v < height - kernel; v++) { + for ( u = 0; u <= kernel; u++) { + targetData[((v) * width) + u] = 0; + targetData[((v) * width) + (width - 1 - u)] = 0; + } + } + + for ( v = kernel + 1; v < height - kernel - 1; v++) { + for ( u = kernel + 1; u < width - kernel; u++) { + A = integralImageData[(v - kernel - 1) * width + (u - kernel - 1)]; + B = integralImageData[(v - kernel - 1) * width + (u + kernel)]; + C = integralImageData[(v + kernel) * width + (u - kernel - 1)]; + D = integralImageData[(v + kernel) * width + (u + kernel)]; + sum = D - C - B + A; + avg = sum / (size); + targetData[v * width + u] = imageData[v * width + u] > (avg + 5) ? 0 : 1; + } + } +}; + +CVUtils.cluster = function(points, threshold, property) { + var i, k, cluster, point, clusters = []; + + if (!property) { + property = "rad"; + } + + function addToCluster(newPoint) { + var found = false; + for ( k = 0; k < clusters.length; k++) { + cluster = clusters[k]; + if (cluster.fits(newPoint)) { + cluster.add(newPoint); + found = true; } - - return sum; } + return found; + } - function determineThreshold() { - var vet = [0], p1, p2, p12, k, m1, m2, m12, - max = (1 << bitsPerPixel) - 1; - - hist = CVUtils.computeHistogram(imageWrapper, bitsPerPixel); - for ( k = 1; k < max; k++) { - p1 = px(0, k); - p2 = px(k + 1, max); - p12 = p1 * p2; - if (p12 === 0) { - p12 = 1; - } - m1 = mx(0, k) * p2; - m2 = mx(k + 1, max) * p1; - m12 = m1 - m2; - vet[k] = m12 * m12 / p12; - } - return ArrayHelper.maxIndex(vet); + // iterate over each cloud + for ( i = 0; i < points.length; i++) { + point = Cluster2.createPoint(points[i], i, property); + if (!addToCluster(point)) { + clusters.push(Cluster2.create(point, threshold)); } + } + return clusters; +}; - threshold = determineThreshold(); - return threshold << bitShift; - }; +CVUtils.Tracer = { + trace: function(points, vec) { + var iteration, maxIterations = 10, top = [], result = [], centerPos = 0, currentPos = 0; - CVUtils.otsuThreshold = function(imageWrapper, targetWrapper) { - var threshold = CVUtils.determineOtsuThreshold(imageWrapper); - - CVUtils.thresholdImage(imageWrapper, threshold, targetWrapper); - return threshold; - }; + function trace(idx, forward) { + var from, to, toIdx, predictedPos, thresholdX = 1, thresholdY = Math.abs(vec[1] / 10), found = false; - // local thresholding - CVUtils.computeBinaryImage = function(imageWrapper, integralWrapper, targetWrapper) { - CVUtils.computeIntegralImage(imageWrapper, integralWrapper); - - if (!targetWrapper) { - targetWrapper = imageWrapper; - } - var imageData = imageWrapper.data; - var targetData = targetWrapper.data; - var width = imageWrapper.size.x; - var height = imageWrapper.size.y; - var integralImageData = integralWrapper.data; - var sum = 0, v, u, kernel = 3, A, B, C, D, avg, size = (kernel * 2 + 1) * (kernel * 2 + 1); - - // clear out top & bottom-border - for ( v = 0; v <= kernel; v++) { - for ( u = 0; u < width; u++) { - targetData[((v) * width) + u] = 0; - targetData[(((height - 1) - v) * width) + u] = 0; + function match(pos, predicted) { + if (pos.x > (predicted.x - thresholdX) + && pos.x < (predicted.x + thresholdX) + && pos.y > (predicted.y - thresholdY) + && pos.y < (predicted.y + thresholdY)) { + return true; + } else { + return false; + } } - } - // clear out left & right border - for ( v = kernel; v < height - kernel; v++) { - for ( u = 0; u <= kernel; u++) { - targetData[((v) * width) + u] = 0; - targetData[((v) * width) + (width - 1 - u)] = 0; - } - } + // check if the next index is within the vec specifications + // if not, check as long as the threshold is met - for ( v = kernel + 1; v < height - kernel - 1; v++) { - for ( u = kernel + 1; u < width - kernel; u++) { - A = integralImageData[(v - kernel - 1) * width + (u - kernel - 1)]; - B = integralImageData[(v - kernel - 1) * width + (u + kernel)]; - C = integralImageData[(v + kernel) * width + (u - kernel - 1)]; - D = integralImageData[(v + kernel) * width + (u + kernel)]; - sum = D - C - B + A; - avg = sum / (size); - targetData[v * width + u] = imageData[v * width + u] > (avg + 5) ? 0 : 1; + from = points[idx]; + if (forward) { + predictedPos = { + x: from.x + vec[0], + y: from.y + vec[1] + }; + } else { + predictedPos = { + x: from.x - vec[0], + y: from.y - vec[1] + }; } - } - }; - - CVUtils.cluster = function(points, threshold, property) { - var i, k, cluster, point, clusters = []; - if (!property) { - property = "rad"; - } - - function addToCluster(point) { - var found = false; - for ( k = 0; k < clusters.length; k++) { - cluster = clusters[k]; - if (cluster.fits(point)) { - cluster.add(point); - found = true; - } + toIdx = forward ? idx + 1 : idx - 1; + to = points[toIdx]; + while (to && ( found = match(to, predictedPos)) !== true && (Math.abs(to.y - from.y) < vec[1])) { + toIdx = forward ? toIdx + 1 : toIdx - 1; + to = points[toIdx]; } - return found; - } - // iterate over each cloud - for ( i = 0; i < points.length; i++) { - point = Cluster2.createPoint(points[i], i, property); - if (!addToCluster(point)) { - clusters.push(Cluster2.create(point, threshold)); - } + return found ? toIdx : null; } - return clusters; - - }; - - CVUtils.Tracer = { - trace : function(points, vec) { - var iteration, maxIterations = 10, top = [], result = [], centerPos = 0, currentPos = 0; - - function trace(idx, forward) { - var from, to, toIdx, predictedPos, thresholdX = 1, thresholdY = Math.abs(vec[1] / 10), found = false; + for ( iteration = 0; iteration < maxIterations; iteration++) { + // randomly select point to start with + centerPos = Math.floor(Math.random() * points.length); - function match(pos, predicted) { - if (pos.x > (predicted.x - thresholdX) && pos.x < (predicted.x + thresholdX) && pos.y > (predicted.y - thresholdY) && pos.y < (predicted.y + thresholdY)) { - return true; - } else { - return false; - } - } - - // check if the next index is within the vec specifications - // if not, check as long as the threshold is met - - from = points[idx]; - if (forward) { - predictedPos = { - x : from.x + vec[0], - y : from.y + vec[1] - }; - } else { - predictedPos = { - x : from.x - vec[0], - y : from.y - vec[1] - }; - } - - toIdx = forward ? idx + 1 : idx - 1; - to = points[toIdx]; - while (to && ( found = match(to, predictedPos)) !== true && (Math.abs(to.y - from.y) < vec[1])) { - toIdx = forward ? toIdx + 1 : toIdx - 1; - to = points[toIdx]; - } - - return found ? toIdx : null; + // trace forward + top = []; + currentPos = centerPos; + top.push(points[currentPos]); + while (( currentPos = trace(currentPos, true)) !== null) { + top.push(points[currentPos]); } - - for ( iteration = 0; iteration < maxIterations; iteration++) { - // randomly select point to start with - centerPos = Math.floor(Math.random() * points.length); - - // trace forward - top = []; + if (centerPos > 0) { currentPos = centerPos; - top.push(points[currentPos]); - while (( currentPos = trace(currentPos, true)) !== null) { + while (( currentPos = trace(currentPos, false)) !== null) { top.push(points[currentPos]); } - if (centerPos > 0) { - currentPos = centerPos; - while (( currentPos = trace(currentPos, false)) !== null) { - top.push(points[currentPos]); - } - } - - if (top.length > result.length) { - result = top; - } - } - - return result; - - } - }; - - CVUtils.DILATE = 1; - CVUtils.ERODE = 2; - - CVUtils.dilate = function(inImageWrapper, outImageWrapper) { - var v, u, inImageData = inImageWrapper.data, outImageData = outImageWrapper.data, height = inImageWrapper.size.y, width = inImageWrapper.size.x, sum, yStart1, yStart2, xStart1, xStart2; - - for ( v = 1; v < height - 1; v++) { - for ( u = 1; u < width - 1; u++) { - yStart1 = v - 1; - yStart2 = v + 1; - xStart1 = u - 1; - xStart2 = u + 1; - sum = inImageData[yStart1 * width + xStart1]/* + inImageData[yStart1*width+u] */ + inImageData[yStart1 * width + xStart2] + - /* inImageData[v*width+xStart1] + */ - inImageData[v * width + u] + /* inImageData[v*width+xStart2] +*/ - inImageData[yStart2 * width + xStart1]/* + inImageData[yStart2*width+u]*/ + inImageData[yStart2 * width + xStart2]; - outImageData[v * width + u] = sum > 0 ? 1 : 0; } - } - }; - CVUtils.erode = function(inImageWrapper, outImageWrapper) { - var v, u, inImageData = inImageWrapper.data, outImageData = outImageWrapper.data, height = inImageWrapper.size.y, width = inImageWrapper.size.x, sum, yStart1, yStart2, xStart1, xStart2; - - for ( v = 1; v < height - 1; v++) { - for ( u = 1; u < width - 1; u++) { - yStart1 = v - 1; - yStart2 = v + 1; - xStart1 = u - 1; - xStart2 = u + 1; - sum = inImageData[yStart1 * width + xStart1]/* + inImageData[yStart1*width+u] */ + inImageData[yStart1 * width + xStart2] + - /* inImageData[v*width+xStart1] + */ - inImageData[v * width + u] + /* inImageData[v*width+xStart2] +*/ - inImageData[yStart2 * width + xStart1]/* + inImageData[yStart2*width+u]*/ + inImageData[yStart2 * width + xStart2]; - outImageData[v * width + u] = sum === 5 ? 1 : 0; + if (top.length > result.length) { + result = top; } } - }; - - CVUtils.subtract = function(aImageWrapper, bImageWrapper, resultImageWrapper) { - if (!resultImageWrapper) { - resultImageWrapper = aImageWrapper; - } - var length = aImageWrapper.data.length, aImageData = aImageWrapper.data, bImageData = bImageWrapper.data, cImageData = resultImageWrapper.data; - - while (length--) { - cImageData[length] = aImageData[length] - bImageData[length]; - } - }; - - CVUtils.bitwiseOr = function(aImageWrapper, bImageWrapper, resultImageWrapper) { - if (!resultImageWrapper) { - resultImageWrapper = aImageWrapper; - } - var length = aImageWrapper.data.length, aImageData = aImageWrapper.data, bImageData = bImageWrapper.data, cImageData = resultImageWrapper.data; - - while (length--) { - cImageData[length] = aImageData[length] || bImageData[length]; - } - }; - - CVUtils.countNonZero = function(imageWrapper) { - var length = imageWrapper.data.length, data = imageWrapper.data, sum = 0; - - while (length--) { - sum += data[length]; - } - return sum; - }; - - CVUtils.topGeneric = function(list, top, scoreFunc) { - var i, minIdx = 0, min = 0, queue = [], score, hit, pos; - - for ( i = 0; i < top; i++) { - queue[i] = { - score : 0, - item : null - }; - } - - for ( i = 0; i < list.length; i++) { - score = scoreFunc.apply(this, [list[i]]); - if (score > min) { - hit = queue[minIdx]; - hit.score = score; - hit.item = list[i]; - min = Number.MAX_VALUE; - for ( pos = 0; pos < top; pos++) { - if (queue[pos].score < min) { - min = queue[pos].score; - minIdx = pos; - } + return result; + } +}; + +CVUtils.DILATE = 1; +CVUtils.ERODE = 2; + +CVUtils.dilate = function(inImageWrapper, outImageWrapper) { + var v, + u, + inImageData = inImageWrapper.data, + outImageData = outImageWrapper.data, + height = inImageWrapper.size.y, + width = inImageWrapper.size.x, + sum, + yStart1, + yStart2, + xStart1, + xStart2; + + for ( v = 1; v < height - 1; v++) { + for ( u = 1; u < width - 1; u++) { + yStart1 = v - 1; + yStart2 = v + 1; + xStart1 = u - 1; + xStart2 = u + 1; + sum = inImageData[yStart1 * width + xStart1] + inImageData[yStart1 * width + xStart2] + + inImageData[v * width + u] + + inImageData[yStart2 * width + xStart1] + inImageData[yStart2 * width + xStart2]; + outImageData[v * width + u] = sum > 0 ? 1 : 0; + } + } +}; + +CVUtils.erode = function(inImageWrapper, outImageWrapper) { + var v, + u, + inImageData = inImageWrapper.data, + outImageData = outImageWrapper.data, + height = inImageWrapper.size.y, + width = inImageWrapper.size.x, + sum, + yStart1, + yStart2, + xStart1, + xStart2; + + for ( v = 1; v < height - 1; v++) { + for ( u = 1; u < width - 1; u++) { + yStart1 = v - 1; + yStart2 = v + 1; + xStart1 = u - 1; + xStart2 = u + 1; + sum = inImageData[yStart1 * width + xStart1] + inImageData[yStart1 * width + xStart2] + + inImageData[v * width + u] + + inImageData[yStart2 * width + xStart1] + inImageData[yStart2 * width + xStart2]; + outImageData[v * width + u] = sum === 5 ? 1 : 0; + } + } +}; + +CVUtils.subtract = function(aImageWrapper, bImageWrapper, resultImageWrapper) { + if (!resultImageWrapper) { + resultImageWrapper = aImageWrapper; + } + var length = aImageWrapper.data.length, + aImageData = aImageWrapper.data, + bImageData = bImageWrapper.data, + cImageData = resultImageWrapper.data; + + while (length--) { + cImageData[length] = aImageData[length] - bImageData[length]; + } +}; + +CVUtils.bitwiseOr = function(aImageWrapper, bImageWrapper, resultImageWrapper) { + if (!resultImageWrapper) { + resultImageWrapper = aImageWrapper; + } + var length = aImageWrapper.data.length, + aImageData = aImageWrapper.data, + bImageData = bImageWrapper.data, + cImageData = resultImageWrapper.data; + + while (length--) { + cImageData[length] = aImageData[length] || bImageData[length]; + } +}; + +CVUtils.countNonZero = function(imageWrapper) { + var length = imageWrapper.data.length, data = imageWrapper.data, sum = 0; + + while (length--) { + sum += data[length]; + } + return sum; +}; + +CVUtils.topGeneric = function(list, top, scoreFunc) { + var i, minIdx = 0, min = 0, queue = [], score, hit, pos; + + for ( i = 0; i < top; i++) { + queue[i] = { + score: 0, + item: null + }; + } + + for ( i = 0; i < list.length; i++) { + score = scoreFunc.apply(this, [list[i]]); + if (score > min) { + hit = queue[minIdx]; + hit.score = score; + hit.item = list[i]; + min = Number.MAX_VALUE; + for ( pos = 0; pos < top; pos++) { + if (queue[pos].score < min) { + min = queue[pos].score; + minIdx = pos; } } } - - return queue; + } + + return queue; +}; + +CVUtils.grayArrayFromImage = function(htmlImage, offsetX, ctx, array) { + ctx.drawImage(htmlImage, offsetX, 0, htmlImage.width, htmlImage.height); + var ctxData = ctx.getImageData(offsetX, 0, htmlImage.width, htmlImage.height).data; + CVUtils.computeGray(ctxData, array); +}; + +CVUtils.grayArrayFromContext = function(ctx, size, offset, array) { + var ctxData = ctx.getImageData(offset.x, offset.y, size.x, size.y).data; + CVUtils.computeGray(ctxData, array); +}; + +CVUtils.grayAndHalfSampleFromCanvasData = function(canvasData, size, outArray) { + var topRowIdx = 0; + var bottomRowIdx = size.x; + var endIdx = Math.floor(canvasData.length / 4); + var outWidth = size.x / 2; + var outImgIdx = 0; + var inWidth = size.x; + var i; + + while (bottomRowIdx < endIdx) { + for ( i = 0; i < outWidth; i++) { + outArray[outImgIdx] = Math.floor(( + (0.299 * canvasData[topRowIdx * 4 + 0] + + 0.587 * canvasData[topRowIdx * 4 + 1] + + 0.114 * canvasData[topRowIdx * 4 + 2]) + + (0.299 * canvasData[(topRowIdx + 1) * 4 + 0] + + 0.587 * canvasData[(topRowIdx + 1) * 4 + 1] + + 0.114 * canvasData[(topRowIdx + 1) * 4 + 2]) + + (0.299 * canvasData[(bottomRowIdx) * 4 + 0] + + 0.587 * canvasData[(bottomRowIdx) * 4 + 1] + + 0.114 * canvasData[(bottomRowIdx) * 4 + 2]) + + (0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] + + 0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] + + 0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4); + outImgIdx++; + topRowIdx = topRowIdx + 2; + bottomRowIdx = bottomRowIdx + 2; + } + topRowIdx = topRowIdx + inWidth; + bottomRowIdx = bottomRowIdx + inWidth; + } +}; + +CVUtils.computeGray = function(imageData, outArray, config) { + var l = (imageData.length / 4) | 0, + i, + singleChannel = config && config.singleChannel === true; + + if (singleChannel) { + for (i = 0; i < l; i++) { + outArray[i] = imageData[i * 4 + 0]; + } + } else { + for (i = 0; i < l; i++) { + outArray[i] = Math.floor( + 0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); + } + } +}; + +CVUtils.loadImageArray = function(src, callback, canvas) { + if (!canvas) { + canvas = document.createElement('canvas'); + } + var img = new Image(); + img.callback = callback; + img.onload = function() { + canvas.width = this.width; + canvas.height = this.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0); + var array = new Uint8Array(this.width * this.height); + ctx.drawImage(this, 0, 0); + var data = ctx.getImageData(0, 0, this.width, this.height).data; + CVUtils.computeGray(data, array); + this.callback(array, { + x: this.width, + y: this.height + }, this); }; - - CVUtils.grayArrayFromImage = function(htmlImage, offsetX, ctx, array) { - ctx.drawImage(htmlImage, offsetX, 0, htmlImage.width, htmlImage.height); - var ctxData = ctx.getImageData(offsetX, 0, htmlImage.width, htmlImage.height).data; - CVUtils.computeGray(ctxData, array); - }; - - CVUtils.grayArrayFromContext = function(ctx, size, offset, array) { - var ctxData = ctx.getImageData(offset.x, offset.y, size.x, size.y).data; - CVUtils.computeGray(ctxData, array); - }; - - CVUtils.grayAndHalfSampleFromCanvasData = function(canvasData, size, outArray) { - var topRowIdx = 0; - var bottomRowIdx = size.x; - var endIdx = Math.floor(canvasData.length / 4); - var outWidth = size.x / 2; - var outImgIdx = 0; - var inWidth = size.x; - var i; - - while (bottomRowIdx < endIdx) { - for ( i = 0; i < outWidth; i++) { - outArray[outImgIdx] = Math.floor(((0.299 * canvasData[topRowIdx * 4 + 0] + 0.587 * canvasData[topRowIdx * 4 + 1] + 0.114 * canvasData[topRowIdx * 4 + 2]) + (0.299 * canvasData[(topRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(topRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(topRowIdx + 1) * 4 + 2]) + (0.299 * canvasData[(bottomRowIdx) * 4 + 0] + 0.587 * canvasData[(bottomRowIdx) * 4 + 1] + 0.114 * canvasData[(bottomRowIdx) * 4 + 2]) + (0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] + 0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] + 0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4); - outImgIdx++; - topRowIdx = topRowIdx + 2; - bottomRowIdx = bottomRowIdx + 2; + img.src = src; +}; + +/** + * @param inImg {ImageWrapper} input image to be sampled + * @param outImg {ImageWrapper} to be stored in + */ +CVUtils.halfSample = function(inImgWrapper, outImgWrapper) { + var inImg = inImgWrapper.data; + var inWidth = inImgWrapper.size.x; + var outImg = outImgWrapper.data; + var topRowIdx = 0; + var bottomRowIdx = inWidth; + var endIdx = inImg.length; + var outWidth = inWidth / 2; + var outImgIdx = 0; + while (bottomRowIdx < endIdx) { + for (var i = 0; i < outWidth; i++) { + outImg[outImgIdx] = Math.floor( + (inImg[topRowIdx] + inImg[topRowIdx + 1] + inImg[bottomRowIdx] + inImg[bottomRowIdx + 1]) / 4); + outImgIdx++; + topRowIdx = topRowIdx + 2; + bottomRowIdx = bottomRowIdx + 2; + } + topRowIdx = topRowIdx + inWidth; + bottomRowIdx = bottomRowIdx + inWidth; + } +}; + +CVUtils.hsv2rgb = function(hsv, rgb) { + var h = hsv[0], + s = hsv[1], + v = hsv[2], + c = v * s, + x = c * (1 - Math.abs((h / 60) % 2 - 1)), + m = v - c, + r = 0, + g = 0, + b = 0; + + rgb = rgb || [0, 0, 0]; + + if (h < 60) { + r = c; + g = x; + } else if (h < 120) { + r = x; + g = c; + } else if (h < 180) { + g = c; + b = x; + } else if (h < 240) { + g = x; + b = c; + } else if (h < 300) { + r = x; + b = c; + } else if (h < 360) { + r = c; + b = x; + } + rgb[0] = ((r + m) * 255) | 0; + rgb[1] = ((g + m) * 255) | 0; + rgb[2] = ((b + m) * 255) | 0; + return rgb; +}; + +CVUtils._computeDivisors = function(n) { + var largeDivisors = [], + divisors = [], + i; + + for (i = 1; i < Math.sqrt(n) + 1; i++) { + if (n % i === 0) { + divisors.push(i); + if (i !== n / i) { + largeDivisors.unshift(Math.floor(n / i)); } - topRowIdx = topRowIdx + inWidth; - bottomRowIdx = bottomRowIdx + inWidth; } - - }; - - CVUtils.computeGray = function(imageData, outArray, config) { - var l = (imageData.length / 4) | 0, - i, - singleChannel = config && config.singleChannel === true; - - if (singleChannel) { - for (i = 0; i < l; i++) { - outArray[i] = imageData[i * 4 + 0]; - } + } + return divisors.concat(largeDivisors); +}; + +CVUtils._computeIntersection = function(arr1, arr2) { + var i = 0, + j = 0, + result = []; + + while (i < arr1.length && j < arr2.length) { + if (arr1[i] === arr2[j]) { + result.push(arr1[i]); + i++; + j++; + } else if (arr1[i] > arr2[j]) { + j++; } else { - for (i = 0; i < l; i++) { - outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); - } - } - }; - - CVUtils.loadImageArray = function(src, callback, canvas) { - if (!canvas) - canvas = document.createElement('canvas'); - var img = new Image(); - img.callback = callback; - img.onload = function() { - canvas.width = this.width; - canvas.height = this.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(this, 0, 0); - var array = new Uint8Array(this.width * this.height); - ctx.drawImage(this, 0, 0); - var data = ctx.getImageData(0, 0, this.width, this.height).data; - CVUtils.computeGray(data, array); - this.callback(array, { - x : this.width, - y : this.height - }, this); - }; - img.src = src; - }; + i++; + } + } + return result; +}; + +CVUtils.calculatePatchSize = function(patchSize, imgSize) { + var divisorsX = this._computeDivisors(imgSize.x), + divisorsY = this._computeDivisors(imgSize.y), + wideSide = Math.max(imgSize.x, imgSize.y), + common = this._computeIntersection(divisorsX, divisorsY), + nrOfPatchesList = [8, 10, 15, 20, 32, 60, 80], + nrOfPatchesMap = { + "x-small": 5, + "small": 4, + "medium": 3, + "large": 2, + "x-large": 1 + }, + nrOfPatchesIdx = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium, + nrOfPatches = nrOfPatchesList[nrOfPatchesIdx], + desiredPatchSize = Math.floor(wideSide / nrOfPatches), + optimalPatchSize; - /** - * @param inImg {ImageWrapper} input image to be sampled - * @param outImg {ImageWrapper} to be stored in - */ - CVUtils.halfSample = function(inImgWrapper, outImgWrapper) { - var inImg = inImgWrapper.data; - var inWidth = inImgWrapper.size.x; - var outImg = outImgWrapper.data; - var topRowIdx = 0; - var bottomRowIdx = inWidth; - var endIdx = inImg.length; - var outWidth = inWidth / 2; - var outImgIdx = 0; - while (bottomRowIdx < endIdx) { - for (var i = 0; i < outWidth; i++) { - outImg[outImgIdx] = Math.floor((inImg[topRowIdx] + inImg[topRowIdx + 1] + inImg[bottomRowIdx] + inImg[bottomRowIdx + 1]) / 4); - outImgIdx++; - topRowIdx = topRowIdx + 2; - bottomRowIdx = bottomRowIdx + 2; - } - topRowIdx = topRowIdx + inWidth; - bottomRowIdx = bottomRowIdx + inWidth; - } - }; + function findPatchSizeForDivisors(divisors) { + var i = 0, + found = divisors[Math.floor(divisors.length / 2)]; - CVUtils.hsv2rgb = function(hsv, rgb) { - var h = hsv[0], s = hsv[1], v = hsv[2], c = v * s, x = c * (1 - Math.abs((h / 60) % 2 - 1)), m = v - c, r = 0, g = 0, b = 0; - rgb = rgb || [0, 0, 0]; - - if (h < 60) { - r = c; - g = x; - } else if (h < 120) { - r = x; - g = c; - } else if (h < 180) { - g = c; - b = x; - } else if (h < 240) { - g = x; - b = c; - } else if (h < 300) { - r = x; - b = c; - } else if (h < 360) { - r = c; - b = x; + while (i < (divisors.length - 1) && divisors[i] < desiredPatchSize) { + i++; } - rgb[0] = ((r + m) * 255) | 0; - rgb[1] = ((g + m) * 255) | 0; - rgb[2] = ((b + m) * 255) | 0; - return rgb; - }; - - CVUtils._computeDivisors = function(n) { - var largeDivisors = [], - divisors = [], - i; - - for (i = 1; i < Math.sqrt(n) + 1; i++) { - if (n % i === 0) { - divisors.push(i); - if (i !== n/i) { - largeDivisors.unshift(Math.floor(n/i)); - } - } - } - return divisors.concat(largeDivisors); - }; - - CVUtils._computeIntersection = function(arr1, arr2) { - var i = 0, - j = 0, - result = []; - - while (i < arr1.length && j < arr2.length) { - if (arr1[i] === arr2[j]) { - result.push(arr1[i]); - i++; - j++; - } else if (arr1[i] > arr2[j]) { - j++; + if (i > 0) { + if (Math.abs(divisors[i] - desiredPatchSize) > Math.abs(divisors[i - 1] - desiredPatchSize)) { + found = divisors[i - 1]; } else { - i++; + found = divisors[i]; } } - return result; - }; - - CVUtils.calculatePatchSize = function(patchSize, imgSize) { - var divisorsX = this._computeDivisors(imgSize.x), - divisorsY = this._computeDivisors(imgSize.y), - wideSide = Math.max(imgSize.x, imgSize.y), - common = this._computeIntersection(divisorsX, divisorsY), - nrOfPatchesList = [8, 10, 15, 20, 32, 60, 80], - nrOfPatchesMap = { - "x-small": 5, - "small": 4, - "medium": 3, - "large": 2, - "x-large": 1 - }, - nrOfPatchesIdx = nrOfPatchesMap[patchSize] || nrOfPatchesMap.medium, - nrOfPatches = nrOfPatchesList[nrOfPatchesIdx], - desiredPatchSize = Math.floor(wideSide/nrOfPatches), - optimalPatchSize; - - function findPatchSizeForDivisors(divisors) { - var i = 0, - found = divisors[Math.floor(divisors.length/2)]; - - while(i < (divisors.length - 1) && divisors[i] < desiredPatchSize) { - i++; - } - if (i > 0) { - if (Math.abs(divisors[i] - desiredPatchSize) > Math.abs(divisors[i-1] - desiredPatchSize)) { - found = divisors[i-1]; - } else { - found = divisors[i]; - } - } - if (desiredPatchSize / found < nrOfPatchesList[nrOfPatchesIdx+1] / nrOfPatchesList[nrOfPatchesIdx] && - desiredPatchSize / found > nrOfPatchesList[nrOfPatchesIdx-1]/nrOfPatchesList[nrOfPatchesIdx] ) { - return {x: found, y: found}; - } - return null; + if (desiredPatchSize / found < nrOfPatchesList[nrOfPatchesIdx + 1] / nrOfPatchesList[nrOfPatchesIdx] && + desiredPatchSize / found > nrOfPatchesList[nrOfPatchesIdx - 1] / nrOfPatchesList[nrOfPatchesIdx] ) { + return {x: found, y: found}; } + return null; + } - optimalPatchSize = findPatchSizeForDivisors(common); + optimalPatchSize = findPatchSizeForDivisors(common); + if (!optimalPatchSize) { + optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(wideSide)); if (!optimalPatchSize) { - optimalPatchSize = findPatchSizeForDivisors(this._computeDivisors(wideSide)); - if (!optimalPatchSize) { - optimalPatchSize = findPatchSizeForDivisors((this._computeDivisors(desiredPatchSize * nrOfPatches))); - } + optimalPatchSize = findPatchSizeForDivisors((this._computeDivisors(desiredPatchSize * nrOfPatches))); } - return optimalPatchSize; - }; - - CVUtils._parseCSSDimensionValues = function(value) { - var dimension = { - value: parseFloat(value), - unit: value.indexOf("%") === value.length-1 ? "%" : "%" - }; + } + return optimalPatchSize; +}; - return dimension; +CVUtils._parseCSSDimensionValues = function(value) { + var dimension = { + value: parseFloat(value), + unit: value.indexOf("%") === value.length - 1 ? "%" : "%" }; - CVUtils._dimensionsConverters = { - top: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.height * (dimension.value / 100)); - } - }, - right: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.width - (context.width * (dimension.value / 100))); - } - }, - bottom: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.height - (context.height * (dimension.value / 100))); - } - }, - left: function(dimension, context) { - if (dimension.unit === "%") { - return Math.floor(context.width * (dimension.value / 100)); - } + return dimension; +}; + +CVUtils._dimensionsConverters = { + top: function(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.height * (dimension.value / 100)); } - }; + }, + right: function(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.width - (context.width * (dimension.value / 100))); + } + }, + bottom: function(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.height - (context.height * (dimension.value / 100))); + } + }, + left: function(dimension, context) { + if (dimension.unit === "%") { + return Math.floor(context.width * (dimension.value / 100)); + } + } +}; - CVUtils.computeImageArea = function(inputWidth, inputHeight, area) { - var context = {width: inputWidth, height: inputHeight}; +CVUtils.computeImageArea = function(inputWidth, inputHeight, area) { + var context = {width: inputWidth, height: inputHeight}; - var parsedArea = Object.keys(area).reduce(function(result, key) { - var value = area[key], - parsed = CVUtils._parseCSSDimensionValues(value), - calculated = CVUtils._dimensionsConverters[key](parsed, context); + var parsedArea = Object.keys(area).reduce(function(result, key) { + var value = area[key], + parsed = CVUtils._parseCSSDimensionValues(value), + calculated = CVUtils._dimensionsConverters[key](parsed, context); - result[key] = calculated; - return result; - }, {}); + result[key] = calculated; + return result; + }, {}); - return { - sx: parsedArea.left, - sy: parsedArea.top, - sw: parsedArea.right - parsedArea.left, - sh: parsedArea.bottom - parsedArea.top - }; + return { + sx: parsedArea.left, + sy: parsedArea.top, + sw: parsedArea.right - parsedArea.left, + sh: parsedArea.bottom - parsedArea.top }; +}; - return (CVUtils); -}); - +export default CVUtils; diff --git a/src/ean_8_reader.js b/src/ean_8_reader.js index f76642ed..628661a8 100644 --- a/src/ean_8_reader.js +++ b/src/ean_8_reader.js @@ -1,55 +1,45 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./ean_reader" - ], - function(EANReader) { - "use strict"; - - function EAN8Reader() { - EANReader.call(this); +import EANReader from './ean_reader'; + +function EAN8Reader() { + EANReader.call(this); +} + +var properties = { + FORMAT: {value: "ean_8", writeable: false} +}; + +EAN8Reader.prototype = Object.create(EANReader.prototype, properties); +EAN8Reader.prototype.constructor = EAN8Reader; + +EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) { + var i, + self = this; + + for ( i = 0; i < 4; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; } + result.push(code.code); + decodedCodes.push(code); + } - var properties = { - FORMAT: {value: "ean_8", writeable: false} - }; - - EAN8Reader.prototype = Object.create(EANReader.prototype, properties); - EAN8Reader.prototype.constructor = EAN8Reader; - - EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) { - var i, - self = this; - - for ( i = 0; i < 4; i++) { - code = self._decodeCode(code.end, self.CODE_G_START); - if (!code) { - return null; - } - result.push(code.code); - decodedCodes.push(code); - } - - code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); - if (code === null) { - return null; - } - decodedCodes.push(code); - - for ( i = 0; i < 4; i++) { - code = self._decodeCode(code.end, self.CODE_G_START); - if (!code) { - return null; - } - decodedCodes.push(code); - result.push(code.code); - } - - return code; - }; - - return (EAN8Reader); + code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); + if (code === null) { + return null; } -); \ No newline at end of file + decodedCodes.push(code); + + for ( i = 0; i < 4; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + decodedCodes.push(code); + result.push(code.code); + } + + return code; +}; + +export default EAN8Reader; diff --git a/src/ean_reader.js b/src/ean_reader.js index f207055b..bb7e68ca 100644 --- a/src/ean_reader.js +++ b/src/ean_reader.js @@ -1,337 +1,327 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./barcode_reader" - ], - function(BarcodeReader) { - "use strict"; - - function EANReader(opts) { - BarcodeReader.call(this, opts); - } - - var properties = { - CODE_L_START : {value: 0}, - MODULO : {value: 7}, - CODE_G_START : {value: 10}, - START_PATTERN : {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, - STOP_PATTERN : {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, - MIDDLE_PATTERN : {value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7]}, - CODE_PATTERN : {value: [ - [3, 2, 1, 1], - [2, 2, 2, 1], - [2, 1, 2, 2], - [1, 4, 1, 1], - [1, 1, 3, 2], - [1, 2, 3, 1], - [1, 1, 1, 4], - [1, 3, 1, 2], - [1, 2, 1, 3], - [3, 1, 1, 2], - [1, 1, 2, 3], - [1, 2, 2, 2], - [2, 2, 1, 2], - [1, 1, 4, 1], - [2, 3, 1, 1], - [1, 3, 2, 1], - [4, 1, 1, 1], - [2, 1, 3, 1], - [3, 1, 2, 1], - [2, 1, 1, 3] - ]}, - CODE_FREQUENCY : {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]}, - SINGLE_CODE_ERROR: {value: 0.67}, - AVG_CODE_ERROR: {value: 0.27}, - FORMAT: {value: "ean_13", writeable: false} - }; - - EANReader.prototype = Object.create(BarcodeReader.prototype, properties); - EANReader.prototype.constructor = EANReader; - - EANReader.prototype._decodeCode = function(start, coderange) { - var counter = [0, 0, 0, 0], - i, - self = this, - offset = start, - isWhite = !self._row[offset], - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : start, - end : start - }, - code, - error, - normalized; - - if (!coderange) { - coderange = self.CODE_PATTERN.length; - } +import BarcodeReader from './barcode_reader'; + +function EANReader(opts) { + BarcodeReader.call(this, opts); +} + +var properties = { + CODE_L_START: {value: 0}, + MODULO: {value: 7}, + CODE_G_START: {value: 10}, + START_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, + STOP_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]}, + MIDDLE_PATTERN: {value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7]}, + CODE_PATTERN: {value: [ + [3, 2, 1, 1], + [2, 2, 2, 1], + [2, 1, 2, 2], + [1, 4, 1, 1], + [1, 1, 3, 2], + [1, 2, 3, 1], + [1, 1, 1, 4], + [1, 3, 1, 2], + [1, 2, 1, 3], + [3, 1, 1, 2], + [1, 1, 2, 3], + [1, 2, 2, 2], + [2, 2, 1, 2], + [1, 1, 4, 1], + [2, 3, 1, 1], + [1, 3, 2, 1], + [4, 1, 1, 1], + [2, 1, 3, 1], + [3, 1, 2, 1], + [2, 1, 1, 3] + ]}, + CODE_FREQUENCY: {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]}, + SINGLE_CODE_ERROR: {value: 0.67}, + AVG_CODE_ERROR: {value: 0.27}, + FORMAT: {value: "ean_13", writeable: false} +}; + +EANReader.prototype = Object.create(BarcodeReader.prototype, properties); +EANReader.prototype.constructor = EANReader; + +EANReader.prototype._decodeCode = function(start, coderange) { + var counter = [0, 0, 0, 0], + i, + self = this, + offset = start, + isWhite = !self._row[offset], + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: start, + end: start + }, + code, + error, + normalized; + + if (!coderange) { + coderange = self.CODE_PATTERN.length; + } - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < coderange; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - bestMatch.end = i; - if (bestMatch.error > self.AVG_CODE_ERROR) { - return null; - } - return bestMatch; + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < coderange; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; } - } else { - counterPos++; } - counter[counterPos] = 1; - isWhite = !isWhite; + bestMatch.end = i; + if (bestMatch.error > self.AVG_CODE_ERROR) { + return null; + } + return bestMatch; } + } else { + counterPos++; } - return null; - }; - - EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, epsilon) { - var counter = [], - self = this, - i, - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }, - error, - j, - sum, - normalized; - - if (!offset) { - offset = self._nextSet(self._row); - } - - if (isWhite === undefined) { - isWhite = false; - } - - if (tryHarder === undefined) { - tryHarder = true; - } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; +}; + +EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder, epsilon) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized; + + if (!offset) { + offset = self._nextSet(self._row); + } - if ( epsilon === undefined) { - epsilon = self.AVG_CODE_ERROR; - } + if (isWhite === undefined) { + isWhite = false; + } - for ( i = 0; i < pattern.length; i++) { - counter[i] = 0; - } + if (tryHarder === undefined) { + tryHarder = true; + } - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - sum = 0; - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - error = self._matchPattern(normalized, pattern); - - if (error < epsilon) { - bestMatch.error = error; - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } - } - if (tryHarder) { - for ( j = 0; j < counter.length - 2; j++) { - counter[j] = counter[j + 2]; - } - counter[counter.length - 2] = 0; - counter[counter.length - 1] = 0; - counterPos--; - } else { - return null; - } - } else { - counterPos++; - } - counter[counterPos] = 1; - isWhite = !isWhite; - } - } - return null; - }; + if ( epsilon === undefined) { + epsilon = self.AVG_CODE_ERROR; + } - EANReader.prototype._findStart = function() { - var self = this, - leadingWhitespaceStart, - offset = self._nextSet(self._row), - startInfo; + for ( i = 0; i < pattern.length; i++) { + counter[i] = 0; + } - while(!startInfo) { - startInfo = self._findPattern(self.START_PATTERN, offset); - if (!startInfo) { - return null; + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for ( j = 0; j < counter.length; j++) { + sum += counter[j]; } - leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start); - if (leadingWhitespaceStart >= 0) { - if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { - return startInfo; + normalized = self._normalize(counter); + if (normalized) { + error = self._matchPattern(normalized, pattern); + + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; } } - offset = startInfo.end; - startInfo = null; - } - }; - - EANReader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; - - trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; + if (tryHarder) { + for ( j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; + } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; } + } else { + counterPos++; } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; +}; + +EANReader.prototype._findStart = function() { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset); + if (!startInfo) { return null; - }; + } + leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start); + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; + } + } + offset = startInfo.end; + startInfo = null; + } +}; - EANReader.prototype._findEnd = function(offset, isWhite) { - var self = this, - endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false); +EANReader.prototype._verifyTrailingWhitespace = function(endInfo) { + var self = this, + trailingWhitespaceEnd; - return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; - }; + trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; +}; - EANReader.prototype._calculateFirstDigit = function(codeFrequency) { - var i, - self = this; +EANReader.prototype._findEnd = function(offset, isWhite) { + var self = this, + endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false); - for ( i = 0; i < self.CODE_FREQUENCY.length; i++) { - if (codeFrequency === self.CODE_FREQUENCY[i]) { - return i; - } - } - return null; - }; + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; +}; - EANReader.prototype._decodePayload = function(code, result, decodedCodes) { - var i, - self = this, - codeFrequency = 0x0, - firstDigit; +EANReader.prototype._calculateFirstDigit = function(codeFrequency) { + var i, + self = this; - for ( i = 0; i < 6; i++) { - code = self._decodeCode(code.end); - if (!code) { - return null; - } - if (code.code >= self.CODE_G_START) { - code.code = code.code - self.CODE_G_START; - codeFrequency |= 1 << (5 - i); - } else { - codeFrequency |= 0 << (5 - i); - } - result.push(code.code); - decodedCodes.push(code); - } + for ( i = 0; i < self.CODE_FREQUENCY.length; i++) { + if (codeFrequency === self.CODE_FREQUENCY[i]) { + return i; + } + } + return null; +}; + +EANReader.prototype._decodePayload = function(code, result, decodedCodes) { + var i, + self = this, + codeFrequency = 0x0, + firstDigit; + + for ( i = 0; i < 6; i++) { + code = self._decodeCode(code.end); + if (!code) { + return null; + } + if (code.code >= self.CODE_G_START) { + code.code = code.code - self.CODE_G_START; + codeFrequency |= 1 << (5 - i); + } else { + codeFrequency |= 0 << (5 - i); + } + result.push(code.code); + decodedCodes.push(code); + } - firstDigit = self._calculateFirstDigit(codeFrequency); - if (firstDigit === null) { - return null; - } - result.unshift(firstDigit); + firstDigit = self._calculateFirstDigit(codeFrequency); + if (firstDigit === null) { + return null; + } + result.unshift(firstDigit); - code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); - if (code === null) { - return null; - } - decodedCodes.push(code); + code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false); + if (code === null) { + return null; + } + decodedCodes.push(code); - for ( i = 0; i < 6; i++) { - code = self._decodeCode(code.end, self.CODE_G_START); - if (!code) { - return null; - } - decodedCodes.push(code); - result.push(code.code); - } + for ( i = 0; i < 6; i++) { + code = self._decodeCode(code.end, self.CODE_G_START); + if (!code) { + return null; + } + decodedCodes.push(code); + result.push(code.code); + } - return code; - }; + return code; +}; - EANReader.prototype._decode = function() { - var startInfo, - self = this, - code, - result = [], - decodedCodes = []; +EANReader.prototype._decode = function() { + var startInfo, + self = this, + code, + result = [], + decodedCodes = []; - startInfo = self._findStart(); - if (!startInfo) { - return null; - } - code = { - code : startInfo.code, - start : startInfo.start, - end : startInfo.end - }; - decodedCodes.push(code); - code = self._decodePayload(code, result, decodedCodes); - if (!code) { - return null; - } - code = self._findEnd(code.end, false); - if (!code){ - return null; - } + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + code = { + code: startInfo.code, + start: startInfo.start, + end: startInfo.end + }; + decodedCodes.push(code); + code = self._decodePayload(code, result, decodedCodes); + if (!code) { + return null; + } + code = self._findEnd(code.end, false); + if (!code){ + return null; + } - decodedCodes.push(code); + decodedCodes.push(code); - // Checksum - if (!self._checksum(result)) { - return null; - } + // Checksum + if (!self._checksum(result)) { + return null; + } - return { - code : result.join(""), - start : startInfo.start, - end : code.end, - codeset : "", - startInfo : startInfo, - decodedCodes : decodedCodes - }; - }; - - EANReader.prototype._checksum = function(result) { - var sum = 0, i; - - for ( i = result.length - 2; i >= 0; i -= 2) { - sum += result[i]; - } - sum *= 3; - for ( i = result.length - 1; i >= 0; i -= 2) { - sum += result[i]; - } - return sum % 10 === 0; - }; - - return (EANReader); + return { + code: result.join(""), + start: startInfo.start, + end: code.end, + codeset: "", + startInfo: startInfo, + decodedCodes: decodedCodes + }; +}; + +EANReader.prototype._checksum = function(result) { + var sum = 0, i; + + for ( i = result.length - 2; i >= 0; i -= 2) { + sum += result[i]; } -); \ No newline at end of file + sum *= 3; + for ( i = result.length - 1; i >= 0; i -= 2) { + sum += result[i]; + } + return sum % 10 === 0; +}; + +export default (EANReader); diff --git a/src/events.js b/src/events.js index d5eea266..cc565892 100644 --- a/src/events.js +++ b/src/events.js @@ -1,91 +1,82 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +export default (function() { + var events = {}; -define(function() { - "use strict"; + function getEvent(eventName) { + if (!events[eventName]) { + events[eventName] = { + subscribers: [] + }; + } + return events[eventName]; + } - var _events = function() { - var events = {}; + function clearEvents(){ + events = {}; + } - function getEvent(eventName) { - if (!events[eventName]) { - events[eventName] = { - subscribers : [] - }; - } - return events[eventName]; - } - - function clearEvents(){ - events = {}; + function publishSubscription(subscription, data) { + if (subscription.async) { + setTimeout(function() { + subscription.callback(data); + }, 4); + } else { + subscription.callback(data); } + } - function publishSubscription(subscription, data) { - if (subscription.async) { - setTimeout(function() { - subscription.callback(data); - }, 4); - } else { - subscription.callback(data); + function subscribe(event, callback, async) { + var subscription; + + if ( typeof callback === "function") { + subscription = { + callback: callback, + async: async + }; + } else { + subscription = callback; + if (!subscription.callback) { + throw "Callback was not specified on options"; } } - - function subscribe(event, callback, async) { - var subscription; - if ( typeof callback === "function") { - subscription = { - callback : callback, - async : async - }; - } else { - subscription = callback; - if (!subscription.callback) { - throw "Callback was not specified on options"; - } - } + getEvent(event).subscribers.push(subscription); + } - getEvent(event).subscribers.push(subscription); - } + return { + subscribe: function(event, callback, async) { + return subscribe(event, callback, async); + }, + publish: function(eventName, data) { + var event = getEvent(eventName), + subscribers = event.subscribers; + + event.subscribers = subscribers.filter(function(subscriber) { + publishSubscription(subscriber, data); + return !subscriber.once; + }); + }, + once: function(event, callback, async) { + subscribe(event, { + callback: callback, + async: async, + once: true + }); + }, + unsubscribe: function(eventName, callback) { + var event; - return { - subscribe : function(event, callback, async) { - return subscribe(event, callback, async); - }, - publish : function(eventName, data) { - var event = getEvent(eventName), - subscribers = event.subscribers; - - event.subscribers = subscribers.filter(function(subscriber) { - publishSubscription(subscriber, data); - return !subscriber.once; - }); - }, - once: function(event, callback, async) { - subscribe(event, { - callback: callback, - async: async, - once: true - }); - }, - unsubscribe: function(eventName, callback) { - var event; - - if (eventName) { - event = getEvent(eventName); - if (event && callback) { - event.subscribers = event.subscribers.filter(function(subscriber){ - return subscriber.callback !== callback; - }); - } else { - event.subscribers = []; - } + if (eventName) { + event = getEvent(eventName); + if (event && callback) { + event.subscribers = event.subscribers.filter(function(subscriber){ + return subscriber.callback !== callback; + }); } else { - clearEvents(); + event.subscribers = []; } + } else { + clearEvents(); } - }; - }(); - - return _events; -}); \ No newline at end of file + } + }; +})(); diff --git a/src/frame_grabber.js b/src/frame_grabber.js index 92dcd7a9..6cc64fa9 100644 --- a/src/frame_grabber.js +++ b/src/frame_grabber.js @@ -1,78 +1,73 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +import CVUtils from './cv_utils'; -define(["cv_utils"], function(CVUtils) { - "use strict"; +var FrameGrabber = {}; - var FrameGrabber = {}; +FrameGrabber.create = function(inputStream, canvas) { + var _that = {}, + _streamConfig = inputStream.getConfig(), + _video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), + _canvasSize = inputStream.getCanvasSize(), + _size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), + topRight = inputStream.getTopRight(), + _sx = topRight.x, + _sy = topRight.y, + _canvas, + _ctx = null, + _data = null; - FrameGrabber.create = function(inputStream, canvas) { - var _that = {}, - _streamConfig = inputStream.getConfig(), - _video_size = CVUtils.imageRef(inputStream.getRealWidth(), inputStream.getRealHeight()), - _canvasSize = inputStream.getCanvasSize(), - _size = CVUtils.imageRef(inputStream.getWidth(), inputStream.getHeight()), - topRight = inputStream.getTopRight(), - _sx = topRight.x, - _sy = topRight.y, - _canvas, - _ctx = null, - _data = null; + _canvas = canvas ? canvas : document.createElement("canvas"); + _canvas.width = _canvasSize.x; + _canvas.height = _canvasSize.y; + _ctx = _canvas.getContext("2d"); + _data = new Uint8Array(_size.x * _size.y); + console.log("FrameGrabber", JSON.stringify({ + size: _size, + topRight: topRight, + videoSize: _video_size, + canvasSize: _canvasSize + })); - _canvas = canvas ? canvas : document.createElement("canvas"); - _canvas.width = _canvasSize.x; - _canvas.height = _canvasSize.y; - _ctx = _canvas.getContext("2d"); - _data = new Uint8Array(_size.x * _size.y); - console.log("FrameGrabber", JSON.stringify({ - size: _size, - topRight: topRight, - videoSize: _video_size, - canvasSize: _canvasSize - })); - - /** - * Uses the given array as frame-buffer - */ - _that.attachData = function(data) { - _data = data; - }; + /** + * Uses the given array as frame-buffer + */ + _that.attachData = function(data) { + _data = data; + }; - /** - * Returns the used frame-buffer - */ - _that.getData = function() { - return _data; - }; + /** + * Returns the used frame-buffer + */ + _that.getData = function() { + return _data; + }; - /** - * Fetches a frame from the input-stream and puts into the frame-buffer. - * The image-data is converted to gray-scale and then half-sampled if configured. - */ - _that.grab = function() { - var doHalfSample = _streamConfig.halfSample, - frame = inputStream.getFrame(), - ctxData; - if (frame) { - _ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y); - ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data; - if(doHalfSample){ - CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data); - } else { - CVUtils.computeGray(ctxData, _data, _streamConfig); - } - return true; + /** + * Fetches a frame from the input-stream and puts into the frame-buffer. + * The image-data is converted to gray-scale and then half-sampled if configured. + */ + _that.grab = function() { + var doHalfSample = _streamConfig.halfSample, + frame = inputStream.getFrame(), + ctxData; + if (frame) { + _ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y); + ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data; + if (doHalfSample){ + CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data); } else { - return false; + CVUtils.computeGray(ctxData, _data, _streamConfig); } - }; - - _that.getSize = function() { - return _size; - }; + return true; + } else { + return false; + } + }; - return _that; + _that.getSize = function() { + return _size; }; - return (FrameGrabber); -}); + return _that; +}; + +export default FrameGrabber; diff --git a/src/html_utils.js b/src/html_utils.js deleted file mode 100644 index c26c13ce..00000000 --- a/src/html_utils.js +++ /dev/null @@ -1,40 +0,0 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define([], function() { - "use strict"; - - function createNode(htmlStr) { - var temp = document.createElement('div'); - - temp.innerHTML = htmlStr; - while (temp.firstChild) { - return temp.firstChild; - } - } - - function mergeObjects(obj1, obj2) { - for (var p in obj2) { - try { - if (obj2[p].constructor == Object) { - obj1[p] = mergeObjects(obj1[p], obj2[p]); - } else { - obj1[p] = obj2[p]; - } - } catch(e) { - obj1[p] = obj2[p]; - } - } - - return obj1; - } - - return { - createNode : function(htmlStr) { - return createNode(htmlStr); - }, - mergeObjects : function(obj1, obj2) { - return mergeObjects(obj1, obj2); - } - }; -}); \ No newline at end of file diff --git a/src/i2of5_reader.js b/src/i2of5_reader.js index dc31655c..f2a2f90c 100644 --- a/src/i2of5_reader.js +++ b/src/i2of5_reader.js @@ -1,344 +1,334 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./barcode_reader", - "./html_utils" - ], - function(BarcodeReader, HTMLUtils) { - "use strict"; - - function I2of5Reader(opts) { - opts = HTMLUtils.mergeObjects(getDefaulConfig(), opts); - BarcodeReader.call(this, opts); - this.barSpaceRatio = [1, 1]; - if (opts.normalizeBarSpaceWidth) { - this.SINGLE_CODE_ERROR = 0.38; - this.AVG_CODE_ERROR = 0.09; - } +import BarcodeReader from './barcode_reader'; +const merge = require('lodash/object/merge'); + +function I2of5Reader(opts) { + opts = merge(getDefaulConfig(), opts); + BarcodeReader.call(this, opts); + this.barSpaceRatio = [1, 1]; + if (opts.normalizeBarSpaceWidth) { + this.SINGLE_CODE_ERROR = 0.38; + this.AVG_CODE_ERROR = 0.09; + } +} + +function getDefaulConfig() { + var config = {}; + + Object.keys(I2of5Reader.CONFIG_KEYS).forEach(function(key) { + config[key] = I2of5Reader.CONFIG_KEYS[key].default; + }); + return config; +} + +var N = 1, + W = 3, + properties = { + MODULO: {value: 10}, + START_PATTERN: {value: [N * 2.5, N * 2.5, N * 2.5, N * 2.5]}, + STOP_PATTERN: {value: [N * 2, N * 2, W * 2]}, + CODE_PATTERN: {value: [ + [N, N, W, W, N], + [W, N, N, N, W], + [N, W, N, N, W], + [W, W, N, N, N], + [N, N, W, N, W], + [W, N, W, N, N], + [N, W, W, N, N], + [N, N, N, W, W], + [W, N, N, W, N], + [N, W, N, W, N] + ]}, + SINGLE_CODE_ERROR: {value: 0.78, writable: true}, + AVG_CODE_ERROR: {value: 0.38, writable: true}, + MAX_CORRECTION_FACTOR: {value: 5}, + FORMAT: {value: "i2of5"} + }; + +I2of5Reader.prototype = Object.create(BarcodeReader.prototype, properties); +I2of5Reader.prototype.constructor = I2of5Reader; + +I2of5Reader.prototype._matchPattern = function(counter, code) { + if (this.config.normalizeBarSpaceWidth) { + var i, + counterSum = [0, 0], + codeSum = [0, 0], + correction = [0, 0], + correctionRatio = this.MAX_CORRECTION_FACTOR, + correctionRatioInverse = 1 / correctionRatio; + + for (i = 0; i < counter.length; i++) { + counterSum[i % 2] += counter[i]; + codeSum[i % 2] += code[i]; } - - function getDefaulConfig() { - var config = {}; - - Object.keys(I2of5Reader.CONFIG_KEYS).forEach(function(key) { - config[key] = I2of5Reader.CONFIG_KEYS[key]['default']; - }); - return config; + correction[0] = codeSum[0] / counterSum[0]; + correction[1] = codeSum[1] / counterSum[1]; + + correction[0] = Math.max(Math.min(correction[0], correctionRatio), correctionRatioInverse); + correction[1] = Math.max(Math.min(correction[1], correctionRatio), correctionRatioInverse); + this.barSpaceRatio = correction; + for (i = 0; i < counter.length; i++) { + counter[i] *= this.barSpaceRatio[i % 2]; } + } + return BarcodeReader.prototype._matchPattern.call(this, counter, code); +}; + +I2of5Reader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized, + epsilon = self.AVG_CODE_ERROR; + + isWhite = isWhite || false; + tryHarder = tryHarder || false; + + if (!offset) { + offset = self._nextSet(self._row); + } - var N = 1, - W = 3, - properties = { - MODULO : {value: 10}, - START_PATTERN : {value: [N*2.5, N*2.5, N*2.5, N*2.5]}, - STOP_PATTERN : {value: [N*2, N*2, W*2]}, - CODE_PATTERN : {value: [ - [N, N, W, W, N], - [W, N, N, N, W], - [N, W, N, N, W], - [W, W, N, N, N], - [N, N, W, N, W], - [W, N, W, N, N], - [N, W, W, N, N], - [N, N, N, W, W], - [W, N, N, W, N], - [N, W, N, W, N] - ]}, - SINGLE_CODE_ERROR: {value: 0.78, writable: true}, - AVG_CODE_ERROR: {value: 0.38, writable: true}, - MAX_CORRECTION_FACTOR: {value: 5}, - FORMAT: {value: "i2of5"} - }; + for ( i = 0; i < pattern.length; i++) { + counter[i] = 0; + } - I2of5Reader.prototype = Object.create(BarcodeReader.prototype, properties); - I2of5Reader.prototype.constructor = I2of5Reader; - - I2of5Reader.prototype._matchPattern = function(counter, code) { - if (this.config.normalizeBarSpaceWidth) { - var i, - counterSum = [0, 0], - codeSum = [0, 0], - correction = [0, 0], - correctionRatio = this.MAX_CORRECTION_FACTOR, - correctionRatioInverse = 1 / correctionRatio; - - for (i = 0; i < counter.length; i++) { - counterSum[i % 2] += counter[i]; - codeSum[i % 2] += code[i]; - } - correction[0] = codeSum[0] / counterSum[0]; - correction[1] = codeSum[1] / counterSum[1]; - - correction[0] = Math.max(Math.min(correction[0], correctionRatio), correctionRatioInverse); - correction[1] = Math.max(Math.min(correction[1], correctionRatio), correctionRatioInverse); - this.barSpaceRatio = correction; - for (i = 0; i < counter.length; i++) { - counter[i] *= this.barSpaceRatio[i % 2]; + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for ( j = 0; j < counter.length; j++) { + sum += counter[j]; } - } - return BarcodeReader.prototype._matchPattern.call(this, counter, code); - }; + normalized = self._normalize(counter); + if (normalized) { + error = self._matchPattern(normalized, pattern); - I2of5Reader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) { - var counter = [], - self = this, - i, - counterPos = 0, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }, - error, - j, - sum, - normalized, - epsilon = self.AVG_CODE_ERROR; - - isWhite = isWhite || false; - tryHarder = tryHarder || false; - - if (!offset) { - offset = self._nextSet(self._row); - } - - for ( i = 0; i < pattern.length; i++) { - counter[i] = 0; - } - - for ( i = offset; i < self._row.length; i++) { - if (self._row[i] ^ isWhite) { - counter[counterPos]++; - } else { - if (counterPos === counter.length - 1) { - sum = 0; - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - error = self._matchPattern(normalized, pattern); - - if (error < epsilon) { - bestMatch.error = error; - bestMatch.start = i - sum; - bestMatch.end = i; - return bestMatch; - } - } - if (tryHarder) { - for (j = 0; j < counter.length - 2; j++) { - counter[j] = counter[j + 2]; - } - counter[counter.length - 2] = 0; - counter[counter.length - 1] = 0; - counterPos--; - } else { - return null; - } - } else { - counterPos++; + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; } - counter[counterPos] = 1; - isWhite = !isWhite; } - } - return null; - }; - - I2of5Reader.prototype._findStart = function() { - var self = this, - leadingWhitespaceStart, - offset = self._nextSet(self._row), - startInfo, - narrowBarWidth = 1; - - while(!startInfo) { - startInfo = self._findPattern(self.START_PATTERN, offset, false, true); - if (!startInfo) { - return null; - } - narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / 4); - leadingWhitespaceStart = startInfo.start - narrowBarWidth*10; - if (leadingWhitespaceStart >= 0) { - if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { - return startInfo; + if (tryHarder) { + for (j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; } - offset = startInfo.end; - startInfo = null; - } - }; - - I2of5Reader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; - - trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } + } else { + counterPos++; } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; +}; + +I2of5Reader.prototype._findStart = function() { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo, + narrowBarWidth = 1; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset, false, true); + if (!startInfo) { return null; - }; - - I2of5Reader.prototype._findEnd = function() { - var self = this, - endInfo, - tmp; - - self._row.reverse(); - endInfo = self._findPattern(self.STOP_PATTERN); - self._row.reverse(); - - if (endInfo === null) { - return null; + } + narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / 4); + leadingWhitespaceStart = startInfo.start - narrowBarWidth * 10; + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; } + } + offset = startInfo.end; + startInfo = null; + } +}; - // reverse numbers - tmp = endInfo.start; - endInfo.start = self._row.length - endInfo.end; - endInfo.end = self._row.length - tmp; +I2of5Reader.prototype._verifyTrailingWhitespace = function(endInfo) { + var self = this, + trailingWhitespaceEnd; - return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; - }; + trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; +}; - I2of5Reader.prototype._decodePair = function(counterPair) { - var i, - code, - codes = [], - self = this; +I2of5Reader.prototype._findEnd = function() { + var self = this, + endInfo, + tmp; - for (i = 0; i < counterPair.length; i++) { - code = self._decodeCode(counterPair[i]); - if (!code) { - return null; - } - codes.push(code); - } - return codes; - }; + self._row.reverse(); + endInfo = self._findPattern(self.STOP_PATTERN); + self._row.reverse(); - I2of5Reader.prototype._decodeCode = function(counter) { - var j, - self = this, - sum = 0, - normalized, - error, - epsilon = self.AVG_CODE_ERROR, - code, - bestMatch = { - error : Number.MAX_VALUE, - code : -1, - start : 0, - end : 0 - }; - - for ( j = 0; j < counter.length; j++) { - sum += counter[j]; - } - normalized = self._normalize(counter); - if (normalized) { - for (code = 0; code < self.CODE_PATTERN.length; code++) { - error = self._matchPattern(normalized, self.CODE_PATTERN[code]); - if (error < bestMatch.error) { - bestMatch.code = code; - bestMatch.error = error; - } - } - if (bestMatch.error < epsilon) { - return bestMatch; - } - } - return null; - }; + if (endInfo === null) { + return null; + } - I2of5Reader.prototype._decodePayload = function(counters, result, decodedCodes) { - var i, - self = this, - pos = 0, - counterLength = counters.length, - counterPair = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], - codes; - - while (pos < counterLength) { - for (i = 0; i < 5; i++) { - counterPair[0][i] = counters[pos]*this.barSpaceRatio[0]; - counterPair[1][i] = counters[pos + 1]*this.barSpaceRatio[1]; - pos += 2; - } - codes = self._decodePair(counterPair); - if (!codes) { - return null; - } - for (i = 0; i < codes.length; i++) { - result.push(codes[i].code + ""); - decodedCodes.push(codes[i]); - } - } - return codes; - }; + // reverse numbers + tmp = endInfo.start; + endInfo.start = self._row.length - endInfo.end; + endInfo.end = self._row.length - tmp; - I2of5Reader.prototype._verifyCounterLength = function(counters) { - return (counters.length % 10 === 0); - }; + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; +}; - I2of5Reader.prototype._decode = function() { - var startInfo, - endInfo, - self = this, - code, - result = [], - decodedCodes = [], - counters; - - startInfo = self._findStart(); - if (!startInfo) { - return null; - } - decodedCodes.push(startInfo); +I2of5Reader.prototype._decodePair = function(counterPair) { + var i, + code, + codes = [], + self = this; - endInfo = self._findEnd(); - if (!endInfo) { - return null; - } + for (i = 0; i < counterPair.length; i++) { + code = self._decodeCode(counterPair[i]); + if (!code) { + return null; + } + codes.push(code); + } + return codes; +}; + +I2of5Reader.prototype._decodeCode = function(counter) { + var j, + self = this, + sum = 0, + normalized, + error, + epsilon = self.AVG_CODE_ERROR, + code, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }; - counters = self._fillCounters(startInfo.end, endInfo.start, false); - if (!self._verifyCounterLength(counters)) { - return null; - } - code = self._decodePayload(counters, result, decodedCodes); - if (!code) { - return null; - } - if (result.length % 2 !== 0 || - result.length < 6) { - return null; + for ( j = 0; j < counter.length; j++) { + sum += counter[j]; + } + normalized = self._normalize(counter); + if (normalized) { + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(normalized, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; } + } + if (bestMatch.error < epsilon) { + return bestMatch; + } + } + return null; +}; + +I2of5Reader.prototype._decodePayload = function(counters, result, decodedCodes) { + var i, + self = this, + pos = 0, + counterLength = counters.length, + counterPair = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], + codes; + + while (pos < counterLength) { + for (i = 0; i < 5; i++) { + counterPair[0][i] = counters[pos] * this.barSpaceRatio[0]; + counterPair[1][i] = counters[pos + 1] * this.barSpaceRatio[1]; + pos += 2; + } + codes = self._decodePair(counterPair); + if (!codes) { + return null; + } + for (i = 0; i < codes.length; i++) { + result.push(codes[i].code + ""); + decodedCodes.push(codes[i]); + } + } + return codes; +}; + +I2of5Reader.prototype._verifyCounterLength = function(counters) { + return (counters.length % 10 === 0); +}; + +I2of5Reader.prototype._decode = function() { + var startInfo, + endInfo, + self = this, + code, + result = [], + decodedCodes = [], + counters; + + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + decodedCodes.push(startInfo); - decodedCodes.push(endInfo); - return { - code : result.join(""), - start : startInfo.start, - end : endInfo.end, - startInfo : startInfo, - decodedCodes : decodedCodes - }; - }; + endInfo = self._findEnd(); + if (!endInfo) { + return null; + } - I2of5Reader.CONFIG_KEYS = { - normalizeBarSpaceWidth: { - 'type': 'boolean', - 'default': false, - 'description': 'If true, the reader tries to normalize the' + - 'width-difference between bars and spaces' - } - }; + counters = self._fillCounters(startInfo.end, endInfo.start, false); + if (!self._verifyCounterLength(counters)) { + return null; + } + code = self._decodePayload(counters, result, decodedCodes); + if (!code) { + return null; + } + if (result.length % 2 !== 0 || + result.length < 6) { + return null; + } - return (I2of5Reader); + decodedCodes.push(endInfo); + return { + code: result.join(""), + start: startInfo.start, + end: endInfo.end, + startInfo: startInfo, + decodedCodes: decodedCodes + }; +}; + +I2of5Reader.CONFIG_KEYS = { + normalizeBarSpaceWidth: { + 'type': 'boolean', + 'default': false, + 'description': 'If true, the reader tries to normalize the' + + 'width-difference between bars and spaces' } -); \ No newline at end of file +}; + +export default I2of5Reader; diff --git a/src/image_debug.js b/src/image_debug.js index 3eb96784..77f08a62 100644 --- a/src/image_debug.js +++ b/src/image_debug.js @@ -1,49 +1,41 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(function() { - "use strict"; - - return { - drawRect: function(pos, size, ctx, style){ - ctx.strokeStyle = style.color; - ctx.fillStyle = style.color; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.strokeRect(pos.x, pos.y, size.x, size.y); - }, - drawPath: function(path, def, ctx, style) { - ctx.strokeStyle = style.color; - ctx.fillStyle = style.color; - ctx.lineWidth = style.lineWidth; - ctx.beginPath(); - ctx.moveTo(path[0][def.x], path[0][def.y]); - for (var j = 1; j < path.length; j++) { - ctx.lineTo(path[j][def.x], path[j][def.y]); - } - ctx.closePath(); - ctx.stroke(); - }, - drawImage: function(imageData, size, ctx) { - var canvasData = ctx.getImageData(0, 0, size.x, size.y), - data = canvasData.data, - imageDataPos = imageData.length, - canvasDataPos = data.length, - value; +export default { + drawRect: function(pos, size, ctx, style){ + ctx.strokeStyle = style.color; + ctx.fillStyle = style.color; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.strokeRect(pos.x, pos.y, size.x, size.y); + }, + drawPath: function(path, def, ctx, style) { + ctx.strokeStyle = style.color; + ctx.fillStyle = style.color; + ctx.lineWidth = style.lineWidth; + ctx.beginPath(); + ctx.moveTo(path[0][def.x], path[0][def.y]); + for (var j = 1; j < path.length; j++) { + ctx.lineTo(path[j][def.x], path[j][def.y]); + } + ctx.closePath(); + ctx.stroke(); + }, + drawImage: function(imageData, size, ctx) { + var canvasData = ctx.getImageData(0, 0, size.x, size.y), + data = canvasData.data, + imageDataPos = imageData.length, + canvasDataPos = data.length, + value; - if (canvasDataPos/imageDataPos !== 4) { - return false; - } - while(imageDataPos--){ - value = imageData[imageDataPos]; - data[--canvasDataPos] = 255; - data[--canvasDataPos] = value; - data[--canvasDataPos] = value; - data[--canvasDataPos] = value; - } - ctx.putImageData(canvasData, 0, 0); - return true; + if (canvasDataPos / imageDataPos !== 4) { + return false; + } + while (imageDataPos--){ + value = imageData[imageDataPos]; + data[--canvasDataPos] = 255; + data[--canvasDataPos] = value; + data[--canvasDataPos] = value; + data[--canvasDataPos] = value; } - }; - -}); + ctx.putImageData(canvasData, 0, 0); + return true; + } +}; diff --git a/src/image_loader.js b/src/image_loader.js index 09bb9f33..83dc9801 100644 --- a/src/image_loader.js +++ b/src/image_loader.js @@ -1,63 +1,56 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +var ImageLoader = {}; +ImageLoader.load = function(directory, callback, offset, size, sequence) { + var htmlImagesSrcArray = new Array(size), + htmlImagesArray = new Array(htmlImagesSrcArray.length), + i, + img, + num; -define(function() { - "use strict"; - - var ImageLoader = {}; - ImageLoader.load = function(directory, callback, offset, size, sequence) { - var htmlImagesSrcArray = new Array(size), - htmlImagesArray = new Array(htmlImagesSrcArray.length), - i, - img, - num; - - if (sequence === false) { - htmlImagesSrcArray[0] = directory; - } else { - for ( i = 0; i < htmlImagesSrcArray.length; i++) { - num = (offset + i); - htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg"; - } + if (sequence === false) { + htmlImagesSrcArray[0] = directory; + } else { + for ( i = 0; i < htmlImagesSrcArray.length; i++) { + num = (offset + i); + htmlImagesSrcArray[i] = directory + "image-" + ("00" + num).slice(-3) + ".jpg"; } - htmlImagesArray.notLoaded = []; - htmlImagesArray.addImage = function(img) { - htmlImagesArray.notLoaded.push(img); - }; - htmlImagesArray.loaded = function(loadedImg) { - var notloadedImgs = htmlImagesArray.notLoaded; - for (var x = 0; x < notloadedImgs.length; x++) { - if (notloadedImgs[x] == loadedImg) { - notloadedImgs.splice(x, 1); - for (var y = 0; y < htmlImagesSrcArray.length; y++) { - var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/")); - if (loadedImg.src.lastIndexOf(imgName) != -1) { - htmlImagesArray[y] = loadedImg; - break; - } + } + htmlImagesArray.notLoaded = []; + htmlImagesArray.addImage = function(image) { + htmlImagesArray.notLoaded.push(image); + }; + htmlImagesArray.loaded = function(loadedImg) { + var notloadedImgs = htmlImagesArray.notLoaded; + for (var x = 0; x < notloadedImgs.length; x++) { + if (notloadedImgs[x] === loadedImg) { + notloadedImgs.splice(x, 1); + for (var y = 0; y < htmlImagesSrcArray.length; y++) { + var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/")); + if (loadedImg.src.lastIndexOf(imgName) !== -1) { + htmlImagesArray[y] = loadedImg; + break; } - break; } + break; } - if (notloadedImgs.length === 0) { - console.log("Images loaded"); - callback.apply(null, [htmlImagesArray]); - } - }; - - for ( i = 0; i < htmlImagesSrcArray.length; i++) { - img = new Image(); - htmlImagesArray.addImage(img); - addOnloadHandler(img, htmlImagesArray); - img.src = htmlImagesSrcArray[i]; + } + if (notloadedImgs.length === 0) { + console.log("Images loaded"); + callback.apply(null, [htmlImagesArray]); } }; - - function addOnloadHandler(img, htmlImagesArray) { - img.onload = function() { - htmlImagesArray.loaded(this); - }; + + for ( i = 0; i < htmlImagesSrcArray.length; i++) { + img = new Image(); + htmlImagesArray.addImage(img); + addOnloadHandler(img, htmlImagesArray); + img.src = htmlImagesSrcArray[i]; } +}; + +function addOnloadHandler(img, htmlImagesArray) { + img.onload = function() { + htmlImagesArray.loaded(this); + }; +} - return (ImageLoader); -}); +export default (ImageLoader); diff --git a/src/image_wrapper.js b/src/image_wrapper.js index b520fa73..673dc068 100644 --- a/src/image_wrapper.js +++ b/src/image_wrapper.js @@ -1,427 +1,347 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define([ - "subImage", - "cv_utils", - "array_helper", - "gl-matrix" - ], - function(SubImage, CVUtils, ArrayHelper, glMatrix) { - - 'use strict'; - var vec2 = glMatrix.vec2, - mat2 = glMatrix.mat2; - - /** - * Represents a basic image combining the data and size. - * In addition, some methods for manipulation are contained. - * @param size {x,y} The size of the image in pixel - * @param data {Array} If given, a flat array containing the pixel data - * @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed) - * @param initialize {Boolean} Indicating if the array should be initialized on creation. - * @returns {ImageWrapper} - */ - function ImageWrapper(size, data, ArrayType, initialize) { - if (!data) { - if (ArrayType) { - this.data = new ArrayType(size.x * size.y); - if (ArrayType === Array && initialize) { - ArrayHelper.init(this.data, 0); - } - } else { - this.data = new Uint8Array(size.x * size.y); - if (Uint8Array === Array && initialize) { - ArrayHelper.init(this.data, 0); - } +import SubImage from './subImage'; +import CVUtils from './cv_utils'; +import ArrayHelper from './array_helper'; +import {vec2} from 'gl-matrix'; + +/** + * Represents a basic image combining the data and size. + * In addition, some methods for manipulation are contained. + * @param size {x,y} The size of the image in pixel + * @param data {Array} If given, a flat array containing the pixel data + * @param ArrayType {Type} If given, the desired DataType of the Array (may be typed/non-typed) + * @param initialize {Boolean} Indicating if the array should be initialized on creation. + * @returns {ImageWrapper} + */ +function ImageWrapper(size, data, ArrayType, initialize) { + if (!data) { + if (ArrayType) { + this.data = new ArrayType(size.x * size.y); + if (ArrayType === Array && initialize) { + ArrayHelper.init(this.data, 0); } - } else { - this.data = data; - } - this.size = size; - } - - /** - * tests if a position is within the image with a given offset - * @param imgRef {x, y} The location to test - * @param border Number the padding value in pixel - * @returns {Boolean} true if location inside the image's border, false otherwise - * @see cvd/image.h - */ - ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) { - return (imgRef.x >= border) && (imgRef.y >= border) && (imgRef.x < (this.size.x - border)) && (imgRef.y < (this.size.y - border)); - }; - - /** - * Transforms an image according to the given affine-transformation matrix. - * @param inImg ImageWrapper a image containing the information to be extracted. - * @param outImg ImageWrapper the image to be filled. The whole image out image is filled by the in image. - * @param M mat2 the matrix used to map point in the out matrix to those in the in matrix - * @param inOrig vec2 origin in the in image - * @param outOrig vec2 origin in the out image - * @returns Number the number of pixels not in the in image - * @see cvd/vision.h - */ - ImageWrapper.transform = function(inImg, outImg, M, inOrig, outOrig) { - var w = outImg.size.x, h = outImg.size.y, iw = inImg.size.x, ih = inImg.size.y; - var across = vec2.clone([M[0], M[2]]); - var down = vec2.clone([M[1], M[3]]); - var defaultValue = 0; - - var p0 = vec2.subtract(inOrig, mat2.xVec2(M, outOrig, vec2.clone()), vec2.clone()); - - var min_x = p0[0], min_y = p0[1]; - var max_x = min_x, max_y = min_y; - var p, i, j; - - var sampleFunc = ImageWrapper.sample; - - if (across[0] < 0) - min_x += w * across[0]; - else - max_x += w * across[0]; - - if (down[0] < 0) - min_x += h * down[0]; - else - max_x += h * down[0]; - - if (across[1] < 0) - min_y += w * across[1]; - else - max_y += w * across[1]; - - if (down[1] < 0) - min_y += h * down[1]; - else - max_y += h * down[1]; - - var carrigeReturn = vec2.subtract(down, vec2.scale(across, w, vec2.clone()), vec2.clone()); - - if (min_x >= 0 && min_y >= 0 && max_x < iw - 1 && max_y < ih - 1) { - p = p0; - for ( i = 0; i < h; ++i, vec2.add(p, carrigeReturn)) - for ( j = 0; j < w; ++j, vec2.add(p, across)) - outImg.set(j, i, sampleFunc(inImg, p[0], p[1])); - return 0; - } else { - var x_bound = iw - 1; - var y_bound = ih - 1; - var count = 0; - p = p0; - for ( i = 0; i < h; ++i, vec2.add(p, carrigeReturn)) { - for ( j = 0; j < w; ++j, vec2.add(p, across)) { - if (0 <= p[0] && 0 <= p[1] && p[0] < x_bound && p[1] < y_bound) { - outImg.set(j, i, sampleFunc(inImg, p[0], p[1])); - } else { - outImg.set(j, i, defaultValue); ++count; - } - } + this.data = new Uint8Array(size.x * size.y); + if (Uint8Array === Array && initialize) { + ArrayHelper.init(this.data, 0); } - return count; } - }; - - /** - * Performs bilinear sampling - * @param inImg Image to extract sample from - * @param x the x-coordinate - * @param y the y-coordinate - * @returns the sampled value - * @see cvd/vision.h - */ - ImageWrapper.sample = function(inImg, x, y) { - var lx = Math.floor(x); - var ly = Math.floor(y); - var w = inImg.size.x; - var base = ly * inImg.size.x + lx; - var a = inImg.data[base + 0]; - var b = inImg.data[base + 1]; - var c = inImg.data[base + w]; - var d = inImg.data[base + w + 1]; - var e = a - b; - x -= lx; - y -= ly; - - var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a); - return result; - }; - - /** - * Initializes a given array. Sets each element to zero. - * @param array {Array} The array to initialize - */ - ImageWrapper.clearArray = function(array) { - var l = array.length; - while (l--) { - array[l] = 0; - } - }; - - /** - * Creates a {SubImage} from the current image ({this}). - * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) - * @param size {ImageRef} The size of the resulting image - * @returns {SubImage} A shared part of the original image - */ - ImageWrapper.prototype.subImage = function(from, size) { - return new SubImage(from, size, this); - }; - - /** - * Creates an {ImageWrapper) and copies the needed underlying image-data area - * @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied - * @param from {ImageRef} The location where to copy from (top-left location) - */ - ImageWrapper.prototype.subImageAsCopy = function(imageWrapper, from) { - var sizeY = imageWrapper.size.y, sizeX = imageWrapper.size.x; - var x, y; - for ( x = 0; x < sizeX; x++) { - for ( y = 0; y < sizeY; y++) { - imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x]; - } - } - }; - - ImageWrapper.prototype.copyTo = function(imageWrapper) { - var length = this.data.length, srcData = this.data, dstData = imageWrapper.data; - - while (length--) { - dstData[length] = srcData[length]; - } - }; - - /** - * Retrieves a given pixel position from the image - * @param x {Number} The x-position - * @param y {Number} The y-position - * @returns {Number} The grayscale value at the pixel-position - */ - ImageWrapper.prototype.get = function(x, y) { - return this.data[y * this.size.x + x]; - }; - - /** - * Retrieves a given pixel position from the image - * @param x {Number} The x-position - * @param y {Number} The y-position - * @returns {Number} The grayscale value at the pixel-position - */ - ImageWrapper.prototype.getSafe = function(x, y) { - var i; - - if (!this.indexMapping) { - this.indexMapping = { - x : [], - y : [] - }; - for (i = 0; i < this.size.x; i++) { - this.indexMapping.x[i] = i; - this.indexMapping.x[i + this.size.x] = i; - } - for (i = 0; i < this.size.y; i++) { - this.indexMapping.y[i] = i; - this.indexMapping.y[i + this.size.y] = i; - } + } else { + this.data = data; + } + this.size = size; +} + +/** + * tests if a position is within the image with a given offset + * @param imgRef {x, y} The location to test + * @param border Number the padding value in pixel + * @returns {Boolean} true if location inside the image's border, false otherwise + * @see cvd/image.h + */ +ImageWrapper.prototype.inImageWithBorder = function(imgRef, border) { + return (imgRef.x >= border) + && (imgRef.y >= border) + && (imgRef.x < (this.size.x - border)) + && (imgRef.y < (this.size.y - border)); +}; + +/** + * Performs bilinear sampling + * @param inImg Image to extract sample from + * @param x the x-coordinate + * @param y the y-coordinate + * @returns the sampled value + * @see cvd/vision.h + */ +ImageWrapper.sample = function(inImg, x, y) { + var lx = Math.floor(x); + var ly = Math.floor(y); + var w = inImg.size.x; + var base = ly * inImg.size.x + lx; + var a = inImg.data[base + 0]; + var b = inImg.data[base + 1]; + var c = inImg.data[base + w]; + var d = inImg.data[base + w + 1]; + var e = a - b; + x -= lx; + y -= ly; + + var result = Math.floor(x * (y * (e - c + d) - e) + y * (c - a) + a); + return result; +}; + +/** + * Initializes a given array. Sets each element to zero. + * @param array {Array} The array to initialize + */ +ImageWrapper.clearArray = function(array) { + var l = array.length; + while (l--) { + array[l] = 0; + } +}; + +/** + * Creates a {SubImage} from the current image ({this}). + * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) + * @param size {ImageRef} The size of the resulting image + * @returns {SubImage} A shared part of the original image + */ +ImageWrapper.prototype.subImage = function(from, size) { + return new SubImage(from, size, this); +}; + +/** + * Creates an {ImageWrapper) and copies the needed underlying image-data area + * @param imageWrapper {ImageWrapper} The target {ImageWrapper} where the data should be copied + * @param from {ImageRef} The location where to copy from (top-left location) + */ +ImageWrapper.prototype.subImageAsCopy = function(imageWrapper, from) { + var sizeY = imageWrapper.size.y, sizeX = imageWrapper.size.x; + var x, y; + for ( x = 0; x < sizeX; x++) { + for ( y = 0; y < sizeY; y++) { + imageWrapper.data[y * sizeX + x] = this.data[(from.y + y) * this.size.x + from.x + x]; } - return this.data[(this.indexMapping.y[y + this.size.y]) * this.size.x + this.indexMapping.x[x + this.size.x]]; - }; + } +}; - /** - * Sets a given pixel position in the image - * @param x {Number} The x-position - * @param y {Number} The y-position - * @param value {Number} The grayscale value to set - * @returns {ImageWrapper} The Image itself (for possible chaining) - */ - ImageWrapper.prototype.set = function(x, y, value) { - this.data[y * this.size.x + x] = value; - return this; - }; +ImageWrapper.prototype.copyTo = function(imageWrapper) { + var length = this.data.length, srcData = this.data, dstData = imageWrapper.data; - /** - * Sets the border of the image (1 pixel) to zero - */ - ImageWrapper.prototype.zeroBorder = function() { - var i, width = this.size.x, height = this.size.y, data = this.data; - for ( i = 0; i < width; i++) { - data[i] = data[(height - 1) * width + i] = 0; - } - for ( i = 1; i < height - 1; i++) { - data[i * width] = data[i * width + (width - 1)] = 0; + while (length--) { + dstData[length] = srcData[length]; + } +}; + +/** + * Retrieves a given pixel position from the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ +ImageWrapper.prototype.get = function(x, y) { + return this.data[y * this.size.x + x]; +}; + +/** + * Retrieves a given pixel position from the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ +ImageWrapper.prototype.getSafe = function(x, y) { + var i; + + if (!this.indexMapping) { + this.indexMapping = { + x: [], + y: [] + }; + for (i = 0; i < this.size.x; i++) { + this.indexMapping.x[i] = i; + this.indexMapping.x[i + this.size.x] = i; } - }; - - /** - * Inverts a binary image in place - */ - ImageWrapper.prototype.invert = function() { - var data = this.data, length = data.length; - - while (length--) { - data[length] = data[length] ? 0 : 1; + for (i = 0; i < this.size.y; i++) { + this.indexMapping.y[i] = i; + this.indexMapping.y[i + this.size.y] = i; } + } + return this.data[(this.indexMapping.y[y + this.size.y]) * this.size.x + this.indexMapping.x[x + this.size.x]]; +}; + +/** + * Sets a given pixel position in the image + * @param x {Number} The x-position + * @param y {Number} The y-position + * @param value {Number} The grayscale value to set + * @returns {ImageWrapper} The Image itself (for possible chaining) + */ +ImageWrapper.prototype.set = function(x, y, value) { + this.data[y * this.size.x + x] = value; + return this; +}; + +/** + * Sets the border of the image (1 pixel) to zero + */ +ImageWrapper.prototype.zeroBorder = function() { + var i, width = this.size.x, height = this.size.y, data = this.data; + for ( i = 0; i < width; i++) { + data[i] = data[(height - 1) * width + i] = 0; + } + for ( i = 1; i < height - 1; i++) { + data[i * width] = data[i * width + (width - 1)] = 0; + } +}; - }; +/** + * Inverts a binary image in place + */ +ImageWrapper.prototype.invert = function() { + var data = this.data, length = data.length; - ImageWrapper.prototype.convolve = function(kernel) { - var x, y, kx, ky, kSize = (kernel.length / 2) | 0, accu = 0; - for ( y = 0; y < this.size.y; y++) { - for ( x = 0; x < this.size.x; x++) { - accu = 0; - for ( ky = -kSize; ky <= kSize; ky++) { - for ( kx = -kSize; kx <= kSize; kx++) { - accu += kernel[ky+kSize][kx + kSize] * this.getSafe(x + kx, y + ky); - } + while (length--) { + data[length] = data[length] ? 0 : 1; + } +}; + +ImageWrapper.prototype.convolve = function(kernel) { + var x, y, kx, ky, kSize = (kernel.length / 2) | 0, accu = 0; + for ( y = 0; y < this.size.y; y++) { + for ( x = 0; x < this.size.x; x++) { + accu = 0; + for ( ky = -kSize; ky <= kSize; ky++) { + for ( kx = -kSize; kx <= kSize; kx++) { + accu += kernel[ky + kSize][kx + kSize] * this.getSafe(x + kx, y + ky); } - this.data[y * this.size.x + x] = accu; } + this.data[y * this.size.x + x] = accu; } - }; - - ImageWrapper.prototype.moments = function(labelcount) { - var data = this.data, - x, - y, - height = this.size.y, - width = this.size.x, - val, - ysq, - labelsum = [], - i, - label, - mu11, - mu02, - mu20, - x_, - y_, - tmp, - result = [], - PI = Math.PI, - PI_4 = PI / 4; - - if (labelcount <= 0) { - return result; - } - - for ( i = 0; i < labelcount; i++) { - labelsum[i] = { - m00 : 0, - m01 : 0, - m10 : 0, - m11 : 0, - m02 : 0, - m20 : 0, - theta : 0, - rad : 0 - }; - } + } +}; + +ImageWrapper.prototype.moments = function(labelcount) { + var data = this.data, + x, + y, + height = this.size.y, + width = this.size.x, + val, + ysq, + labelsum = [], + i, + label, + mu11, + mu02, + mu20, + x_, + y_, + tmp, + result = [], + PI = Math.PI, + PI_4 = PI / 4; + + if (labelcount <= 0) { + return result; + } - for ( y = 0; y < height; y++) { - ysq = y * y; - for ( x = 0; x < width; x++) { - val = data[y * width + x]; - if (val > 0) { - label = labelsum[val - 1]; - label.m00 += 1; - label.m01 += y; - label.m10 += x; - label.m11 += x * y; - label.m02 += ysq; - label.m20 += x * x; - } - } - } + for ( i = 0; i < labelcount; i++) { + labelsum[i] = { + m00: 0, + m01: 0, + m10: 0, + m11: 0, + m02: 0, + m20: 0, + theta: 0, + rad: 0 + }; + } - for ( i = 0; i < labelcount; i++) { - label = labelsum[i]; - if (!isNaN(label.m00) && label.m00 !== 0) { - x_ = label.m10 / label.m00; - y_ = label.m01 / label.m00; - mu11 = label.m11 / label.m00 - x_ * y_; - mu02 = label.m02 / label.m00 - y_ * y_; - mu20 = label.m20 / label.m00 - x_ * x_; - tmp = (mu02 - mu20) / (2 * mu11); - tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4 ) + PI; - label.theta = (tmp * 180 / PI + 90) % 180 - 90; - if (label.theta < 0) { - label.theta += 180; - } - label.rad = tmp > PI ? tmp - PI : tmp; - label.vec = vec2.clone([Math.cos(tmp), Math.sin(tmp)]); - result.push(label); + for ( y = 0; y < height; y++) { + ysq = y * y; + for ( x = 0; x < width; x++) { + val = data[y * width + x]; + if (val > 0) { + label = labelsum[val - 1]; + label.m00 += 1; + label.m01 += y; + label.m10 += x; + label.m11 += x * y; + label.m02 += ysq; + label.m20 += x * x; } } + } - return result; - }; - - /** - * Displays the {ImageWrapper} in a given canvas - * @param canvas {Canvas} The canvas element to write to - * @param scale {Number} Scale which is applied to each pixel-value - */ - ImageWrapper.prototype.show = function(canvas, scale) { - var ctx, - frame, - data, - current, - pixel, - x, - y; - - if (!scale) { - scale = 1.0; - } - ctx = canvas.getContext('2d'); - canvas.width = this.size.x; - canvas.height = this.size.y; - frame = ctx.getImageData(0, 0, canvas.width, canvas.height); - data = frame.data; - current = 0; - for (y = 0; y < this.size.y; y++) { - for (x = 0; x < this.size.x; x++) { - pixel = y * this.size.x + x; - current = this.get(x, y) * scale; - data[pixel * 4 + 0] = current; - data[pixel * 4 + 1] = current; - data[pixel * 4 + 2] = current; - data[pixel * 4 + 3] = 255; + for ( i = 0; i < labelcount; i++) { + label = labelsum[i]; + if (!isNaN(label.m00) && label.m00 !== 0) { + x_ = label.m10 / label.m00; + y_ = label.m01 / label.m00; + mu11 = label.m11 / label.m00 - x_ * y_; + mu02 = label.m02 / label.m00 - y_ * y_; + mu20 = label.m20 / label.m00 - x_ * x_; + tmp = (mu02 - mu20) / (2 * mu11); + tmp = 0.5 * Math.atan(tmp) + (mu11 >= 0 ? PI_4 : -PI_4 ) + PI; + label.theta = (tmp * 180 / PI + 90) % 180 - 90; + if (label.theta < 0) { + label.theta += 180; } + label.rad = tmp > PI ? tmp - PI : tmp; + label.vec = vec2.clone([Math.cos(tmp), Math.sin(tmp)]); + result.push(label); } - //frame.data = data; - ctx.putImageData(frame, 0, 0); - }; + } - /** - * Displays the {SubImage} in a given canvas - * @param canvas {Canvas} The canvas element to write to - * @param scale {Number} Scale which is applied to each pixel-value - */ - ImageWrapper.prototype.overlay = function(canvas, scale, from) { - if (!scale || scale < 0 || scale > 360) { - scale = 360; - } - var hsv = [0, 1, 1]; - var rgb = [0, 0, 0]; - var whiteRgb = [255, 255, 255]; - var blackRgb = [0, 0, 0]; - var result = []; - var ctx = canvas.getContext('2d'); - var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y); - var data = frame.data; - var length = this.data.length; - while (length--) { - hsv[0] = this.data[length] * scale; - result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : CVUtils.hsv2rgb(hsv, rgb); - data[length * 4 + 0] = result[0]; - data[length * 4 + 1] = result[1]; - data[length * 4 + 2] = result[2]; - data[length * 4 + 3] = 255; + return result; +}; + +/** + * Displays the {ImageWrapper} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ +ImageWrapper.prototype.show = function(canvas, scale) { + var ctx, + frame, + data, + current, + pixel, + x, + y; + + if (!scale) { + scale = 1.0; + } + ctx = canvas.getContext('2d'); + canvas.width = this.size.x; + canvas.height = this.size.y; + frame = ctx.getImageData(0, 0, canvas.width, canvas.height); + data = frame.data; + current = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + pixel = y * this.size.x + x; + current = this.get(x, y) * scale; + data[pixel * 4 + 0] = current; + data[pixel * 4 + 1] = current; + data[pixel * 4 + 2] = current; + data[pixel * 4 + 3] = 255; } - ctx.putImageData(frame, from.x, from.y); - }; + } + //frame.data = data; + ctx.putImageData(frame, 0, 0); +}; + +/** + * Displays the {SubImage} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ +ImageWrapper.prototype.overlay = function(canvas, scale, from) { + if (!scale || scale < 0 || scale > 360) { + scale = 360; + } + var hsv = [0, 1, 1]; + var rgb = [0, 0, 0]; + var whiteRgb = [255, 255, 255]; + var blackRgb = [0, 0, 0]; + var result = []; + var ctx = canvas.getContext('2d'); + var frame = ctx.getImageData(from.x, from.y, this.size.x, this.size.y); + var data = frame.data; + var length = this.data.length; + while (length--) { + hsv[0] = this.data[length] * scale; + result = hsv[0] <= 0 ? whiteRgb : hsv[0] >= 360 ? blackRgb : CVUtils.hsv2rgb(hsv, rgb); + data[length * 4 + 0] = result[0]; + data[length * 4 + 1] = result[1]; + data[length * 4 + 2] = result[2]; + data[length * 4 + 3] = 255; + } + ctx.putImageData(frame, from.x, from.y); +}; - return (ImageWrapper); -}); \ No newline at end of file +export default ImageWrapper; diff --git a/src/input_stream.js b/src/input_stream.js index 182d8736..c8cccc9b 100644 --- a/src/input_stream.js +++ b/src/input_stream.js @@ -1,317 +1,317 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define(["image_loader"], function(ImageLoader) { - "use strict"; - - var InputStream = {}; - InputStream.createVideoStream = function(video) { - var that = {}, - _config = null, - _eventNames = ['canrecord', 'ended'], - _eventHandlers = {}, - _calculatedWidth, - _calculatedHeight, - _topRight = {x: 0, y: 0}, - _canvasSize = {x: 0, y: 0}; - - function initSize() { - var width = video.videoWidth, - height = video.videoHeight; - - _calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; - _calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; - - _canvasSize.x = _calculatedWidth; - _canvasSize.y = _calculatedHeight; +import ImageLoader from './image_loader'; + +var InputStream = {}; +InputStream.createVideoStream = function(video) { + var that = {}, + _config = null, + _eventNames = ['canrecord', 'ended'], + _eventHandlers = {}, + _calculatedWidth, + _calculatedHeight, + _topRight = {x: 0, y: 0}, + _canvasSize = {x: 0, y: 0}; + + function initSize() { + var width = video.videoWidth, + height = video.videoHeight; + + _calculatedWidth = + _config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width; + _calculatedHeight = + _config.size ? width / height > 1 ? Math.floor((height / width) * _config.size) : _config.size : height; + + _canvasSize.x = _calculatedWidth; + _canvasSize.y = _calculatedHeight; + } + + that.getRealWidth = function() { + return video.videoWidth; + }; + + that.getRealHeight = function() { + return video.videoHeight; + }; + + that.getWidth = function() { + return _calculatedWidth; + }; + + that.getHeight = function() { + return _calculatedHeight; + }; + + that.setWidth = function(width) { + _calculatedWidth = width; + }; + + that.setHeight = function(height) { + _calculatedHeight = height; + }; + + that.setInputStream = function(config) { + _config = config; + video.src = (typeof config.src !== 'undefined') ? config.src : ''; + }; + + that.ended = function() { + return video.ended; + }; + + that.getConfig = function() { + return _config; + }; + + that.setAttribute = function(name, value) { + video.setAttribute(name, value); + }; + + that.pause = function() { + video.pause(); + }; + + that.play = function() { + video.play(); + }; + + that.setCurrentTime = function(time) { + if (_config.type !== "LiveStream") { + video.currentTime = time; } + }; - that.getRealWidth = function() { - return video.videoWidth; - }; - - that.getRealHeight = function() { - return video.videoHeight; - }; - - that.getWidth = function() { - return _calculatedWidth; - }; - - that.getHeight = function() { - return _calculatedHeight; - }; - - that.setWidth = function(width) { - _calculatedWidth = width; - }; - - that.setHeight = function(height) { - _calculatedHeight = height; - }; - - that.setInputStream = function(config) { - _config = config; - video.src = (typeof config.src !== 'undefined') ? config.src : ''; - }; - - that.ended = function() { - return video.ended; - }; - - that.getConfig = function() { - return _config; - }; - - that.setAttribute = function(name, value) { - video.setAttribute(name, value); - }; - - that.pause = function() { - video.pause(); - }; - - that.play = function() { - video.play(); - }; - - that.setCurrentTime = function(time) { - if (_config.type !== "LiveStream") - video.currentTime = time; - }; - - that.addEventListener = function(event, f, bool) { - if (_eventNames.indexOf(event) !== -1) { - if (!_eventHandlers[event]) { - _eventHandlers[event] = []; - } - _eventHandlers[event].push(f); - } else { - video.addEventListener(event, f, bool); - } - }; - - that.clearEventHandlers = function() { - _eventNames.forEach(function(eventName) { - var handlers = _eventHandlers[eventName]; - if (handlers && handlers.length > 0) { - handlers.forEach(function(handler) { - video.removeEventListener(eventName, handler); - }); - } - }); - }; - - that.trigger = function(eventName, args) { - var j, - handlers = _eventHandlers[eventName]; - - if (eventName === 'canrecord') { - initSize(); + that.addEventListener = function(event, f, bool) { + if (_eventNames.indexOf(event) !== -1) { + if (!_eventHandlers[event]) { + _eventHandlers[event] = []; } - if (handlers && handlers.length > 0) { - for ( j = 0; j < handlers.length; j++) { - handlers[j].apply(that, args); - } - } - }; - - that.setTopRight = function(topRight) { - _topRight.x = topRight.x; - _topRight.y = topRight.y; - }; - - that.getTopRight = function() { - return _topRight; - }; - - that.setCanvasSize = function(size) { - _canvasSize.x = size.x; - _canvasSize.y = size.y; - }; - - that.getCanvasSize = function() { - return _canvasSize; - }; - - that.getFrame = function() { - return video; - }; - - return that; - }; - - InputStream.createLiveStream = function(video) { - video.setAttribute("autoplay", true); - var that = InputStream.createVideoStream(video); - - that.ended = function() { - return false; - }; - - return that; - }; - - InputStream.createImageStream = function() { - var that = {}; - var _config = null; - - var width = 0, - height = 0, - frameIdx = 0, - paused = true, - loaded = false, - imgArray = null, - size = 0, - offset = 1, - baseUrl = null, - ended = false, - calculatedWidth, - calculatedHeight, - _eventNames = ['canrecord', 'ended'], - _eventHandlers = {}, - _topRight = {x: 0, y: 0}, - _canvasSize = {x: 0, y: 0}; - - function loadImages() { - loaded = false; - ImageLoader.load(baseUrl, function(imgs) { - imgArray = imgs; - width = imgs[0].width; - height = imgs[0].height; - calculatedWidth = _config.size ? width/height > 1 ? _config.size : Math.floor((width/height) * _config.size) : width; - calculatedHeight = _config.size ? width/height > 1 ? Math.floor((height/width) * _config.size) : _config.size : height; - _canvasSize.x = calculatedWidth; - _canvasSize.y = calculatedHeight; - loaded = true; - frameIdx = 0; - setTimeout(function() { - publishEvent("canrecord", []); - }, 0); - }, offset, size, _config.sequence); + _eventHandlers[event].push(f); + } else { + video.addEventListener(event, f, bool); } + }; - function publishEvent(eventName, args) { - var j, - handlers = _eventHandlers[eventName]; - + that.clearEventHandlers = function() { + _eventNames.forEach(function(eventName) { + var handlers = _eventHandlers[eventName]; if (handlers && handlers.length > 0) { - for ( j = 0; j < handlers.length; j++) { - handlers[j].apply(that, args); - } + handlers.forEach(function(handler) { + video.removeEventListener(eventName, handler); + }); + } + }); + }; + + that.trigger = function(eventName, args) { + var j, + handlers = _eventHandlers[eventName]; + + if (eventName === 'canrecord') { + initSize(); + } + if (handlers && handlers.length > 0) { + for ( j = 0; j < handlers.length; j++) { + handlers[j].apply(that, args); } } + }; + that.setTopRight = function(topRight) { + _topRight.x = topRight.x; + _topRight.y = topRight.y; + }; - that.trigger = publishEvent; + that.getTopRight = function() { + return _topRight; + }; - that.getWidth = function() { - return calculatedWidth; - }; + that.setCanvasSize = function(size) { + _canvasSize.x = size.x; + _canvasSize.y = size.y; + }; - that.getHeight = function() { - return calculatedHeight; - }; + that.getCanvasSize = function() { + return _canvasSize; + }; - that.setWidth = function(width) { - calculatedWidth = width; - }; + that.getFrame = function() { + return video; + }; - that.setHeight = function(height) { - calculatedHeight = height; - }; + return that; +}; - that.getRealWidth = function() { - return width; - }; +InputStream.createLiveStream = function(video) { + video.setAttribute("autoplay", true); + var that = InputStream.createVideoStream(video); - that.getRealHeight = function() { - return height; - }; + that.ended = function() { + return false; + }; - that.setInputStream = function(stream) { - _config = stream; - if (stream.sequence === false) { - baseUrl = stream.src; - size = 1; - } else { - baseUrl = stream.src; - size = stream.length; + return that; +}; + +InputStream.createImageStream = function() { + var that = {}; + var _config = null; + + var width = 0, + height = 0, + frameIdx = 0, + paused = true, + loaded = false, + imgArray = null, + size = 0, + offset = 1, + baseUrl = null, + ended = false, + calculatedWidth, + calculatedHeight, + _eventNames = ['canrecord', 'ended'], + _eventHandlers = {}, + _topRight = {x: 0, y: 0}, + _canvasSize = {x: 0, y: 0}; + + function loadImages() { + loaded = false; + ImageLoader.load(baseUrl, function(imgs) { + imgArray = imgs; + width = imgs[0].width; + height = imgs[0].height; + calculatedWidth = + _config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width; + calculatedHeight = + _config.size ? width / height > 1 ? Math.floor((height / width) * _config.size) : _config.size : height; + _canvasSize.x = calculatedWidth; + _canvasSize.y = calculatedHeight; + loaded = true; + frameIdx = 0; + setTimeout(function() { + publishEvent("canrecord", []); + }, 0); + }, offset, size, _config.sequence); + } + + function publishEvent(eventName, args) { + var j, + handlers = _eventHandlers[eventName]; + + if (handlers && handlers.length > 0) { + for ( j = 0; j < handlers.length; j++) { + handlers[j].apply(that, args); } - loadImages(); - }; + } + } - that.ended = function() { - return ended; - }; - that.setAttribute = function() {}; + that.trigger = publishEvent; - that.getConfig = function() { - return _config; - }; + that.getWidth = function() { + return calculatedWidth; + }; - that.pause = function() { - paused = true; - }; + that.getHeight = function() { + return calculatedHeight; + }; - that.play = function() { - paused = false; - }; + that.setWidth = function(newWidth) { + calculatedWidth = newWidth; + }; - that.setCurrentTime = function(time) { - frameIdx = time; - }; + that.setHeight = function(newHeight) { + calculatedHeight = newHeight; + }; - that.addEventListener = function(event, f) { - if (_eventNames.indexOf(event) !== -1) { - if (!_eventHandlers[event]) { - _eventHandlers[event] = []; - } - _eventHandlers[event].push(f); - } - }; - - that.setTopRight = function(topRight) { - _topRight.x = topRight.x; - _topRight.y = topRight.y; - }; - - that.getTopRight = function() { - return _topRight; - }; - - that.setCanvasSize = function(size) { - _canvasSize.x = size.x; - _canvasSize.y = size.y; - }; - - that.getCanvasSize = function() { - return _canvasSize; - }; - - that.getFrame = function() { - var frame; - - if (!loaded){ - return null; - } - if (!paused) { - frame = imgArray[frameIdx]; - if (frameIdx < (size - 1)) { - frameIdx++; - } else { - setTimeout(function() { - ended = true; - publishEvent("ended", []); - }, 0); - } + that.getRealWidth = function() { + return width; + }; + + that.getRealHeight = function() { + return height; + }; + + that.setInputStream = function(stream) { + _config = stream; + if (stream.sequence === false) { + baseUrl = stream.src; + size = 1; + } else { + baseUrl = stream.src; + size = stream.length; + } + loadImages(); + }; + + that.ended = function() { + return ended; + }; + + that.setAttribute = function() {}; + + that.getConfig = function() { + return _config; + }; + + that.pause = function() { + paused = true; + }; + + that.play = function() { + paused = false; + }; + + that.setCurrentTime = function(time) { + frameIdx = time; + }; + + that.addEventListener = function(event, f) { + if (_eventNames.indexOf(event) !== -1) { + if (!_eventHandlers[event]) { + _eventHandlers[event] = []; } - return frame; - }; + _eventHandlers[event].push(f); + } + }; + + that.setTopRight = function(topRight) { + _topRight.x = topRight.x; + _topRight.y = topRight.y; + }; + + that.getTopRight = function() { + return _topRight; + }; + + that.setCanvasSize = function(canvasSize) { + _canvasSize.x = canvasSize.x; + _canvasSize.y = canvasSize.y; + }; - return that; + that.getCanvasSize = function() { + return _canvasSize; }; - return (InputStream); -}); + that.getFrame = function() { + var frame; + + if (!loaded){ + return null; + } + if (!paused) { + frame = imgArray[frameIdx]; + if (frameIdx < (size - 1)) { + frameIdx++; + } else { + setTimeout(function() { + ended = true; + publishEvent("ended", []); + }, 0); + } + } + return frame; + }; + + return that; +}; + +export default InputStream; diff --git a/src/quagga.js b/src/quagga.js index 9fc0be7b..5a371a5e 100644 --- a/src/quagga.js +++ b/src/quagga.js @@ -1,504 +1,492 @@ -/* jshint undef: true, unused: true, browser:true, devel: true, evil: true */ -/* global define */ -define([ - "input_stream", - "image_wrapper", - "barcode_locator", - "barcode_decoder", - "frame_grabber", - "html_utils", - "config", - "events", - "camera_access", - "image_debug", - "gl-matrix", - "result_collector"], -function(InputStream, - ImageWrapper, - BarcodeLocator, - BarcodeDecoder, - FrameGrabber, - HtmlUtils, - _config, - Events, - CameraAccess, - ImageDebug, - glMatrix, - ResultCollector) { - "use strict"; - - var _inputStream, - _framegrabber, - _stopped, - _canvasContainer = { - ctx : { - image : null, - overlay : null - }, - dom : { - image : null, - overlay : null - } +import TypeDefs from './typedefs'; // eslint-disable-line no-unused-vars +import ImageWrapper from './image_wrapper'; +import BarcodeLocator from './barcode_locator'; +import BarcodeDecoder from './barcode_decoder'; +import Config from './config'; +import Events from './events'; +import CameraAccess from './camera_access'; +import ImageDebug from './image_debug'; +import {vec2} from 'gl-matrix'; +import ResultCollector from './result_collector'; + +const merge = require('lodash/object/merge'); +const InputStream = require('input_stream'); +const FrameGrabber = require('frame_grabber'); + +var _inputStream, + _framegrabber, + _stopped, + _canvasContainer = { + ctx: { + image: null, + overlay: null }, - _inputImageWrapper, - _boxSize, - _decoder, - _workerPool = [], - _onUIThread = true, - vec2 = glMatrix.vec2, - _resultCollector; - - function initializeData(imageWrapper) { - initBuffers(imageWrapper); - _decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper); - } - - function initConfig() { - if (typeof document !== "undefined") { - var vis = [{ - node: document.querySelector("div[data-controls]"), - prop: _config.controls - }, { - node: _canvasContainer.dom.overlay, - prop: _config.visual.show - }]; - - for (var i = 0; i < vis.length; i++) { - if (vis[i].node) { - if (vis[i].prop === true) { - vis[i].node.style.display = "block"; - } else { - vis[i].node.style.display = "none"; - } + dom: { + image: null, + overlay: null + } + }, + _inputImageWrapper, + _boxSize, + _decoder, + _workerPool = [], + _onUIThread = true, + _resultCollector, + _config = {}; + +function initializeData(imageWrapper) { + initBuffers(imageWrapper); + _decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper); +} + +function initConfig() { + if (typeof document !== "undefined") { + var vis = [{ + node: document.querySelector("div[data-controls]"), + prop: _config.controls + }, { + node: _canvasContainer.dom.overlay, + prop: _config.visual.show + }]; + + for (var i = 0; i < vis.length; i++) { + if (vis[i].node) { + if (vis[i].prop === true) { + vis[i].node.style.display = "block"; + } else { + vis[i].node.style.display = "none"; } } } } - - function initInputStream(cb) { - var video; - if (_config.inputStream.type == "VideoStream") { - video = document.createElement("video"); - _inputStream = InputStream.createVideoStream(video); - } else if (_config.inputStream.type == "ImageStream") { - _inputStream = InputStream.createImageStream(); - } else if (_config.inputStream.type == "LiveStream") { - var $viewport = document.querySelector("#interactive.viewport"); - if ($viewport) { - video = $viewport.querySelector("video"); - if (!video) { - video = document.createElement("video"); - $viewport.appendChild(video); - } +} + +function initInputStream(cb) { + var video; + if (_config.inputStream.type === "VideoStream") { + video = document.createElement("video"); + _inputStream = InputStream.createVideoStream(video); + } else if (_config.inputStream.type === "ImageStream") { + _inputStream = InputStream.createImageStream(); + } else if (_config.inputStream.type === "LiveStream") { + var $viewport = document.querySelector("#interactive.viewport"); + if ($viewport) { + video = $viewport.querySelector("video"); + if (!video) { + video = document.createElement("video"); + $viewport.appendChild(video); } - _inputStream = InputStream.createLiveStream(video); - CameraAccess.request(video, _config.inputStream.constraints, function(err) { - if (!err) { - _inputStream.trigger("canrecord"); - } else { - return cb(err); - } - }); } - - _inputStream.setAttribute("preload", "auto"); - _inputStream.setAttribute("autoplay", true); - _inputStream.setInputStream(_config.inputStream); - _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); + _inputStream = InputStream.createLiveStream(video); + CameraAccess.request(video, _config.inputStream.constraints, function(err) { + if (!err) { + _inputStream.trigger("canrecord"); + } else { + return cb(err); + } + }); } - function canRecord(cb) { - BarcodeLocator.checkImageConstraints(_inputStream, _config.locator); - initCanvas(); - _framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image); - initConfig(); - - if (_config.numOfWorkers > 0) { - initWorkers(function() { - console.log("Workers created"); - ready(cb); - }); - } else { - initializeData(); + _inputStream.setAttribute("preload", "auto"); + _inputStream.setAttribute("autoplay", true); + _inputStream.setInputStream(_config.inputStream); + _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); +} + +function canRecord(cb) { + BarcodeLocator.checkImageConstraints(_inputStream, _config.locator); + initCanvas(); + _framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image); + initConfig(); + + if (_config.numOfWorkers > 0) { + initWorkers(function() { + console.log("Workers created"); ready(cb); - } - } - - function ready(cb){ - _inputStream.play(); - cb(); + }); + } else { + initializeData(); + ready(cb); } - - function initCanvas() { - if (typeof document !== "undefined") { - var $viewport = document.querySelector("#interactive.viewport"); - _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); - if (!_canvasContainer.dom.image) { - _canvasContainer.dom.image = document.createElement("canvas"); - _canvasContainer.dom.image.className = "imgBuffer"; - if ($viewport && _config.inputStream.type == "ImageStream") { - $viewport.appendChild(_canvasContainer.dom.image); - } +} + +function ready(cb){ + _inputStream.play(); + cb(); +} + +function initCanvas() { + if (typeof document !== "undefined") { + var $viewport = document.querySelector("#interactive.viewport"); + _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); + if (!_canvasContainer.dom.image) { + _canvasContainer.dom.image = document.createElement("canvas"); + _canvasContainer.dom.image.className = "imgBuffer"; + if ($viewport && _config.inputStream.type === "ImageStream") { + $viewport.appendChild(_canvasContainer.dom.image); } - _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); - _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; - _canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; - - _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer"); - if (!_canvasContainer.dom.overlay) { - _canvasContainer.dom.overlay = document.createElement("canvas"); - _canvasContainer.dom.overlay.className = "drawingBuffer"; - if ($viewport) { - $viewport.appendChild(_canvasContainer.dom.overlay); - } - var clearFix = document.createElement("br"); - clearFix.setAttribute("clear", "all"); - if ($viewport) { - $viewport.appendChild(clearFix); - } + } + _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); + _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; + _canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; + + _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer"); + if (!_canvasContainer.dom.overlay) { + _canvasContainer.dom.overlay = document.createElement("canvas"); + _canvasContainer.dom.overlay.className = "drawingBuffer"; + if ($viewport) { + $viewport.appendChild(_canvasContainer.dom.overlay); + } + var clearFix = document.createElement("br"); + clearFix.setAttribute("clear", "all"); + if ($viewport) { + $viewport.appendChild(clearFix); } - _canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); - _canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; - _canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; } + _canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); + _canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; + _canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; } - - function initBuffers(imageWrapper) { - if (imageWrapper) { - _inputImageWrapper = imageWrapper; - } else { - _inputImageWrapper = new ImageWrapper({ - x : _inputStream.getWidth(), - y : _inputStream.getHeight() - }); - } - - console.log(_inputImageWrapper.size); - _boxSize = [ - vec2.clone([0, 0]), - vec2.clone([0, _inputImageWrapper.size.y]), - vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]), - vec2.clone([_inputImageWrapper.size.x, 0]) - ]; - BarcodeLocator.init(_inputImageWrapper, _config.locator); +} + +function initBuffers(imageWrapper) { + if (imageWrapper) { + _inputImageWrapper = imageWrapper; + } else { + _inputImageWrapper = new ImageWrapper({ + x: _inputStream.getWidth(), + y: _inputStream.getHeight() + }); } - function getBoundingBoxes() { - if (_config.locate) { - return BarcodeLocator.locate(); - } else { - return [[ - vec2.clone(_boxSize[0]), - vec2.clone(_boxSize[1]), - vec2.clone(_boxSize[2]), - vec2.clone(_boxSize[3])]]; - } + console.log(_inputImageWrapper.size); + _boxSize = [ + vec2.clone([0, 0]), + vec2.clone([0, _inputImageWrapper.size.y]), + vec2.clone([_inputImageWrapper.size.x, _inputImageWrapper.size.y]), + vec2.clone([_inputImageWrapper.size.x, 0]) + ]; + BarcodeLocator.init(_inputImageWrapper, _config.locator); +} + +function getBoundingBoxes() { + if (_config.locate) { + return BarcodeLocator.locate(); + } else { + return [[ + vec2.clone(_boxSize[0]), + vec2.clone(_boxSize[1]), + vec2.clone(_boxSize[2]), + vec2.clone(_boxSize[3])]]; } +} - function transformResult(result) { - var topRight = _inputStream.getTopRight(), - xOffset = topRight.x, - yOffset = topRight.y, - i; +function transformResult(result) { + var topRight = _inputStream.getTopRight(), + xOffset = topRight.x, + yOffset = topRight.y, + i; - if (!result || (xOffset === 0 && yOffset === 0)) { - return; - } + if (!result || (xOffset === 0 && yOffset === 0)) { + return; + } - if (result.line && result.line.length === 2) { - moveLine(result.line); - } - if (result.boxes && result.boxes.length > 0) { - for (i = 0; i < result.boxes.length; i++) { - moveBox(result.boxes[i]); - } + if (result.line && result.line.length === 2) { + moveLine(result.line); + } + if (result.boxes && result.boxes.length > 0) { + for (i = 0; i < result.boxes.length; i++) { + moveBox(result.boxes[i]); } + } - function moveBox(box) { - var corner = box.length; + function moveBox(box) { + var corner = box.length; - while(corner--) { - box[corner][0] += xOffset; - box[corner][1] += yOffset; - } - } - - function moveLine(line) { - line[0].x += xOffset; - line[0].y += yOffset; - line[1].x += xOffset; - line[1].y += yOffset; + while (corner--) { + box[corner][0] += xOffset; + box[corner][1] += yOffset; } } - function publishResult(result, imageData) { - if (_onUIThread) { - transformResult(result); - if (imageData && result && result.codeResult) { - if (_resultCollector) { - _resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult); - } + function moveLine(line) { + line[0].x += xOffset; + line[0].y += yOffset; + line[1].x += xOffset; + line[1].y += yOffset; + } +} + +function publishResult(result, imageData) { + if (_onUIThread) { + transformResult(result); + if (imageData && result && result.codeResult) { + if (_resultCollector) { + _resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult); } } - - Events.publish("processed", result); - if (result && result.codeResult) { - Events.publish("detected", result); - } } - function locateAndDecode() { - var result, - boxes; - - boxes = getBoundingBoxes(); - if (boxes) { - result = _decoder.decodeFromBoundingBoxes(boxes); - result = result || {}; - result.boxes = boxes; - publishResult(result, _inputImageWrapper.data); - } else { - publishResult(); - } + Events.publish("processed", result); + if (result && result.codeResult) { + Events.publish("detected", result); } - - function update() { - var availableWorker; - - if (_onUIThread) { - if (_workerPool.length > 0) { - availableWorker = _workerPool.filter(function(workerThread) { - return !workerThread.busy; - })[0]; - if (availableWorker) { - _framegrabber.attachData(availableWorker.imageData); - } else { - return; // all workers are busy - } +} + +function locateAndDecode() { + var result, + boxes; + + boxes = getBoundingBoxes(); + if (boxes) { + result = _decoder.decodeFromBoundingBoxes(boxes); + result = result || {}; + result.boxes = boxes; + publishResult(result, _inputImageWrapper.data); + } else { + publishResult(); + } +} + +function update() { + var availableWorker; + + if (_onUIThread) { + if (_workerPool.length > 0) { + availableWorker = _workerPool.filter(function(workerThread) { + return !workerThread.busy; + })[0]; + if (availableWorker) { + _framegrabber.attachData(availableWorker.imageData); } else { - _framegrabber.attachData(_inputImageWrapper.data); - } - if (_framegrabber.grab()) { - if (availableWorker) { - availableWorker.busy = true; - availableWorker.worker.postMessage({ - cmd: 'process', - imageData: availableWorker.imageData - }, [availableWorker.imageData.buffer]); - } else { - locateAndDecode(); - } + return; // all workers are busy } } else { - locateAndDecode(); + _framegrabber.attachData(_inputImageWrapper.data); } - } - - function start() { - _stopped = false; - ( function frame() { - if (!_stopped) { - update(); - if (_onUIThread && _config.inputStream.type == "LiveStream") { - window.requestAnimFrame(frame); - } + if (_framegrabber.grab()) { + if (availableWorker) { + availableWorker.busy = true; + availableWorker.worker.postMessage({ + cmd: 'process', + imageData: availableWorker.imageData + }, [availableWorker.imageData.buffer]); + } else { + locateAndDecode(); } - }()); - } - - function initWorkers(cb) { - var i; - _workerPool = []; - - for (i = 0; i < _config.numOfWorkers; i++) { - initWorker(workerInitialized); } - - function workerInitialized(workerThread) { - _workerPool.push(workerThread); - if (_workerPool.length >= _config.numOfWorkers){ - cb(); + } else { + locateAndDecode(); + } +} + +function start() { + _stopped = false; + ( function frame() { + if (!_stopped) { + update(); + if (_onUIThread && _config.inputStream.type === "LiveStream") { + window.requestAnimFrame(frame); } } - } + }()); +} - function initWorker(cb) { - var blobURL, - workerThread = { - worker: undefined, - imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), - busy: true - }; - - blobURL = generateWorkerBlob(); - workerThread.worker = new Worker(blobURL); - - workerThread.worker.onmessage = function(e) { - if (e.data.event === 'initialized') { - URL.revokeObjectURL(blobURL); - workerThread.busy = false; - workerThread.imageData = new Uint8Array(e.data.imageData); - console.log("Worker initialized"); - return cb(workerThread); - } else if (e.data.event === 'processed') { - workerThread.imageData = new Uint8Array(e.data.imageData); - workerThread.busy = false; - publishResult(e.data.result, workerThread.imageData); - } else if (e.data.event === 'error') { - console.log("Worker error: " + e.data.message); - } - }; +function initWorkers(cb) { + var i; + _workerPool = []; - workerThread.worker.postMessage({ - cmd: 'init', - size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()}, - imageData: workerThread.imageData, - config: _config - }, [workerThread.imageData.buffer]); + for (i = 0; i < _config.numOfWorkers; i++) { + initWorker(workerInitialized); } - - function workerInterface(factory) { - if (factory) { - /* jshint ignore:start */ - var Quagga = factory(); - if (!Quagga) { - self.postMessage({'event': 'error', message: 'Quagga could not be created'}); - return; - } - /* jshint ignore:end */ + function workerInitialized(workerThread) { + _workerPool.push(workerThread); + if (_workerPool.length >= _config.numOfWorkers){ + cb(); } - /* jshint ignore:start */ - var imageWrapper; - - self.onmessage = function(e) { - if (e.data.cmd === 'init') { - var config = e.data.config; - config.numOfWorkers = 0; - imageWrapper = new Quagga.ImageWrapper({ - x : e.data.size.x, - y : e.data.size.y - }, new Uint8Array(e.data.imageData)); - Quagga.init(config, ready, imageWrapper); - Quagga.onProcessed(onProcessed); - } else if (e.data.cmd === 'process') { - imageWrapper.data = new Uint8Array(e.data.imageData); - Quagga.start(); - } else if (e.data.cmd === 'setReaders') { - Quagga.setReaders(e.data.readers); - } + } +} + +function initWorker(cb) { + var blobURL, + workerThread = { + worker: undefined, + imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), + busy: true }; - function onProcessed(result) { - self.postMessage({'event': 'processed', imageData: imageWrapper.data, result: result}, [imageWrapper.data.buffer]); + blobURL = generateWorkerBlob(); + workerThread.worker = new Worker(blobURL); + + workerThread.worker.onmessage = function(e) { + if (e.data.event === 'initialized') { + URL.revokeObjectURL(blobURL); + workerThread.busy = false; + workerThread.imageData = new Uint8Array(e.data.imageData); + console.log("Worker initialized"); + return cb(workerThread); + } else if (e.data.event === 'processed') { + workerThread.imageData = new Uint8Array(e.data.imageData); + workerThread.busy = false; + publishResult(e.data.result, workerThread.imageData); + } else if (e.data.event === 'error') { + console.log("Worker error: " + e.data.message); } + }; - function ready() { - self.postMessage({'event': 'initialized', imageData: imageWrapper.data}, [imageWrapper.data.buffer]); + workerThread.worker.postMessage({ + cmd: 'init', + size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()}, + imageData: workerThread.imageData, + config: _config + }, [workerThread.imageData.buffer]); +} + + +function workerInterface(factory) { + /* eslint-disable no-undef*/ + if (factory) { + var Quagga = factory(); + if (!Quagga) { + self.postMessage({'event': 'error', message: 'Quagga could not be created'}); + return; } - /* jshint ignore:end */ } + var imageWrapper; + + self.onmessage = function(e) { + if (e.data.cmd === 'init') { + var config = e.data.config; + config.numOfWorkers = 0; + imageWrapper = new Quagga.ImageWrapper({ + x: e.data.size.x, + y: e.data.size.y + }, new Uint8Array(e.data.imageData)); + Quagga.init(config, ready, imageWrapper); + Quagga.onProcessed(onProcessed); + } else if (e.data.cmd === 'process') { + imageWrapper.data = new Uint8Array(e.data.imageData); + Quagga.start(); + } else if (e.data.cmd === 'setReaders') { + Quagga.setReaders(e.data.readers); + } + }; - function generateWorkerBlob() { - var blob, - factorySource; + function onProcessed(result) { + self.postMessage({ + 'event': 'processed', + imageData: imageWrapper.data, + result: result + }, [imageWrapper.data.buffer]); + } - /* jshint ignore:start */ - if (typeof __factorySource__ !== 'undefined') { - factorySource = __factorySource__; - } - /* jshint ignore:end */ + function ready() { // eslint-disable-line + self.postMessage({'event': 'initialized', imageData: imageWrapper.data}, [imageWrapper.data.buffer]); + } - blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'], - {type : 'text/javascript'}); + /* eslint-enable */ +} - return window.URL.createObjectURL(blob); +function generateWorkerBlob() { + var blob, + factorySource; + + /* jshint ignore:start */ + if (typeof __factorySource__ !== 'undefined') { + factorySource = __factorySource__; // eslint-disable-line no-undef } + /* jshint ignore:end */ - function setReaders(readers) { - if (_decoder) { - _decoder.setReaders(readers); - } else if (_onUIThread && _workerPool.length > 0) { - _workerPool.forEach(function(workerThread) { - workerThread.worker.postMessage({cmd: 'setReaders', readers: readers}); - }); - } + blob = new Blob(['(' + workerInterface.toString() + ')(' + factorySource + ');'], + {type: 'text/javascript'}); + + return window.URL.createObjectURL(blob); +} + +function setReaders(readers) { + if (_decoder) { + _decoder.setReaders(readers); + } else if (_onUIThread && _workerPool.length > 0) { + _workerPool.forEach(function(workerThread) { + workerThread.worker.postMessage({cmd: 'setReaders', readers: readers}); + }); } +} - return { - init : function(config, cb, imageWrapper) { - _config = HtmlUtils.mergeObjects(_config, config); - if (imageWrapper) { - _onUIThread = false; - initializeData(imageWrapper); - return cb(); - } else { - initInputStream(cb); +export default { + init: function(config, cb, imageWrapper) { + _config = merge({}, Config, config); + if (imageWrapper) { + _onUIThread = false; + initializeData(imageWrapper); + return cb(); + } else { + initInputStream(cb); + } + }, + start: function() { + start(); + }, + stop: function() { + _stopped = true; + _workerPool.forEach(function(workerThread) { + workerThread.worker.terminate(); + console.log("Worker terminated!"); + }); + _workerPool.length = 0; + if (_config.inputStream.type === "LiveStream") { + CameraAccess.release(); + _inputStream.clearEventHandlers(); + } + }, + pause: function() { + _stopped = true; + }, + onDetected: function(callback) { + Events.subscribe("detected", callback); + }, + offDetected: function(callback) { + Events.unsubscribe("detected", callback); + }, + onProcessed: function(callback) { + Events.subscribe("processed", callback); + }, + offProcessed: function(callback) { + Events.unsubscribe("processed", callback); + }, + setReaders: function(readers) { + setReaders(readers); + }, + registerResultCollector: function(resultCollector) { + if (resultCollector && typeof resultCollector.addResult === 'function') { + _resultCollector = resultCollector; + } + }, + canvas: _canvasContainer, + decodeSingle: function(config, resultCallback) { + config = merge({ + inputStream: { + type: "ImageStream", + sequence: false, + size: 800, + src: config.src + }, + numOfWorkers: 1, + locator: { + halfSample: false } - }, - start : function() { + }, config); + this.init(config, function() { + Events.once("processed", function(result) { + _stopped = true; + resultCallback.call(null, result); + }, true); start(); - }, - stop : function() { - _stopped = true; - _workerPool.forEach(function(workerThread) { - workerThread.worker.terminate(); - console.log("Worker terminated!"); - }); - _workerPool.length = 0; - if (_config.inputStream.type === "LiveStream") { - CameraAccess.release(); - _inputStream.clearEventHandlers(); - } - }, - pause: function() { - _stopped = true; - }, - onDetected : function(callback) { - Events.subscribe("detected", callback); - }, - offDetected: function(callback) { - Events.unsubscribe("detected", callback); - }, - onProcessed: function(callback) { - Events.subscribe("processed", callback); - }, - offProcessed: function(callback) { - Events.unsubscribe("processed", callback); - }, - setReaders: function(readers) { - setReaders(readers); - }, - registerResultCollector: function(resultCollector) { - if (resultCollector && typeof resultCollector.addResult === 'function') { - _resultCollector = resultCollector; - } - }, - canvas : _canvasContainer, - decodeSingle : function(config, resultCallback) { - config = HtmlUtils.mergeObjects({ - inputStream: { - type : "ImageStream", - sequence : false, - size: 800, - src: config.src - }, - numOfWorkers: 1, - locator: { - halfSample: false - } - }, config); - this.init(config, function() { - Events.once("processed", function(result) { - _stopped = true; - resultCallback.call(null, result); - }, true); - start(); - }); - }, - ImageWrapper: ImageWrapper, - ImageDebug: ImageDebug, - ResultCollector: ResultCollector - }; -}); + }); + }, + ImageWrapper: ImageWrapper, + ImageDebug: ImageDebug, + ResultCollector: ResultCollector +}; diff --git a/src/rasterizer.js b/src/rasterizer.js index 756f1d35..0e62fbd6 100644 --- a/src/rasterizer.js +++ b/src/rasterizer.js @@ -1,198 +1,195 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +import Tracer from './tracer'; /** * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization */ -define(["tracer"], function(Tracer) { - "use strict"; +var Rasterizer = { + createContour2D: function() { + return { + dir: null, + index: null, + firstVertex: null, + insideContours: null, + nextpeer: null, + prevpeer: null + }; + }, + CONTOUR_DIR: { + CW_DIR: 0, + CCW_DIR: 1, + UNKNOWN_DIR: 2 + }, + DIR: { + OUTSIDE_EDGE: -32767, + INSIDE_EDGE: -32766 + }, + create: function(imageWrapper, labelWrapper) { + var imageData = imageWrapper.data, + labelData = labelWrapper.data, + width = imageWrapper.size.x, + height = imageWrapper.size.y, + tracer = Tracer.create(imageWrapper, labelWrapper); - var Rasterizer = { - createContour2D : function() { - return { - dir : null, - index : null, - firstVertex : null, - insideContours : null, - nextpeer : null, - prevpeer : null - }; - }, - CONTOUR_DIR : { - CW_DIR : 0, - CCW_DIR : 1, - UNKNOWN_DIR : 2 - }, - DIR : { - OUTSIDE_EDGE : -32767, - INSIDE_EDGE : -32766 - }, - create : function(imageWrapper, labelWrapper) { - var imageData = imageWrapper.data, - labelData = labelWrapper.data, - width = imageWrapper.size.x, - height = imageWrapper.size.y, - tracer = Tracer.create(imageWrapper, labelWrapper); + return { + rasterize: function(depthlabel) { + var color, + bc, + lc, + labelindex, + cx, + cy, + colorMap = [], + vertex, + p, + cc, + sc, + pos, + connectedCount = 0, + i; - return { - rasterize : function(depthlabel) { - var color, - bc, - lc, - labelindex, - cx, - cy, - colorMap = [], - vertex, - p, - cc, - sc, - pos, - connectedCount = 0, - i; - - for ( i = 0; i < 400; i++) { - colorMap[i] = 0; - } + for ( i = 0; i < 400; i++) { + colorMap[i] = 0; + } - colorMap[0] = imageData[0]; - cc = null; - for ( cy = 1; cy < height - 1; cy++) { - labelindex = 0; - bc = colorMap[0]; - for ( cx = 1; cx < width - 1; cx++) { - pos = cy * width + cx; - if (labelData[pos] === 0) { - color = imageData[pos]; - if (color !== bc) { - if (labelindex === 0) { - lc = connectedCount + 1; - colorMap[lc] = color; - bc = color; - vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE); - if (vertex !== null) { - connectedCount++; - labelindex = lc; - p = Rasterizer.createContour2D(); + colorMap[0] = imageData[0]; + cc = null; + for ( cy = 1; cy < height - 1; cy++) { + labelindex = 0; + bc = colorMap[0]; + for ( cx = 1; cx < width - 1; cx++) { + pos = cy * width + cx; + if (labelData[pos] === 0) { + color = imageData[pos]; + if (color !== bc) { + if (labelindex === 0) { + lc = connectedCount + 1; + colorMap[lc] = color; + bc = color; + vertex = tracer.contourTracing(cy, cx, lc, color, Rasterizer.DIR.OUTSIDE_EDGE); + if (vertex !== null) { + connectedCount++; + labelindex = lc; + p = Rasterizer.createContour2D(); + p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; + p.index = labelindex; + p.firstVertex = vertex; + p.nextpeer = cc; + p.insideContours = null; + if (cc !== null) { + cc.prevpeer = p; + } + cc = p; + } + } else { + vertex = tracer + .contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex); + if (vertex !== null) { + p = Rasterizer.createContour2D(); + p.firstVertex = vertex; + p.insideContours = null; + if (depthlabel === 0) { + p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR; + } else { p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; - p.index = labelindex; - p.firstVertex = vertex; - p.nextpeer = cc; - p.insideContours = null; - if (cc !== null) { - cc.prevpeer = p; - } - cc = p; } - } else { - vertex = tracer.contourTracing(cy, cx, Rasterizer.DIR.INSIDE_EDGE, color, labelindex); - if (vertex !== null) { - p = Rasterizer.createContour2D(); - p.firstVertex = vertex; - p.insideContours = null; - if (depthlabel === 0) { - p.dir = Rasterizer.CONTOUR_DIR.CCW_DIR; - } else { - p.dir = Rasterizer.CONTOUR_DIR.CW_DIR; - } - p.index = depthlabel; - sc = cc; - while ((sc !== null) && sc.index !== labelindex) { - sc = sc.nextpeer; - } - if (sc !== null) { - p.nextpeer = sc.insideContours; - if (sc.insideContours !== null) { - sc.insideContours.prevpeer = p; - } - sc.insideContours = p; + p.index = depthlabel; + sc = cc; + while ((sc !== null) && sc.index !== labelindex) { + sc = sc.nextpeer; + } + if (sc !== null) { + p.nextpeer = sc.insideContours; + if (sc.insideContours !== null) { + sc.insideContours.prevpeer = p; } + sc.insideContours = p; } } - } else { - labelData[pos] = labelindex; - } - } else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { - labelindex = 0; - if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { - bc = imageData[pos]; - } else { - bc = colorMap[0]; } } else { - labelindex = labelData[pos]; - bc = colorMap[labelindex]; + labelData[pos] = labelindex; + } + } else if (labelData[pos] === Rasterizer.DIR.OUTSIDE_EDGE + || labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { + labelindex = 0; + if (labelData[pos] === Rasterizer.DIR.INSIDE_EDGE) { + bc = imageData[pos]; + } else { + bc = colorMap[0]; } + } else { + labelindex = labelData[pos]; + bc = colorMap[labelindex]; } } - sc = cc; - while (sc !== null) { - sc.index = depthlabel; - sc = sc.nextpeer; + } + sc = cc; + while (sc !== null) { + sc.index = depthlabel; + sc = sc.nextpeer; + } + return { + cc: cc, + count: connectedCount + }; + }, + debug: { + drawContour: function(canvas, firstContour) { + var ctx = canvas.getContext("2d"), + pq = firstContour, + iq, + q, + p; + + ctx.strokeStyle = "red"; + ctx.fillStyle = "red"; + ctx.lineWidth = 1; + + if (pq !== null) { + iq = pq.insideContours; + } else { + iq = null; } - return { - cc : cc, - count : connectedCount - }; - }, - debug: { - drawContour : function(canvas, firstContour) { - var ctx = canvas.getContext("2d"), - pq = firstContour, - iq, - q, - p; - - ctx.strokeStyle = "red"; - ctx.fillStyle = "red"; - ctx.lineWidth = 1; - - if (pq !== null) { - iq = pq.insideContours; + + while (pq !== null) { + if (iq !== null) { + q = iq; + iq = iq.nextpeer; } else { - iq = null; - } - - while (pq !== null) { - if (iq !== null) { - q = iq; - iq = iq.nextpeer; + q = pq; + pq = pq.nextpeer; + if (pq !== null) { + iq = pq.insideContours; } else { - q = pq; - pq = pq.nextpeer; - if (pq !== null) { - iq = pq.insideContours; - } else { - iq = null; - } - } - - switch(q.dir) { - case Rasterizer.CONTOUR_DIR.CW_DIR: - ctx.strokeStyle = "red"; - break; - case Rasterizer.CONTOUR_DIR.CCW_DIR: - ctx.strokeStyle = "blue"; - break; - case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR: - ctx.strokeStyle = "green"; - break; + iq = null; } - - p = q.firstVertex; - ctx.beginPath(); - ctx.moveTo(p.x, p.y); - do { - p = p.next; - ctx.lineTo(p.x, p.y); - } while(p !== q.firstVertex); - ctx.stroke(); } + + switch (q.dir) { + case Rasterizer.CONTOUR_DIR.CW_DIR: + ctx.strokeStyle = "red"; + break; + case Rasterizer.CONTOUR_DIR.CCW_DIR: + ctx.strokeStyle = "blue"; + break; + case Rasterizer.CONTOUR_DIR.UNKNOWN_DIR: + ctx.strokeStyle = "green"; + break; + } + + p = q.firstVertex; + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + do { + p = p.next; + ctx.lineTo(p.x, p.y); + } while (p !== q.firstVertex); + ctx.stroke(); } } - }; - } - }; + } + }; + } +}; - return (Rasterizer); -}); +export default Rasterizer; diff --git a/src/result_collector.js b/src/result_collector.js index f29bf959..987445bc 100644 --- a/src/result_collector.js +++ b/src/result_collector.js @@ -1,59 +1,57 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +import ImageDebug from './image_debug'; -define(["image_debug"], function(ImageDebug) { - "use strict"; - - function contains(codeResult, list) { - if (list) { - return list.some(function (item) { - return Object.keys(item).every(function (key) { - return item[key] === codeResult[key]; - }); +function contains(codeResult, list) { + if (list) { + return list.some(function (item) { + return Object.keys(item).every(function (key) { + return item[key] === codeResult[key]; }); - } - return false; + }); } + return false; +} - function passesFilter(codeResult, filter) { - if (typeof filter === 'function') { - return filter(codeResult); - } - return true; +function passesFilter(codeResult, filter) { + if (typeof filter === 'function') { + return filter(codeResult); } + return true; +} - return { - create: function(config) { - var canvas = document.createElement("canvas"), - ctx = canvas.getContext("2d"), - results = [], - capacity = config.capacity || 20, - capture = config.capture === true; +export default { + create: function(config) { + var canvas = document.createElement("canvas"), + ctx = canvas.getContext("2d"), + results = [], + capacity = config.capacity || 20, + capture = config.capture === true; - function matchesConstraints(codeResult) { - return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter); - } + function matchesConstraints(codeResult) { + return capacity + && codeResult + && !contains(codeResult, config.blacklist) + && passesFilter(codeResult, config.filter); + } - return { - addResult: function(data, imageSize, codeResult) { - var result = {}; + return { + addResult: function(data, imageSize, codeResult) { + var result = {}; - if (matchesConstraints(codeResult)) { - capacity--; - result.codeResult = codeResult; - if (capture) { - canvas.width = imageSize.x; - canvas.height = imageSize.y; - ImageDebug.drawImage(data, imageSize, ctx); - result.frame = canvas.toDataURL(); - } - results.push(result); + if (matchesConstraints(codeResult)) { + capacity--; + result.codeResult = codeResult; + if (capture) { + canvas.width = imageSize.x; + canvas.height = imageSize.y; + ImageDebug.drawImage(data, imageSize, ctx); + result.frame = canvas.toDataURL(); } - }, - getResults: function() { - return results; + results.push(result); } - }; - } - }; -}); + }, + getResults: function() { + return results; + } + }; + } +}; diff --git a/src/skeletonizer.js b/src/skeletonizer.js index 166f3051..3f8241bd 100644 --- a/src/skeletonizer.js +++ b/src/skeletonizer.js @@ -1,204 +1,209 @@ -/* jshint undef: true, unused: true, browser:true, devel: true, -W041: false */ -/* global define */ - -define(function() { - "use strict"; - - /* @preserve ASM BEGIN */ - function Skeletonizer(stdlib, foreign, buffer) { - "use asm"; - - var images = new stdlib.Uint8Array(buffer), - size = foreign.size | 0, - imul = stdlib.Math.imul; - - function erode(inImagePtr, outImagePtr) { - inImagePtr = inImagePtr | 0; - outImagePtr = outImagePtr | 0; - - var v = 0, - u = 0, - sum = 0, - yStart1 = 0, - yStart2 = 0, - xStart1 = 0, - xStart2 = 0, - offset = 0; - - for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { - offset = (offset + size) | 0; - for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { - yStart1 = (offset - size) | 0; - yStart2 = (offset + size) | 0; - xStart1 = (u - 1) | 0; - xStart2 = (u + 1) | 0; - sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + (images[(inImagePtr + offset + u) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0; - if ((sum | 0) == (5 | 0)) { - images[(outImagePtr + offset + u) | 0] = 1; - } else { - images[(outImagePtr + offset + u) | 0] = 0; - } +/* @preserve ASM BEGIN */ +/* eslint-disable eqeqeq*/ +function Skeletonizer(stdlib, foreign, buffer) { + "use asm"; + + var images = new stdlib.Uint8Array(buffer), + size = foreign.size | 0, + imul = stdlib.Math.imul; + + function erode(inImagePtr, outImagePtr) { + inImagePtr = inImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var v = 0, + u = 0, + sum = 0, + yStart1 = 0, + yStart2 = 0, + xStart1 = 0, + xStart2 = 0, + offset = 0; + + for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { + offset = (offset + size) | 0; + for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { + yStart1 = (offset - size) | 0; + yStart2 = (offset + size) | 0; + xStart1 = (u - 1) | 0; + xStart2 = (u + 1) | 0; + sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + + (images[(inImagePtr + offset + u) | 0] | 0) + + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0; + if ((sum | 0) == (5 | 0)) { + images[(outImagePtr + offset + u) | 0] = 1; + } else { + images[(outImagePtr + offset + u) | 0] = 0; } } - return; } + return; + } - function subtract(aImagePtr, bImagePtr, outImagePtr) { - aImagePtr = aImagePtr | 0; - bImagePtr = bImagePtr | 0; - outImagePtr = outImagePtr | 0; + function subtract(aImagePtr, bImagePtr, outImagePtr) { + aImagePtr = aImagePtr | 0; + bImagePtr = bImagePtr | 0; + outImagePtr = outImagePtr | 0; - var length = 0; + var length = 0; - length = imul(size, size) | 0; + length = imul(size, size) | 0; - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(outImagePtr + length) | 0] = ((images[(aImagePtr + length) | 0] | 0) - (images[(bImagePtr + length) | 0] | 0)) | 0; - } + while ((length | 0) > 0) { + length = (length - 1) | 0; + images[(outImagePtr + length) | 0] = + ((images[(aImagePtr + length) | 0] | 0) - (images[(bImagePtr + length) | 0] | 0)) | 0; } + } - function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) { - aImagePtr = aImagePtr | 0; - bImagePtr = bImagePtr | 0; - outImagePtr = outImagePtr | 0; + function bitwiseOr(aImagePtr, bImagePtr, outImagePtr) { + aImagePtr = aImagePtr | 0; + bImagePtr = bImagePtr | 0; + outImagePtr = outImagePtr | 0; - var length = 0; + var length = 0; - length = imul(size, size) | 0; + length = imul(size, size) | 0; - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(outImagePtr + length) | 0] = ((images[(aImagePtr + length) | 0] | 0) | (images[(bImagePtr + length) | 0] | 0)) | 0; - } + while ((length | 0) > 0) { + length = (length - 1) | 0; + images[(outImagePtr + length) | 0] = + ((images[(aImagePtr + length) | 0] | 0) | (images[(bImagePtr + length) | 0] | 0)) | 0; } + } - function countNonZero(imagePtr) { - imagePtr = imagePtr | 0; - - var sum = 0, - length = 0; + function countNonZero(imagePtr) { + imagePtr = imagePtr | 0; - length = imul(size, size) | 0; + var sum = 0, + length = 0; - while ((length | 0) > 0) { - length = (length - 1) | 0; - sum = ((sum | 0) + (images[(imagePtr + length) | 0] | 0)) | 0; - } + length = imul(size, size) | 0; - return (sum | 0); + while ((length | 0) > 0) { + length = (length - 1) | 0; + sum = ((sum | 0) + (images[(imagePtr + length) | 0] | 0)) | 0; } - function init(imagePtr, value) { - imagePtr = imagePtr | 0; - value = value | 0; + return (sum | 0); + } - var length = 0; + function init(imagePtr, value) { + imagePtr = imagePtr | 0; + value = value | 0; - length = imul(size, size) | 0; + var length = 0; - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(imagePtr + length) | 0] = value; - } + length = imul(size, size) | 0; + + while ((length | 0) > 0) { + length = (length - 1) | 0; + images[(imagePtr + length) | 0] = value; } + } - function dilate(inImagePtr, outImagePtr) { - inImagePtr = inImagePtr | 0; - outImagePtr = outImagePtr | 0; - - var v = 0, - u = 0, - sum = 0, - yStart1 = 0, - yStart2 = 0, - xStart1 = 0, - xStart2 = 0, - offset = 0; - - for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { - offset = (offset + size) | 0; - for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { - yStart1 = (offset - size) | 0; - yStart2 = (offset + size) | 0; - xStart1 = (u - 1) | 0; - xStart2 = (u + 1) | 0; - sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + (images[(inImagePtr + offset + u) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0; - if ((sum | 0) > (0 | 0)) { - images[(outImagePtr + offset + u) | 0] = 1; - } else { - images[(outImagePtr + offset + u) | 0] = 0; - } + function dilate(inImagePtr, outImagePtr) { + inImagePtr = inImagePtr | 0; + outImagePtr = outImagePtr | 0; + + var v = 0, + u = 0, + sum = 0, + yStart1 = 0, + yStart2 = 0, + xStart1 = 0, + xStart2 = 0, + offset = 0; + + for ( v = 1; (v | 0) < ((size - 1) | 0); v = (v + 1) | 0) { + offset = (offset + size) | 0; + for ( u = 1; (u | 0) < ((size - 1) | 0); u = (u + 1) | 0) { + yStart1 = (offset - size) | 0; + yStart2 = (offset + size) | 0; + xStart1 = (u - 1) | 0; + xStart2 = (u + 1) | 0; + sum = ((images[(inImagePtr + yStart1 + xStart1) | 0] | 0) + + (images[(inImagePtr + yStart1 + xStart2) | 0] | 0) + + (images[(inImagePtr + offset + u) | 0] | 0) + + (images[(inImagePtr + yStart2 + xStart1) | 0] | 0) + + (images[(inImagePtr + yStart2 + xStart2) | 0] | 0)) | 0; + if ((sum | 0) > (0 | 0)) { + images[(outImagePtr + offset + u) | 0] = 1; + } else { + images[(outImagePtr + offset + u) | 0] = 0; } } - return; } + return; + } - function memcpy(srcImagePtr, dstImagePtr) { - srcImagePtr = srcImagePtr | 0; - dstImagePtr = dstImagePtr | 0; + function memcpy(srcImagePtr, dstImagePtr) { + srcImagePtr = srcImagePtr | 0; + dstImagePtr = dstImagePtr | 0; - var length = 0; + var length = 0; - length = imul(size, size) | 0; + length = imul(size, size) | 0; - while ((length | 0) > 0) { - length = (length - 1) | 0; - images[(dstImagePtr + length) | 0] = (images[(srcImagePtr + length) | 0] | 0); - } + while ((length | 0) > 0) { + length = (length - 1) | 0; + images[(dstImagePtr + length) | 0] = (images[(srcImagePtr + length) | 0] | 0); } + } - function zeroBorder(imagePtr) { - imagePtr = imagePtr | 0; + function zeroBorder(imagePtr) { + imagePtr = imagePtr | 0; - var x = 0, - y = 0; + var x = 0, + y = 0; - for ( x = 0; (x | 0) < ((size - 1) | 0); x = (x + 1) | 0) { - images[(imagePtr + x) | 0] = 0; - images[(imagePtr + y) | 0] = 0; - y = ((y + size) - 1) | 0; - images[(imagePtr + y) | 0] = 0; - y = (y + 1) | 0; - } - for ( x = 0; (x | 0) < (size | 0); x = (x + 1) | 0) { - images[(imagePtr + y) | 0] = 0; - y = (y + 1) | 0; - } + for ( x = 0; (x | 0) < ((size - 1) | 0); x = (x + 1) | 0) { + images[(imagePtr + x) | 0] = 0; + images[(imagePtr + y) | 0] = 0; + y = ((y + size) - 1) | 0; + images[(imagePtr + y) | 0] = 0; + y = (y + 1) | 0; } - - function skeletonize() { - var subImagePtr = 0, - erodedImagePtr = 0, - tempImagePtr = 0, - skelImagePtr = 0, - sum = 0, - done = 0; - - erodedImagePtr = imul(size, size) | 0; - tempImagePtr = (erodedImagePtr + erodedImagePtr) | 0; - skelImagePtr = (tempImagePtr + erodedImagePtr) | 0; - - // init skel-image - init(skelImagePtr, 0); - zeroBorder(subImagePtr); - - do { - erode(subImagePtr, erodedImagePtr); - dilate(erodedImagePtr, tempImagePtr); - subtract(subImagePtr, tempImagePtr, tempImagePtr); - bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr); - memcpy(erodedImagePtr, subImagePtr); - sum = countNonZero(subImagePtr) | 0; - done = ((sum | 0) == 0 | 0); - } while(!done); + for ( x = 0; (x | 0) < (size | 0); x = (x + 1) | 0) { + images[(imagePtr + y) | 0] = 0; + y = (y + 1) | 0; } + } - return { - skeletonize : skeletonize - }; + function skeletonize() { + var subImagePtr = 0, + erodedImagePtr = 0, + tempImagePtr = 0, + skelImagePtr = 0, + sum = 0, + done = 0; + + erodedImagePtr = imul(size, size) | 0; + tempImagePtr = (erodedImagePtr + erodedImagePtr) | 0; + skelImagePtr = (tempImagePtr + erodedImagePtr) | 0; + + // init skel-image + init(skelImagePtr, 0); + zeroBorder(subImagePtr); + + do { + erode(subImagePtr, erodedImagePtr); + dilate(erodedImagePtr, tempImagePtr); + subtract(subImagePtr, tempImagePtr, tempImagePtr); + bitwiseOr(skelImagePtr, tempImagePtr, skelImagePtr); + memcpy(erodedImagePtr, subImagePtr); + sum = countNonZero(subImagePtr) | 0; + done = ((sum | 0) == 0 | 0); + } while (!done); } - /* @preserve ASM END */ - return Skeletonizer; -}); + return { + skeletonize: skeletonize + }; +} + +export default Skeletonizer; +/* eslint-enable eqeqeq*/ +/* @preserve ASM END */ diff --git a/src/subImage.js b/src/subImage.js index 66fe26f2..84922bba 100644 --- a/src/subImage.js +++ b/src/subImage.js @@ -1,97 +1,90 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +/** + * Construct representing a part of another {ImageWrapper}. Shares data + * between the parent and the child. + * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) + * @param size {ImageRef} The size of the resulting image + * @param I {ImageWrapper} The {ImageWrapper} to share from + * @returns {SubImage} A shared part of the original image + */ +function SubImage(from, size, I) { + if (!I) { + I = { + data: null, + size: size + }; + } + this.data = I.data; + this.originalSize = I.size; + this.I = I; -define(["typedefs"], function() { - "use strict"; + this.from = from; + this.size = size; +} - /** - * Construct representing a part of another {ImageWrapper}. Shares data - * between the parent and the child. - * @param from {ImageRef} The position where to start the {SubImage} from. (top-left corner) - * @param size {ImageRef} The size of the resulting image - * @param I {ImageWrapper} The {ImageWrapper} to share from - * @returns {SubImage} A shared part of the original image - */ - function SubImage(from, size, I) { - if (!I) { - I = { - data : null, - size : size - }; - } - this.data = I.data; - this.originalSize = I.size; - this.I = I; +/** + * Displays the {SubImage} in a given canvas + * @param canvas {Canvas} The canvas element to write to + * @param scale {Number} Scale which is applied to each pixel-value + */ +SubImage.prototype.show = function(canvas, scale) { + var ctx, + frame, + data, + current, + y, + x, + pixel; - this.from = from; - this.size = size; + if (!scale) { + scale = 1.0; } - - /** - * Displays the {SubImage} in a given canvas - * @param canvas {Canvas} The canvas element to write to - * @param scale {Number} Scale which is applied to each pixel-value - */ - SubImage.prototype.show = function(canvas, scale) { - var ctx, - frame, - data, - current, - y, - x, - pixel; - - if (!scale) { - scale = 1.0; + ctx = canvas.getContext('2d'); + canvas.width = this.size.x; + canvas.height = this.size.y; + frame = ctx.getImageData(0, 0, canvas.width, canvas.height); + data = frame.data; + current = 0; + for (y = 0; y < this.size.y; y++) { + for (x = 0; x < this.size.x; x++) { + pixel = y * this.size.x + x; + current = this.get(x, y) * scale; + data[pixel * 4 + 0] = current; + data[pixel * 4 + 1] = current; + data[pixel * 4 + 2] = current; + data[pixel * 4 + 3] = 255; } - ctx = canvas.getContext('2d'); - canvas.width = this.size.x; - canvas.height = this.size.y; - frame = ctx.getImageData(0, 0, canvas.width, canvas.height); - data = frame.data; - current = 0; - for (y = 0; y < this.size.y; y++) { - for (x = 0; x < this.size.x; x++) { - pixel = y * this.size.x + x; - current = this.get(x, y) * scale; - data[pixel * 4 + 0] = current; - data[pixel * 4 + 1] = current; - data[pixel * 4 + 2] = current; - data[pixel * 4 + 3] = 255; - } - } - frame.data = data; - ctx.putImageData(frame, 0, 0); - }; + } + frame.data = data; + ctx.putImageData(frame, 0, 0); +}; + +/** + * Retrieves a given pixel position from the {SubImage} + * @param x {Number} The x-position + * @param y {Number} The y-position + * @returns {Number} The grayscale value at the pixel-position + */ +SubImage.prototype.get = function(x, y) { + return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x]; +}; - /** - * Retrieves a given pixel position from the {SubImage} - * @param x {Number} The x-position - * @param y {Number} The y-position - * @returns {Number} The grayscale value at the pixel-position - */ - SubImage.prototype.get = function(x, y) { - return this.data[(this.from.y + y) * this.originalSize.x + this.from.x + x]; - }; +/** + * Updates the underlying data from a given {ImageWrapper} + * @param image {ImageWrapper} The updated image + */ +SubImage.prototype.updateData = function(image) { + this.originalSize = image.size; + this.data = image.data; +}; - /** - * Updates the underlying data from a given {ImageWrapper} - * @param image {ImageWrapper} The updated image - */ - SubImage.prototype.updateData = function(image) { - this.originalSize = image.size; - this.data = image.data; - }; +/** + * Updates the position of the shared area + * @param from {x,y} The new location + * @returns {SubImage} returns {this} for possible chaining + */ +SubImage.prototype.updateFrom = function(from) { + this.from = from; + return this; +}; - /** - * Updates the position of the shared area - * @param from {x,y} The new location - * @returns {SubImage} returns {this} for possible chaining - */ - SubImage.prototype.updateFrom = function(from) { - this.from = from; - return this; - }; - - return (SubImage); -}); \ No newline at end of file +export default (SubImage); diff --git a/src/tracer.js b/src/tracer.js index da52df3e..6a39afe3 100644 --- a/src/tracer.js +++ b/src/tracer.js @@ -1,108 +1,101 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - /** * http://www.codeproject.com/Tips/407172/Connected-Component-Labeling-and-Vectorization */ -define(function() { - "use strict"; - - var Tracer = { - searchDirections : [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]], - create : function(imageWrapper, labelWrapper) { - var imageData = imageWrapper.data, - labelData = labelWrapper.data, - searchDirections = this.searchDirections, - width = imageWrapper.size.x, - pos; +var Tracer = { + searchDirections: [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]], + create: function(imageWrapper, labelWrapper) { + var imageData = imageWrapper.data, + labelData = labelWrapper.data, + searchDirections = this.searchDirections, + width = imageWrapper.size.x, + pos; - function trace(current, color, label, edgelabel) { - var i, - y, - x; + function trace(current, color, label, edgelabel) { + var i, + y, + x; - for ( i = 0; i < 7; i++) { - y = current.cy + searchDirections[current.dir][0]; - x = current.cx + searchDirections[current.dir][1]; - pos = y * width + x; - if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) { - labelData[pos] = label; - current.cy = y; - current.cx = x; - return true; - } else { - if (labelData[pos] === 0) { - labelData[pos] = edgelabel; - } - current.dir = (current.dir + 1) % 8; + for ( i = 0; i < 7; i++) { + y = current.cy + searchDirections[current.dir][0]; + x = current.cx + searchDirections[current.dir][1]; + pos = y * width + x; + if ((imageData[pos] === color) && ((labelData[pos] === 0) || (labelData[pos] === label))) { + labelData[pos] = label; + current.cy = y; + current.cx = x; + return true; + } else { + if (labelData[pos] === 0) { + labelData[pos] = edgelabel; } + current.dir = (current.dir + 1) % 8; } - return false; } + return false; + } - function vertex2D(x, y, dir) { - return { - dir : dir, - x : x, - y : y, - next : null, - prev : null - }; - } + function vertex2D(x, y, dir) { + return { + dir: dir, + x: x, + y: y, + next: null, + prev: null + }; + } - function contourTracing(sy, sx, label, color, edgelabel) { - var Fv = null, - Cv, - P, - ldir, - current = { - cx : sx, - cy : sy, - dir : 0 - }; + function contourTracing(sy, sx, label, color, edgelabel) { + var Fv = null, + Cv, + P, + ldir, + current = { + cx: sx, + cy: sy, + dir: 0 + }; - if (trace(current, color, label, edgelabel)) { - Fv = vertex2D(sx, sy, current.dir); - Cv = Fv; + if (trace(current, color, label, edgelabel)) { + Fv = vertex2D(sx, sy, current.dir); + Cv = Fv; + ldir = current.dir; + P = vertex2D(current.cx, current.cy, 0); + P.prev = Cv; + Cv.next = P; + P.next = null; + Cv = P; + do { + current.dir = (current.dir + 6) % 8; + trace(current, color, label, edgelabel); + if (ldir !== current.dir) { + Cv.dir = current.dir; + P = vertex2D(current.cx, current.cy, 0); + P.prev = Cv; + Cv.next = P; + P.next = null; + Cv = P; + } else { + Cv.dir = ldir; + Cv.x = current.cx; + Cv.y = current.cy; + } ldir = current.dir; - P = vertex2D(current.cx, current.cy, 0); - P.prev = Cv; - Cv.next = P; - P.next = null; - Cv = P; - do { - current.dir = (current.dir + 6) % 8; - trace(current, color, label, edgelabel); - if (ldir != current.dir) { - Cv.dir = current.dir; - P = vertex2D(current.cx, current.cy, 0); - P.prev = Cv; - Cv.next = P; - P.next = null; - Cv = P; - } else { - Cv.dir = ldir; - Cv.x = current.cx; - Cv.y = current.cy; - } - ldir = current.dir; - } while(current.cx != sx || current.cy != sy); - Fv.prev = Cv.prev; - Cv.prev.next = Fv; - } - return Fv; + } while (current.cx !== sx || current.cy !== sy); + Fv.prev = Cv.prev; + Cv.prev.next = Fv; } - - return { - trace : function(current, color, label, edgelabel) { - return trace(current, color, label, edgelabel); - }, - contourTracing : function(sy, sx, label, color, edgelabel) { - return contourTracing(sy, sx, label, color, edgelabel); - } - }; + return Fv; } - }; - return (Tracer); -}); + return { + trace: function(current, color, label, edgelabel) { + return trace(current, color, label, edgelabel); + }, + contourTracing: function(sy, sx, label, color, edgelabel) { + return contourTracing(sy, sx, label, color, edgelabel); + } + }; + } +}; + +export default (Tracer); diff --git a/src/typedefs.js b/src/typedefs.js index dc13ab58..d3a607e6 100644 --- a/src/typedefs.js +++ b/src/typedefs.js @@ -3,7 +3,6 @@ * Normalizes browser-specific prefixes */ -glMatrixArrayType = Float32Array; if (typeof window !== 'undefined') { window.requestAnimFrame = (function () { return window.requestAnimationFrame || @@ -11,12 +10,13 @@ if (typeof window !== 'undefined') { window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || - function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { + function (/* function FrameRequestCallback */ callback) { window.setTimeout(callback, 1000 / 60); }; })(); - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + navigator.getUserMedia = navigator.getUserMedia || + navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; } Math.imul = Math.imul || function(a, b) { @@ -26,5 +26,5 @@ Math.imul = Math.imul || function(a, b) { bl = b & 0xffff; // the shift by 0 fixes the sign on the high part // the final |0 converts the unsigned value into a signed value - return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0); -}; \ No newline at end of file + return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0); +}; diff --git a/src/upc_e_reader.js b/src/upc_e_reader.js index d2dfaf0e..a60fd374 100644 --- a/src/upc_e_reader.js +++ b/src/upc_e_reader.js @@ -1,114 +1,103 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ - -define( - [ - "./ean_reader" - ], - function(EANReader) { - "use strict"; - - function UPCEReader() { - EANReader.call(this); +import EANReader from './ean_reader'; + +function UPCEReader() { + EANReader.call(this); +} + +var properties = { + CODE_FREQUENCY: {value: [ + [ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ], + [7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]}, + STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]}, + FORMAT: {value: "upc_e", writeable: false} +}; + +UPCEReader.prototype = Object.create(EANReader.prototype, properties); +UPCEReader.prototype.constructor = UPCEReader; + +UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) { + var i, + self = this, + codeFrequency = 0x0; + + for ( i = 0; i < 6; i++) { + code = self._decodeCode(code.end); + if (!code) { + return null; } + if (code.code >= self.CODE_G_START) { + code.code = code.code - self.CODE_G_START; + codeFrequency |= 1 << (5 - i); + } + result.push(code.code); + decodedCodes.push(code); + } + if (!self._determineParity(codeFrequency, result)) { + return null; + } - var properties = { - CODE_FREQUENCY : {value: [ - [ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ], - [7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]}, - STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]}, - FORMAT: {value: "upc_e", writeable: false} - }; - - UPCEReader.prototype = Object.create(EANReader.prototype, properties); - UPCEReader.prototype.constructor = UPCEReader; - - UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) { - var i, - self = this, - codeFrequency = 0x0; - - for ( i = 0; i < 6; i++) { - code = self._decodeCode(code.end); - if (!code) { - return null; - } - if (code.code >= self.CODE_G_START) { - code.code = code.code - self.CODE_G_START; - codeFrequency |= 1 << (5 - i); - } - result.push(code.code); - decodedCodes.push(code); - } - if (!self._determineParity(codeFrequency, result)) { - return null; - } + return code; +}; - return code; - }; - - UPCEReader.prototype._determineParity = function(codeFrequency, result) { - var self =this, - i, - nrSystem; - - for (nrSystem = 0; nrSystem < self.CODE_FREQUENCY.length; nrSystem++){ - for ( i = 0; i < self.CODE_FREQUENCY[nrSystem].length; i++) { - if (codeFrequency === self.CODE_FREQUENCY[nrSystem][i]) { - result.unshift(nrSystem); - result.push(i); - return true; - } - } - } - return false; - }; - - UPCEReader.prototype._convertToUPCA = function(result) { - var upca = [result[0]], - lastDigit = result[result.length - 2]; - - if (lastDigit <= 2) { - upca = upca.concat(result.slice(1, 3)) - .concat([lastDigit, 0, 0, 0, 0]) - .concat(result.slice(3, 6)); - } else if (lastDigit === 3) { - upca = upca.concat(result.slice(1, 4)) - .concat([0 ,0, 0, 0, 0]) - .concat(result.slice(4,6)); - } else if (lastDigit === 4) { - upca = upca.concat(result.slice(1, 5)) - .concat([0, 0, 0, 0, 0, result[5]]); - } else { - upca = upca.concat(result.slice(1, 6)) - .concat([0, 0, 0, 0, lastDigit]); - } +UPCEReader.prototype._determineParity = function(codeFrequency, result) { + var i, + nrSystem; - upca.push(result[result.length - 1]); - return upca; - }; + for (nrSystem = 0; nrSystem < this.CODE_FREQUENCY.length; nrSystem++){ + for ( i = 0; i < this.CODE_FREQUENCY[nrSystem].length; i++) { + if (codeFrequency === this.CODE_FREQUENCY[nrSystem][i]) { + result.unshift(nrSystem); + result.push(i); + return true; + } + } + } + return false; +}; + +UPCEReader.prototype._convertToUPCA = function(result) { + var upca = [result[0]], + lastDigit = result[result.length - 2]; + + if (lastDigit <= 2) { + upca = upca.concat(result.slice(1, 3)) + .concat([lastDigit, 0, 0, 0, 0]) + .concat(result.slice(3, 6)); + } else if (lastDigit === 3) { + upca = upca.concat(result.slice(1, 4)) + .concat([0, 0, 0, 0, 0]) + .concat(result.slice(4, 6)); + } else if (lastDigit === 4) { + upca = upca.concat(result.slice(1, 5)) + .concat([0, 0, 0, 0, 0, result[5]]); + } else { + upca = upca.concat(result.slice(1, 6)) + .concat([0, 0, 0, 0, lastDigit]); + } - UPCEReader.prototype._checksum = function(result) { - return EANReader.prototype._checksum.call(this, this._convertToUPCA(result)); - }; + upca.push(result[result.length - 1]); + return upca; +}; - UPCEReader.prototype._findEnd = function(offset, isWhite) { - isWhite = true; - return EANReader.prototype._findEnd.call(this, offset, isWhite); - }; +UPCEReader.prototype._checksum = function(result) { + return EANReader.prototype._checksum.call(this, this._convertToUPCA(result)); +}; - UPCEReader.prototype._verifyTrailingWhitespace = function(endInfo) { - var self = this, - trailingWhitespaceEnd; +UPCEReader.prototype._findEnd = function(offset, isWhite) { + isWhite = true; + return EANReader.prototype._findEnd.call(this, offset, isWhite); +}; - trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start)/2); - if (trailingWhitespaceEnd < self._row.length) { - if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { - return endInfo; - } - } - }; +UPCEReader.prototype._verifyTrailingWhitespace = function(endInfo) { + var self = this, + trailingWhitespaceEnd; - return (UPCEReader); + trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } } -); \ No newline at end of file +}; + +export default UPCEReader; diff --git a/src/upc_reader.js b/src/upc_reader.js index cd6eef2a..a9cc0529 100644 --- a/src/upc_reader.js +++ b/src/upc_reader.js @@ -1,35 +1,24 @@ -/* jshint undef: true, unused: true, browser:true, devel: true */ -/* global define */ +import EANReader from './ean_reader'; -define( - [ - "./ean_reader" - ], - function(EANReader) { - "use strict"; +function UPCReader() { + EANReader.call(this); +} - function UPCReader() { - EANReader.call(this); - } +var properties = { + FORMAT: {value: "upc_a", writeable: false} +}; - var properties = { - FORMAT: {value: "upc_a", writeable: false} - }; +UPCReader.prototype = Object.create(EANReader.prototype, properties); +UPCReader.prototype.constructor = UPCReader; - UPCReader.prototype = Object.create(EANReader.prototype, properties); - UPCReader.prototype.constructor = UPCReader; +UPCReader.prototype._decode = function() { + var result = EANReader.prototype._decode.call(this); - UPCReader.prototype._decode = function() { - var result = EANReader.prototype._decode.call(this); - - if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") { - - result.code = result.code.substring(1); - return result; - } - return null; - }; - - return (UPCReader); + if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") { + result.code = result.code.substring(1); + return result; } -); \ No newline at end of file + return null; +}; + +export default UPCReader; diff --git a/tasks/uglyasm.js b/tasks/uglyasm.js index f11b59a7..a630298c 100644 --- a/tasks/uglyasm.js +++ b/tasks/uglyasm.js @@ -11,14 +11,17 @@ module.exports = function(grunt) { var code = fs.readFileSync('dist/quagga.js', 'utf-8'), minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'), commentEnd = '/* @preserve ASM END */', - asmStartIdx = code.indexOf('/* @preserve ASM BEGIN */'), - asmEndIdx = code.indexOf(commentEnd), - asmCode = code.substring(asmStartIdx, asmEndIdx + commentEnd.length), - asmFunctionRegex = /function (\w+)\(\w+,\s*\w+,\s*\w+\)\s*\{\s*"use asm";/, + moduleFunctionRegex = /function\s*\((\w+,\s*\w+)\)\s*\{\s*\/\* \@preserve ASM BEGIN \*\//, + commentStartIdx = code.indexOf("/* @preserve ASM BEGIN */"), + asmEndIdxTmp = code.indexOf(commentEnd), + asmEndIdx = code.indexOf("}", asmEndIdxTmp), + asmCodeTmp = code.substring(commentStartIdx - Math.min(500, commentStartIdx), + asmEndIdx + 1), + asmStartIdx = asmCodeTmp.search(moduleFunctionRegex), + asmCode = asmCodeTmp.substring(asmStartIdx), asmModule, - asmModuleName, - asmCodeMinified, - asmMinifiedModuleName; + moduleArg1, + asmCodeMinified; asmCodeMinified = asmCode .replace(/\s*\/\/.*/g, '') // remove single-line comments @@ -29,28 +32,32 @@ module.exports = function(grunt) { grunt.log.debug(asmCodeMinified); - asmModule = asmCode.match(asmFunctionRegex); + asmModule = moduleFunctionRegex.exec(asmCode); if (!asmModule) { grunt.log.error("No ASM module found"); return; } - asmModuleName = asmModule[1]; - grunt.log.debug(asmModuleName); + moduleArg1 = asmModule[1]; + grunt.log.debug(moduleArg1); - asmModule = minifiedCode.match(asmFunctionRegex); - if (!asmModule) { + var insertionPoint = minifiedCode.search(moduleFunctionRegex); + if (insertionPoint === -1) { grunt.log.error("No ASM module found in minified file"); return; } + grunt.log.debug(insertionPoint); + + var insertionPointEnd = minifiedCode.indexOf(commentEnd, insertionPoint); + insertionPointEnd = minifiedCode.indexOf("}", insertionPointEnd) + 1; - asmMinifiedModuleName = asmModule[1]; - grunt.log.debug(asmMinifiedModuleName); + grunt.log.debug(insertionPointEnd); - asmCodeMinified = asmCodeMinified.replace(asmModuleName, asmMinifiedModuleName); + minifiedCode = minifiedCode.substring(0, insertionPoint) + + asmCodeMinified + + minifiedCode.substring(insertionPointEnd); - minifiedCode = minifiedCode.replace(/\/\* @preserve ASM BEGIN \*\/[^]*?\/\* @preserve ASM END \*\//, asmCodeMinified); fs.writeFileSync('dist/quagga.min.js', minifiedCode); grunt.log.ok('dist/quagga.min.js written'); }); -}; \ No newline at end of file +}; diff --git a/test-main.js b/test-main.js deleted file mode 100644 index 6665e6f5..00000000 --- a/test-main.js +++ /dev/null @@ -1,55 +0,0 @@ -var allTestFiles = []; -var TEST_REGEXP = /(spec|test)\.js$/i; - -var pathToModule = function(path) { - return path.replace(/^\/base\//, '').replace(/\.js$/, ''); -}; - -Object.keys(window.__karma__.files).forEach(function(file) { - if (TEST_REGEXP.test(file)) { - allTestFiles.push(pathToModule(file)); - } -}); - -require.config({ - baseUrl: '/base', - - paths: { - 'array_helper': 'src/array_helper', - 'cv_utils': 'src/cv_utils', - 'typedefs': 'src/typedefs', - 'cluster': 'src/cluster', - 'camera_access': 'src/camera_access', - 'events': 'src/events', - 'html_utils': 'src/html_utils', - 'quagga': 'src/quagga', - 'barcode_decoder': 'src/barcode_decoder', - 'barcode_locator': 'src/barcode_locator', - 'barcode_reader': 'src/barcode_reader', - 'bresenham': 'src/bresenham', - 'codabar_reader': 'src/codabar_reader', - 'code_39_reader': 'src/code_39_reader', - 'code_39_vin_reader': 'src/code_39_vin_reader', - 'code_128_reader': 'src/code_128_reader', - 'config': 'src/config', - 'ean_8_reader': 'src/ean_8_reader', - 'ean_reader': 'src/ean_reader', - 'frame_grabber': 'src/frame_grabber', - 'image_debug': 'src/image_debug', - 'image_loader': 'src/image_loader', - 'image_wrapper': 'src/image_wrapper', - 'input_stream': 'src/input_stream', - 'rasterizer': 'src/rasterizer', - 'skeletonizer': 'src/skeletonizer', - 'subImage': 'src/subImage', - 'tracer': 'src/tracer', - 'upc_e_reader': 'src/upc_e_reader', - 'upc_reader': 'src/upc_reader', - 'async': 'node_modules/async/lib/async', - 'gl-matrix': 'node_modules/gl-matrix/dist/gl-matrix-min', - 'result_collector': 'src/result_collector', - 'i2of5_reader': 'src/i2of5_reader' - }, - deps: allTestFiles, - callback: window.__karma__.start -}); diff --git a/test/integration/integration.spec.js b/test/integration/integration.spec.js new file mode 100644 index 00000000..6a6f50fb --- /dev/null +++ b/test/integration/integration.spec.js @@ -0,0 +1,245 @@ +const Quagga = require('../../src/quagga'); +const async = require('async'); + +describe('decodeSingle', function () { + var baseFolder = "base/test/fixtures/"; + + function generateConfig() { + return { + inputStream: { + size: 640 + }, + locator: { + patchSize: "medium", + halfSample: true + }, + numOfWorkers: 0, + decoder: { + readers: ["ean_reader"] + }, + locate: true, + src: null + }; + } + + this.timeout(10000); + + function _runTestSet(testSet, config) { + var readers = config.decoder.readers.slice(), + format, + folder; + + if (typeof readers[0] === 'string'){ + format = readers[0]; + } else { + format = readers[0].format; + } + + folder = baseFolder + format.split('_').slice(0, -1).join('_') + "/"; + + it('should decode ' + folder + " correctly", function(done) { + async.eachSeries(testSet, function (sample, callback) { + config.src = folder + sample.name; + config.readers = readers; + Quagga.decodeSingle(config, function(result) { + console.log(sample.name); + expect(result.codeResult.code).to.equal(sample.result); + expect(result.codeResult.format).to.equal(sample.format); + callback(); + }); + }, function() { + done(); + }); + }); + } + + describe("EAN", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "3574660239843"}, + {"name": "image-002.jpg", "result": "8032754490297"}, + {"name": "image-003.jpg", "result": "4006209700068"}, + /* {"name": "image-004.jpg", "result": "9002233139084"}, */ + /* {"name": "image-005.jpg", "result": "8004030044005"}, */ + {"name": "image-006.jpg", "result": "4003626011159"}, + {"name": "image-007.jpg", "result": "2111220009686"}, + {"name": "image-008.jpg", "result": "9000275609022"}, + {"name": "image-009.jpg", "result": "9004593978587"}, + {"name": "image-010.jpg", "result": "9002244845578"} + ]; + + testSet.forEach(function(sample) { + sample.format = "ean_13"; + }); + + config.decoder.readers = ['ean_reader']; + _runTestSet(testSet, config); + }); + + describe("Code128", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "0001285112001000040801"}, + // {"name": "image-002.jpg", "result": "FANAVF1461710"}, + // {"name": "image-003.jpg", "result": "673023"}, + // {"name": "image-004.jpg", "result": "010210150301625334"}, + {"name": "image-005.jpg", "result": "419055603900009001012999"}, + {"name": "image-006.jpg", "result": "419055603900009001012999"}, + {"name": "image-007.jpg", "result": "T 000003552345"}, + {"name": "image-008.jpg", "result": "FANAVF1461710"}, + {"name": "image-009.jpg", "result": "0001285112001000040801"}, + {"name": "image-010.jpg", "result": "673023"} + ]; + + testSet.forEach(function(sample) { + sample.format = "code_128"; + }); + + config.decoder.readers = ['code_128_reader']; + _runTestSet(testSet, config); + }); + + describe("Code39", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "B3% $DAD$"}, + {"name": "image-003.jpg", "result": "CODE39"}, + {"name": "image-004.jpg", "result": "QUAGGAJS"}, + /* {"name": "image-005.jpg", "result": "CODE39"}, */ + {"name": "image-006.jpg", "result": "2/4-8/16-32"}, + {"name": "image-007.jpg", "result": "2/4-8/16-32"}, + {"name": "image-008.jpg", "result": "CODE39"}, + {"name": "image-009.jpg", "result": "2/4-8/16-32"}, + {"name": "image-010.jpg", "result": "CODE39"} + ]; + + testSet.forEach(function(sample) { + sample.format = "code_39"; + }); + + config.decoder.readers = ['code_39_reader']; + _runTestSet(testSet, config); + }); + + describe("EAN-8", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "42191605"}, + {"name": "image-002.jpg", "result": "42191605"}, + {"name": "image-003.jpg", "result": "90311208"}, + {"name": "image-004.jpg", "result": "24057257"}, + {"name": "image-005.jpg", "result": "90162602"}, + {"name": "image-006.jpg", "result": "24036153"}, + {"name": "image-007.jpg", "result": "42176817"}, + {"name": "image-008.jpg", "result": "42191605"}, + {"name": "image-009.jpg", "result": "42242215"}, + {"name": "image-010.jpg", "result": "42184799"} + ]; + + testSet.forEach(function(sample) { + sample.format = "ean_8"; + }); + + config.decoder.readers = ['ean_8_reader']; + _runTestSet(testSet, config); + }); + + describe("UPC", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "882428015268"}, + {"name": "image-002.jpg", "result": "882428015268"}, + {"name": "image-003.jpg", "result": "882428015084"}, + {"name": "image-004.jpg", "result": "882428015343"}, + {"name": "image-005.jpg", "result": "882428015343"}, + /* {"name": "image-006.jpg", "result": "882428015046"}, */ + {"name": "image-007.jpg", "result": "882428015084"}, + {"name": "image-008.jpg", "result": "882428015046"}, + {"name": "image-009.jpg", "result": "039047013551"}, + {"name": "image-010.jpg", "result": "039047013551"} + ]; + + testSet.forEach(function(sample) { + sample.format = "upc_a"; + }); + + config.decoder.readers = ['upc_reader']; + _runTestSet(testSet, config); + }); + + describe("UPC-E", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "04965802"}, + {"name": "image-002.jpg", "result": "04965802"}, + {"name": "image-003.jpg", "result": "03897425"}, + {"name": "image-004.jpg", "result": "05096893"}, + {"name": "image-005.jpg", "result": "05096893"}, + {"name": "image-006.jpg", "result": "05096893"}, + {"name": "image-007.jpg", "result": "03897425"}, + {"name": "image-008.jpg", "result": "01264904"}, + /*{"name": "image-009.jpg", "result": "01264904"},*/ + {"name": "image-010.jpg", "result": "01264904"} + ]; + + testSet.forEach(function(sample) { + sample.format = "upc_e"; + }); + + config.decoder.readers = ['upc_e_reader']; + _runTestSet(testSet, config); + }); + + describe("Codabar", function() { + var config = generateConfig(), + testSet = [ + {"name": "image-001.jpg", "result": "A10/53+17-70D"}, + {"name": "image-002.jpg", "result": "B546745735B"}, + {"name": "image-003.jpg", "result": "C$399.95A"}, + {"name": "image-004.jpg", "result": "B546745735B"}, + {"name": "image-005.jpg", "result": "C$399.95A"}, + {"name": "image-006.jpg", "result": "B546745735B"}, + {"name": "image-007.jpg", "result": "C$399.95A"}, + {"name": "image-008.jpg", "result": "A16:9/4:3/3:2D"}, + {"name": "image-009.jpg", "result": "C$399.95A"}, + {"name": "image-010.jpg", "result": "C$399.95A"} + ]; + + testSet.forEach(function(sample) { + sample.format = "codabar"; + }); + + config.decoder.readers = ['codabar_reader']; + _runTestSet(testSet, config); + }); + + describe("I2of5 with localization", function() { + var config = { + inputStream: { + size: 800, + singleChannel: false + }, + locator: { + patchSize: "small", + halfSample: false + }, + numOfWorkers: 0, + decoder: { + readers: ["i2of5_reader"] + }, + locate: true, + src: null + }, + testSet = [ + {"name": "image-001.jpg", "result": "2167361334"}, + {"name": "image-002.jpg", "result": "2167361334"}, + {"name": "image-003.jpg", "result": "2167361334"}, + {"name": "image-004.jpg", "result": "2167361334"}, + {"name": "image-005.jpg", "result": "2167361334"} + ]; + testSet.forEach(function(sample) { + sample.format = "i2of5"; + }); + _runTestSet(testSet, config); + }); +}); diff --git a/test/spec/array_helper.spec.js b/test/spec/array_helper.spec.js new file mode 100644 index 00000000..e0c24f3c --- /dev/null +++ b/test/spec/array_helper.spec.js @@ -0,0 +1,53 @@ +const ArrayHelper = require('../../src/array_helper'); + +describe('init', function() { + it('initializes an array with the given value', function() { + var input = [0, 0, 0]; + ArrayHelper.init(input, 5); + expect(input).to.deep.equal([5, 5, 5]); + }); +}); + +describe('shuffle', function() { + before(function() { + sinon.stub(Math, 'random').returns(0.5); + }); + + after(function() { + sinon.restore(Math); + }); + it('shuffles the content of an array', function() { + var input = [1, 2, 3]; + expect(ArrayHelper.shuffle(input)).to.deep.equal([3, 1, 2]); + }); +}); + +describe('toPointList', function() { + it('converts an Array to a List of poitns', function() { + var input = [[1, 2], [2, 2], [3, 2]]; + expect(ArrayHelper.toPointList(input)).to.equal("[[1,2],\r\n[2,2],\r\n[3,2]]"); + }); +}); + +describe('threshold', function() { + it('returns all elements above the given threshold', function() { + var input = [1, 2, 3]; + expect(ArrayHelper.threshold(input, 2, function(score) { + return score * 1.5; + })).to.deep.equal([2, 3]); + }); +}); + +describe('maxIndex', function() { + it('gets the index of the biggest element in the array', function() { + var input = [1, 2, 3]; + expect(ArrayHelper.maxIndex(input)).to.equal(2); + }); +}); + +describe('max', function() { + it('gets the biggest element in the array', function() { + var input = [1, 3, 2]; + expect(ArrayHelper.max(input)).to.equal(3); + }); +}); diff --git a/test/spec/barcode_locator.spec.js b/test/spec/barcode_locator.spec.js new file mode 100644 index 00000000..2b573cd1 --- /dev/null +++ b/test/spec/barcode_locator.spec.js @@ -0,0 +1,130 @@ +const BarcodeLocator = require('../../src/barcode_locator'); +const Config = require('../../src/config'); +const merge = require('lodash/object/merge'); + +describe('checkImageConstraints', function() { + var config, + inputStream, + imageSize, + streamConfig = {}; + + beforeEach(function() { + imageSize = { + x: 640, y: 480 + }; + config = merge({}, Config); + inputStream = { + getWidth: function() { + return imageSize.x; + }, + getHeight: function() { + return imageSize.y; + }, + setWidth: function() {}, + setHeight: function() {}, + setTopRight: function() {}, + setCanvasSize: function() {}, + getConfig: function() { + return streamConfig; + } + }; + sinon.stub(inputStream, "setWidth", function(width) { + imageSize.x = width; + }); + sinon.stub(inputStream, "setHeight", function(height) { + imageSize.y = height; + }); + sinon.stub(inputStream, "setTopRight"); + sinon.stub(inputStream, "setCanvasSize"); + }); + + afterEach(function() { + inputStream.setWidth.restore(); + inputStream.setHeight.restore(); + }); + + it('should not adjust the image-size if not needed', function() { + var expected = {x: imageSize.x, y: imageSize.y}; + BarcodeLocator.checkImageConstraints(inputStream, config.locator); + expect(inputStream.getWidth()).to.be.equal(expected.x); + expect(inputStream.getHeight()).to.be.equal(expected.y); + }); + + it('should adjust the image-size', function() { + var expected = {x: imageSize.x, y: imageSize.y}; + + config.locator.halfSample = true; + imageSize.y += 1; + BarcodeLocator.checkImageConstraints(inputStream, config.locator); + expect(inputStream.getWidth()).to.be.equal(expected.x); + expect(inputStream.getHeight()).to.be.equal(expected.y); + }); + + it('should adjust the image-size', function() { + var expected = {x: imageSize.x, y: imageSize.y}; + + imageSize.y += 1; + config.locator.halfSample = false; + BarcodeLocator.checkImageConstraints(inputStream, config.locator); + expect(inputStream.getHeight()).to.be.equal(expected.y); + expect(inputStream.getWidth()).to.be.equal(expected.x); + }); + + it("should take the defined area into account", function() { + var expectedSize = { + x: 420, + y: 315 + }, + expectedTopRight = { + x: 115, + y: 52 + }, + expectedCanvasSize = { + x: 640, + y: 480 + }; + + streamConfig.area = { + top: "11%", + right: "15%", + bottom: "20%", + left: "18%" + }; + + config.locator.halfSample = false; + BarcodeLocator.checkImageConstraints(inputStream, config.locator); + expect(inputStream.getHeight()).to.be.equal(expectedSize.y); + expect(inputStream.getWidth()).to.be.equal(expectedSize.x); + expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight); + expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize); + }); + + it("should return the original size if set to full image", function() { + var expectedSize = { + x: 640, + y: 480 + }, + expectedTopRight = { + x: 0, + y: 0 + }, + expectedCanvasSize = { + x: 640, + y: 480 + }; + + streamConfig.area = { + top: "0%", + right: "0%", + bottom: "0%", + left: "0%" + }; + + config.locator.halfSample = false; + BarcodeLocator.checkImageConstraints(inputStream, config.locator); + expect(inputStream.getHeight()).to.be.equal(expectedSize.y); + expect(inputStream.getWidth()).to.be.equal(expectedSize.x); + expect(inputStream.setTopRight.getCall(0).args[0]).to.deep.equal(expectedTopRight); + expect(inputStream.setCanvasSize.getCall(0).args[0]).to.deep.equal(expectedCanvasSize); + }); +}); diff --git a/test/spec/camera_access.spec.js b/test/spec/camera_access.spec.js new file mode 100644 index 00000000..e583bf5d --- /dev/null +++ b/test/spec/camera_access.spec.js @@ -0,0 +1,116 @@ +const CameraAccess = require('../../src/camera_access'); + +var originalURL, + originalMediaStreamTrack, + video, + stream; + +beforeEach(function() { + var tracks = [{ + stop: function() {} + }]; + + originalURL = window.URL; + originalMediaStreamTrack = window.MediaStreamTrack; + window.MediaStreamTrack = {}; + window.URL = null; + + stream = { + getVideoTracks: function() { + return tracks; + } + }; + sinon.spy(tracks[0], "stop"); + + video = { + src: null, + addEventListener: function() {}, + removeEventListener: function() {}, + play: function() {}, + videoWidth: 320, + videoHeight: 480 + }; + sinon.stub(video, "addEventListener", function(event, cb) { + cb(); + }); + sinon.stub(video, "play"); +}); + +afterEach(function() { + window.URL = originalURL; + window.MediaStreamTrack = originalMediaStreamTrack; +}); + +describe('success', function() { + beforeEach(function() { + sinon.stub(navigator, "getUserMedia", function(constraints, success) { + success(stream); + }); + }); + + afterEach(function() { + navigator.getUserMedia.restore(); + }); + describe('request', function () { + it('should request the camera', function (done) { + CameraAccess.request(video, {}, function () { + expect(navigator.getUserMedia.calledOnce).to.equal(true); + expect(video.src).to.deep.equal(stream); + done(); + }); + }); + }); + + describe('release', function () { + it('should release the camera', function (done) { + CameraAccess.request(video, {}, function () { + expect(video.src).to.deep.equal(stream); + CameraAccess.release(); + expect(video.src.getVideoTracks()).to.have.length(1); + expect(video.src.getVideoTracks()[0].stop.calledOnce).to.equal(true); + done(); + }); + }); + }); +}); + +describe('failure', function() { + describe("permission denied", function(){ + before(function() { + sinon.stub(navigator, "getUserMedia", function(constraints, success, failure) { + failure(new Error()); + }); + }); + + after(function() { + navigator.getUserMedia.restore(); + }); + + it('should throw if getUserMedia not available', function(done) { + CameraAccess.request(video, {}, function(err) { + expect(err).to.be.defined; + done(); + }); + }); + }); + + describe("not available", function(){ + var originalGetUserMedia; + + before(function() { + originalGetUserMedia = navigator.getUserMedia; + navigator.getUserMedia = undefined; + }); + + after(function() { + navigator.getUserMedia = originalGetUserMedia; + }); + + it('should throw if getUserMedia not available', function(done) { + CameraAccess.request(video, {}, function(err) { + expect(err).to.be.defined; + done(); + }); + }); + }); +}); diff --git a/test/spec/cv_utils.spec.js b/test/spec/cv_utils.spec.js new file mode 100644 index 00000000..0da49899 --- /dev/null +++ b/test/spec/cv_utils.spec.js @@ -0,0 +1,144 @@ +const CVUtils = require('../../src/cv_utils'); + +describe('imageRef', function() { + it('gets the image Reference for a coordinate', function() { + var res = CVUtils.imageRef(1, 2); + expect(res.x).to.equal(1); + expect(res.y).to.equal(2); + expect(res.toVec2()[0]).to.equal(1); + }); +}); + +describe('calculatePatchSize', function() { + it('should not throw an error in case of valid image size', function() { + var expected = {x: 32, y: 32}, + patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480}); + + expect(patchSize).to.be.deep.equal(expected); + }); + + it('should thow an error if image size it not valid', function() { + var expected = {x: 32, y: 32}, + patchSize = CVUtils.calculatePatchSize("medium", {x: 640, y: 480}); + + expect(patchSize).to.be.deep.equal(expected); + }); +}); + +describe('_parseCSSDimensionValues', function() { + it("should convert a percentual value correctly", function() { + var expected = { + value: 10, + unit: "%" + }, + result = CVUtils._parseCSSDimensionValues("10%"); + + expect(result).to.be.deep.equal(expected); + }); + + it("should convert a 0% value correctly", function() { + var expected = { + value: 100, + unit: "%" + }, + result = CVUtils._parseCSSDimensionValues("100%"); + + expect(result).to.be.deep.equal(expected); + }); + + it("should convert a 100% value correctly", function() { + var expected = { + value: 0, + unit: "%" + }, + result = CVUtils._parseCSSDimensionValues("0%"); + + expect(result).to.be.deep.equal(expected); + }); + + it("should convert a pixel value to percentage", function() { + var expected = { + value: 26.3, + unit: "%" + }, + result = CVUtils._parseCSSDimensionValues("26.3px"); + + console.log(result); + expect(result).to.be.deep.equal(expected); + }); +}); + +describe("_dimensionsConverters", function(){ + var context; + + beforeEach(function() { + context = { + width: 640, + height: 480 + }; + }); + + it("should convert a top-value correclty", function() { + var expected = 48, + result = CVUtils._dimensionsConverters.top({value: 10, unit: "%"}, context); + + expect(result).to.be.equal(expected); + }); + + it("should convert a right-value correclty", function() { + var expected = 640 - 128, + result = CVUtils._dimensionsConverters.right({value: 20, unit: "%"}, context); + + expect(result).to.be.equal(expected); + }); + + it("should convert a bottom-value correclty", function() { + var expected = 480 - 77, + result = CVUtils._dimensionsConverters.bottom({value: 16, unit: "%"}, context); + + expect(result).to.be.equal(expected); + }); + + it("should convert a left-value correclty", function() { + var expected = 57, + result = CVUtils._dimensionsConverters.left({value: 9, unit: "%"}, context); + + expect(result).to.be.equal(expected); + }); +}); + +describe("computeImageArea", function() { + it("should calculate an image-area", function() { + var expected = { + sx: 115, + sy: 48, + sw: 429, + sh: 336 + }, + result = CVUtils.computeImageArea(640, 480, { + top: "10%", + right: "15%", + bottom: "20%", + left: "18%" + }); + + expect(result).to.be.deep.equal(expected); + }); + + it("should calculate full image-area", function() { + var expected = { + sx: 0, + sy: 0, + sw: 640, + sh: 480 + }, + result = CVUtils.computeImageArea(640, 480, { + top: "0%", + right: "0%", + bottom: "0%", + left: "0%" + }); + + expect(result).to.be.deep.equal(expected); + }); +}); diff --git a/test/spec/events.spec.js b/test/spec/events.spec.js new file mode 100644 index 00000000..e9bbce93 --- /dev/null +++ b/test/spec/events.spec.js @@ -0,0 +1,114 @@ +const Events = require('../../src/events'); + +beforeEach(function() { + Events.unsubscribe(); +}); +describe("subscribe", function() { + + + it("should call one callback for a single event", function() { + var callbackA = sinon.stub(), + callbackB = sinon.stub(); + + Events.subscribe("test", callbackA); + Events.subscribe("test", callbackB); + Events.publish("test"); + + expect(callbackA.calledOnce).to.be.equal(true); + expect(callbackB.calledOnce).to.be.equal(true); + }); + + it("should call one callback twice if published twice", function() { + var callback = sinon.stub(); + + Events.subscribe("test", callback); + + Events.publish("test"); + Events.publish("test"); + + expect(callback.calledTwice).to.be.equal(true); + }); + + it("should call the callback asynchronuously", function(done) { + var test = { + callback: function() { + + } + }; + + sinon.stub(test, "callback", function() { + expect(test.callback.calledOnce).to.be.true; + done(); + }); + Events.subscribe("test", test.callback, true); + Events.publish("test"); + expect(test.callback.called).to.be.false; + }); +}); + +describe("once", function() { + it("should call the callback once, even when published twice", function() { + var callbackA = sinon.stub(), + callbackB = sinon.stub(); + + Events.once("test", callbackA); + Events.subscribe("test", callbackB); + + Events.publish("test"); + Events.publish("test"); + + expect(callbackA.calledOnce).to.be.equal(true); + expect(callbackB.calledTwice).to.be.equal(true); + }); +}); + +describe("unsubscribe", function() { + it("should unsubscribe all callbacks from a single event", function() { + var callbackA = sinon.stub(), + callbackB = sinon.stub(), + callbackC = sinon.stub(); + + Events.subscribe("test", callbackA); + Events.subscribe("test", callbackB); + Events.subscribe("testC", callbackC); + + Events.publish("test"); + + expect(callbackC.called).to.be.equal(false); + expect(callbackA.calledOnce).to.be.equal(true); + expect(callbackB.calledOnce).to.be.equal(true); + + Events.publish("testC"); + + expect(callbackC.calledOnce).to.be.equal(true); + expect(callbackA.calledOnce).to.be.equal(true); + expect(callbackB.calledOnce).to.be.equal(true); + + Events.unsubscribe("test"); + Events.publish("test"); + + expect(callbackC.calledOnce).to.be.equal(true); + expect(callbackA.calledOnce).to.be.equal(true); + expect(callbackB.calledOnce).to.be.equal(true); + }); + + it("should unsubscribe a single callback from a single event", function() { + var callbackA = sinon.stub(), + callbackB = sinon.stub(); + + Events.subscribe("test", callbackA); + Events.subscribe("test", callbackB); + + Events.publish("test"); + + expect(callbackA.calledOnce).to.be.equal(true); + expect(callbackB.calledOnce).to.be.equal(true); + + + Events.unsubscribe("test", callbackB); + Events.publish("test"); + + expect(callbackA.calledTwice).to.be.equal(true); + expect(callbackB.calledOnce).to.be.equal(true); + }); +}); diff --git a/test/spec/result_collector.spec.js b/test/spec/result_collector.spec.js new file mode 100644 index 00000000..0c898297 --- /dev/null +++ b/test/spec/result_collector.spec.js @@ -0,0 +1,103 @@ +const ResultCollector = require('../../src/result_collector'); +const ImageDebug = require('../../src/image_debug'); + +var canvasMock, + imageSize, + config; + +beforeEach(function() { + imageSize = {x: 320, y: 240}; + config = { + capture: true, + capacity: 20, + blacklist: [{code: "3574660239843", format: "ean_13"}], + filter: function() { + return true; + } + }; + canvasMock = { + getContext: function() { + return {}; + }, + toDataURL: sinon.spy(), + width: 0, + height: 0 + }; + sinon.stub(document, "createElement", function(type) { + if (type === "canvas") { + return canvasMock; + } + }); +}); + +afterEach(function() { + document.createElement.restore(); +}); + + +describe('create', function () { + it("should return a new collector", function() { + ResultCollector.create(config); + expect(document.createElement.calledOnce).to.be.equal(true); + expect(document.createElement.getCall(0).args[0]).to.equal("canvas"); + }); +}); + +describe('addResult', function() { + beforeEach(function() { + sinon.stub(ImageDebug, "drawImage", function() {}); + }); + + afterEach(function() { + ImageDebug.drawImage.restore(); + }); + + it("should not add result if capacity is full", function(){ + config.capacity = 1; + var collector = ResultCollector.create(config); + collector.addResult([], imageSize, {}); + collector.addResult([], imageSize, {}); + collector.addResult([], imageSize, {}); + expect(collector.getResults()).to.have.length(1); + }); + + it("should only add results which match constraints", function(){ + var collector = ResultCollector.create(config), + results; + + collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); + collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"}); + collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"}); + + results = collector.getResults(); + expect(results).to.have.length(2); + + results.forEach(function(result) { + expect(result).not.to.deep.equal(config.blacklist[0]); + }); + }); + + it("should add result if no filter is set", function() { + delete config.filter; + var collector = ResultCollector.create(config); + + collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); + expect(collector.getResults()).to.have.length(1); + }); + + it("should not add results if filter returns false", function() { + config.filter = () => (false); + var collector = ResultCollector.create(config); + + collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); + expect(collector.getResults()).to.have.length(0); + }); + + it("should add result if no blacklist is set", function() { + delete config.blacklist; + var collector = ResultCollector.create(config); + + collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"}); + expect(collector.getResults()).to.have.length(1); + }); +}); diff --git a/test/test-main-integration.js b/test/test-main-integration.js new file mode 100644 index 00000000..9a7cc0d5 --- /dev/null +++ b/test/test-main-integration.js @@ -0,0 +1,8 @@ +require('events').EventEmitter.prototype._maxListeners = 0; +require('core-js/es5'); + +const testsContext = require.context("./integration", true, /.*js$/); +testsContext.keys().forEach(testsContext); + +const componentsContext = require.context('../src/', true, /\.*js$/); +componentsContext.keys().forEach(componentsContext); diff --git a/test/test-main.js b/test/test-main.js new file mode 100644 index 00000000..56eb6ff4 --- /dev/null +++ b/test/test-main.js @@ -0,0 +1,8 @@ +require('events').EventEmitter.prototype._maxListeners = 0; +require('core-js/es5'); + +const testsContext = require.context("./spec", true, /.*js$/); +testsContext.keys().forEach(testsContext); + +const componentsContext = require.context('../src/', true, /\.*js$/); +componentsContext.keys().forEach(componentsContext); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..4aa41fcd --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,39 @@ +var webpack = require('webpack'), + MyUmdPlugin = require('./plugins/umd'), + path = require('path'); + +module.exports = { + entry: [ + './src/quagga.js' + ], + devtool: 'source-map', + module: { + loaders: [{ + test: /\.jsx?$/, + exclude: /node_modules/, + loader: 'babel' + }] + }, + resolve: { + extensions: ['', '.js', '.jsx'], + alias: { + 'input_stream$': path.resolve(__dirname, 'src/input_stream'), + 'frame_grabber$': path.resolve(__dirname, 'src/frame_grabber') + } + }, + output: { + path: __dirname + '/dist', + publicPath: '/', + filename: 'quagga.js', + sourceMapFilename: 'quagga.map' + }, + devServer: { + contentBase: './', + hot: true + }, + plugins: [ + new MyUmdPlugin({ + library: 'Quagga' + }) + ] +}; diff --git a/webpack.config.min.js b/webpack.config.min.js new file mode 100644 index 00000000..8c9fc373 --- /dev/null +++ b/webpack.config.min.js @@ -0,0 +1,11 @@ +var webpack = require('webpack'); +module.exports = require('./webpack.config.js'); + +module.exports.plugins.unshift( + new webpack.optimize.UglifyJsPlugin() +); + +module.exports.output.filename = 'quagga.min.js'; + +module.exports.output.sourceMapFilename = null; +module.exports.devtool = null; diff --git a/webpack.node.config.js b/webpack.node.config.js new file mode 100644 index 00000000..15f569e4 --- /dev/null +++ b/webpack.node.config.js @@ -0,0 +1,24 @@ +var webpack = require('webpack'), + path = require('path'); + +module.exports = require('./webpack.config.js'); + +module.exports.resolve = { + extensions: ['', '.js', '.jsx'], + alias: { + 'input_stream$': path.resolve(__dirname, 'lib/input_stream'), + 'frame_grabber$': path.resolve(__dirname, 'lib/frame_grabber') + } +}; + +module.exports.externals = [ + "get-pixels", + "gl-matrix", + "lodash", + "ndarray", + "ndarray-linear-interpolate" +]; +module.exports.output.libraryTarget = "commonjs2"; +module.exports.plugins = []; +module.exports.output.path = __dirname + '/lib'; +module.exports.output.filename = 'quagga.js';