Spaces:
Running
Running
File size: 6,101 Bytes
9de7b70 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | /**
* 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}
*/
const 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) {
const size = data.size
let points = 0
let sameCountCol = 0
let sameCountRow = 0
let lastCol = null
let lastRow = null
for (let row = 0; row < size; row++) {
sameCountCol = sameCountRow = 0
lastCol = lastRow = null
for (let col = 0; col < size; col++) {
let 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) {
const size = data.size
let points = 0
for (let row = 0; row < size - 1; row++) {
for (let col = 0; col < size - 1; col++) {
const 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) {
const size = data.size
let points = 0
let bitsCol = 0
let bitsRow = 0
for (let row = 0; row < size; row++) {
bitsCol = bitsRow = 0
for (let 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) {
let darkCount = 0
const modulesCount = data.data.length
for (let i = 0; i < modulesCount; i++) darkCount += data.data[i]
const 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) {
const size = data.size
for (let col = 0; col < size; col++) {
for (let 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) {
const numPatterns = Object.keys(exports.Patterns).length
let bestPattern = 0
let lowerPenalty = Infinity
for (let p = 0; p < numPatterns; p++) {
setupFormatFunc(p)
exports.applyMask(p, data)
// Calculate penalty
const 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
}
|