* Split core lib into multiple files * Refactor data encoding methods * Refactor data masking process * Improve qr code generation process * Increase minimum required node version to 0.10 * Add linter * Add tests and tests coverage * Update travis config to fix compilation issues * Add examples folder * Add missing license tag in package.json * Update build script and add sourcemap support * Publish only strictly needed files on npm * Update readme
221 lines
5.4 KiB
JavaScript
221 lines
5.4 KiB
JavaScript
/**
|
|
* 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 PenalityScores = {
|
|
N1: 3,
|
|
N2: 3,
|
|
N3: 40,
|
|
N4: 10
|
|
}
|
|
|
|
/**
|
|
* Find adjacent modules in row/column with the same color
|
|
* and assign a penality value.
|
|
*
|
|
* Points: N1 + i
|
|
* i is the amount by which the number of adjacent modules of the same color exceeds 5
|
|
*/
|
|
function getPenalityN1 (data) {
|
|
var size = data.size
|
|
var points = 0
|
|
|
|
for (var row = 0; row < size; row++) {
|
|
for (var col = 0; col < size; col++) {
|
|
// number of consecutive modules with same color
|
|
var sameCount = 0
|
|
var dark = data.get(row, col)
|
|
|
|
for (var r = -1; r <= 1; r++) {
|
|
if (row + r < 0 || size <= row + r) continue
|
|
|
|
for (var c = -1; c <= 1; c++) {
|
|
if (col + c < 0 || size <= col + c) continue
|
|
if (r === 0 && c === 0) continue
|
|
if (dark === data.get(row + r, col + c)) sameCount++
|
|
}
|
|
}
|
|
|
|
if (sameCount > 5) {
|
|
points += PenalityScores.N1 + sameCount - 5
|
|
}
|
|
}
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
/**
|
|
* Find 2x2 blocks with the same color and assign a penality value
|
|
*
|
|
* Points: N2 * (m - 1) * (n - 1)
|
|
*/
|
|
function getPenalityN2 (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 count = 0
|
|
if (data.get(row, col)) count++
|
|
if (data.get(row + 1, col)) count++
|
|
if (data.get(row, col + 1)) count++
|
|
if (data.get(row + 1, col + 1)) count++
|
|
if (count === 0 || count === 4) points += PenalityScores.N2
|
|
}
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
function getPenalityN3 (data) {
|
|
var size = data.size
|
|
var points = 0
|
|
var row, col
|
|
|
|
for (row = 0; row < size; row++) {
|
|
for (col = 0; col < size - 6; col++) {
|
|
if (data.get(row, col) &&
|
|
!data.get(row, col + 1) &&
|
|
data.get(row, col + 2) &&
|
|
data.get(row, col + 3) &&
|
|
data.get(row, col + 4) &&
|
|
!data.get(row, col + 5) &&
|
|
data.get(row, col + 6)) {
|
|
points += PenalityScores.N3
|
|
}
|
|
}
|
|
}
|
|
|
|
for (col = 0; col < size; col++) {
|
|
for (row = 0; row < size - 6; row++) {
|
|
if (data.get(row, col) &&
|
|
!data.get(row + 1, col) &&
|
|
data.get(row + 2, col) &&
|
|
data.get(row + 3, col) &&
|
|
data.get(row + 4, col) &&
|
|
!data.get(row + 5, col) &&
|
|
data.get(row + 6, col)) {
|
|
points += PenalityScores.N3
|
|
}
|
|
}
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
/**
|
|
* 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%
|
|
*/
|
|
function getPenalityN4 (data) {
|
|
var darkCount = 0
|
|
var size = data.size
|
|
|
|
for (var col = 0; col < size; col++) {
|
|
for (var row = 0; row < size; row++) {
|
|
if (data.get(row, col)) darkCount++
|
|
}
|
|
}
|
|
|
|
var ratio = Math.abs(100 * darkCount / size / size - 50) / 5
|
|
return ratio * PenalityScores.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) {
|
|
var numPatterns = Object.keys(exports.Patterns).length
|
|
var bestPattern = 0
|
|
var lowerPenality = Infinity
|
|
|
|
for (var p = 0; p < numPatterns; p++) {
|
|
exports.applyMask(p, data)
|
|
|
|
// Calculate penality
|
|
var penality =
|
|
getPenalityN1(data) +
|
|
getPenalityN2(data) +
|
|
getPenalityN3(data) +
|
|
getPenalityN4(data)
|
|
|
|
// Undo previously applied mask
|
|
exports.applyMask(p, data)
|
|
|
|
if (penality < lowerPenality) {
|
|
lowerPenality = penality
|
|
bestPattern = p
|
|
}
|
|
}
|
|
|
|
return bestPattern
|
|
}
|