123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- /**
- * Data mask pattern reference
- * @type {Object}
- */
- exports.Patterns = {
- PATTERN000: 0,
- PATTERN001: 1,
- PATTERN010: 2,
- PATTERN011: 3,
- PATTERN100: 4,
- PATTERN101: 5,
- PATTERN110: 6,
- PATTERN111: 7
- }
- /**
- * Weighted penalty scores for the undesirable features
- * @type {Object}
- */
- var PenaltyScores = {
- N1: 3,
- N2: 3,
- N3: 40,
- N4: 10
- }
- /**
- * Check if mask pattern value is valid
- *
- * @param {Number} mask Mask pattern
- * @return {Boolean} true if valid, false otherwise
- */
- exports.isValid = function isValid (mask) {
- return mask != null && mask !== '' && !isNaN(mask) && mask >= 0 && mask <= 7
- }
- /**
- * Returns mask pattern from a value.
- * If value is not valid, returns undefined
- *
- * @param {Number|String} value Mask pattern value
- * @return {Number} Valid mask pattern or undefined
- */
- exports.from = function from (value) {
- return exports.isValid(value) ? parseInt(value, 10) : undefined
- }
- /**
- * Find adjacent modules in row/column with the same color
- * and assign a penalty value.
- *
- * Points: N1 + i
- * i is the amount by which the number of adjacent modules of the same color exceeds 5
- */
- exports.getPenaltyN1 = function getPenaltyN1 (data) {
- var size = data.size
- var points = 0
- var sameCountCol = 0
- var sameCountRow = 0
- var lastCol = null
- var lastRow = null
- for (var row = 0; row < size; row++) {
- sameCountCol = sameCountRow = 0
- lastCol = lastRow = null
- for (var col = 0; col < size; col++) {
- var module = data.get(row, col)
- if (module === lastCol) {
- sameCountCol++
- } else {
- if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5)
- lastCol = module
- sameCountCol = 1
- }
- module = data.get(col, row)
- if (module === lastRow) {
- sameCountRow++
- } else {
- if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5)
- lastRow = module
- sameCountRow = 1
- }
- }
- if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5)
- if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5)
- }
- return points
- }
- /**
- * Find 2x2 blocks with the same color and assign a penalty value
- *
- * Points: N2 * (m - 1) * (n - 1)
- */
- exports.getPenaltyN2 = function getPenaltyN2 (data) {
- var size = data.size
- var points = 0
- for (var row = 0; row < size - 1; row++) {
- for (var col = 0; col < size - 1; col++) {
- var last = data.get(row, col) +
- data.get(row, col + 1) +
- data.get(row + 1, col) +
- data.get(row + 1, col + 1)
- if (last === 4 || last === 0) points++
- }
- }
- return points * PenaltyScores.N2
- }
- /**
- * Find 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column,
- * preceded or followed by light area 4 modules wide
- *
- * Points: N3 * number of pattern found
- */
- exports.getPenaltyN3 = function getPenaltyN3 (data) {
- var size = data.size
- var points = 0
- var bitsCol = 0
- var bitsRow = 0
- for (var row = 0; row < size; row++) {
- bitsCol = bitsRow = 0
- for (var col = 0; col < size; col++) {
- bitsCol = ((bitsCol << 1) & 0x7FF) | data.get(row, col)
- if (col >= 10 && (bitsCol === 0x5D0 || bitsCol === 0x05D)) points++
- bitsRow = ((bitsRow << 1) & 0x7FF) | data.get(col, row)
- if (col >= 10 && (bitsRow === 0x5D0 || bitsRow === 0x05D)) points++
- }
- }
- return points * PenaltyScores.N3
- }
- /**
- * Calculate proportion of dark modules in entire symbol
- *
- * Points: N4 * k
- *
- * k is the rating of the deviation of the proportion of dark modules
- * in the symbol from 50% in steps of 5%
- */
- exports.getPenaltyN4 = function getPenaltyN4 (data) {
- var darkCount = 0
- var modulesCount = data.data.length
- for (var i = 0; i < modulesCount; i++) darkCount += data.data[i]
- var k = Math.abs(Math.ceil((darkCount * 100 / modulesCount) / 5) - 10)
- return k * PenaltyScores.N4
- }
- /**
- * Return mask value at given position
- *
- * @param {Number} maskPattern Pattern reference value
- * @param {Number} i Row
- * @param {Number} j Column
- * @return {Boolean} Mask value
- */
- function getMaskAt (maskPattern, i, j) {
- switch (maskPattern) {
- case exports.Patterns.PATTERN000: return (i + j) % 2 === 0
- case exports.Patterns.PATTERN001: return i % 2 === 0
- case exports.Patterns.PATTERN010: return j % 3 === 0
- case exports.Patterns.PATTERN011: return (i + j) % 3 === 0
- case exports.Patterns.PATTERN100: return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0
- case exports.Patterns.PATTERN101: return (i * j) % 2 + (i * j) % 3 === 0
- case exports.Patterns.PATTERN110: return ((i * j) % 2 + (i * j) % 3) % 2 === 0
- case exports.Patterns.PATTERN111: return ((i * j) % 3 + (i + j) % 2) % 2 === 0
- default: throw new Error('bad maskPattern:' + maskPattern)
- }
- }
- /**
- * Apply a mask pattern to a BitMatrix
- *
- * @param {Number} pattern Pattern reference number
- * @param {BitMatrix} data BitMatrix data
- */
- exports.applyMask = function applyMask (pattern, data) {
- var size = data.size
- for (var col = 0; col < size; col++) {
- for (var row = 0; row < size; row++) {
- if (data.isReserved(row, col)) continue
- data.xor(row, col, getMaskAt(pattern, row, col))
- }
- }
- }
- /**
- * Returns the best mask pattern for data
- *
- * @param {BitMatrix} data
- * @return {Number} Mask pattern reference number
- */
- exports.getBestMask = function getBestMask (data, setupFormatFunc) {
- var numPatterns = Object.keys(exports.Patterns).length
- var bestPattern = 0
- var lowerPenalty = Infinity
- for (var p = 0; p < numPatterns; p++) {
- setupFormatFunc(p)
- exports.applyMask(p, data)
- // Calculate penalty
- var penalty =
- exports.getPenaltyN1(data) +
- exports.getPenaltyN2(data) +
- exports.getPenaltyN3(data) +
- exports.getPenaltyN4(data)
- // Undo previously applied mask
- exports.applyMask(p, data)
- if (penalty < lowerPenalty) {
- lowerPenalty = penalty
- bestPattern = p
- }
- }
- return bestPattern
- }
|