/// Division with fast fraction truncating function intDiv(a, b) { return ~~(a / b) } let getCodeVersion const alignmentCoords = [ [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170], ] addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById("canvas") const ctx = canvas.getContext("2d") ctx.fillStyle = "black" const options = { mask: document.getElementById("mask-expr"), size: document.getElementById("width"), ver: document.getElementById("version"), scale: document.getElementById("scale"), gap: document.getElementById("gap"), } document.getElementById("gen-btn").addEventListener("click", () => { const mask = options.mask.value const size = Number(options.size.value) const scale = Number(options.scale.value) const gap = Math.min(Number(options.gap.value), scale) // not more than 1 module scaled size const wh = scale - gap * 2 // width&height for 1 module with grid gap canvas.width = canvas.height = size * scale ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.fillStyle = "black" for (let row = 0; row < size; row++) { for (let col = 0; col < size; col++) { if (eval(mask) == 0) { ctx.fillRect(col * scale + gap, row * scale + gap, wh, wh) } } } const idSize = 8 * scale { // finder pattern ctx.fillStyle = "rgba(224, 63, 102, 0.5)" ctx.fillRect(0, 0, idSize, idSize) ctx.fillRect(canvas.width - idSize, 0, idSize, idSize) ctx.fillRect(0, canvas.height - idSize, idSize, idSize) } const ver = getCodeVersion() if (ver > 1) { // alignment pattern ctx.fillStyle = "rgba(209, 63, 224, 0.5)" const alignSize = 5 * scale const coords = alignmentCoords[ver - 2] // array begins with version 2 const len = coords.length for (let i = 0; i < len; i++) { for (let j = 0; j < len; j++) { const row = coords[i], col = coords[j] if (row < 8 && col < 8 || row < 8 && col > size - 8 || row > size - 8 && col < 8) { // do not overlap with finder pattern continue } ctx.fillRect((row - 2) * scale, (col - 2) * scale, alignSize, alignSize) } } } { // timing pattern ctx.fillStyle = "rgba(76, 63, 224, 0.5)" const idEndPos = 6 * scale const betweenIds = canvas.width - idSize * 2 ctx.fillRect(idSize, idEndPos, betweenIds, scale) ctx.fillRect(idEndPos, idSize, scale, betweenIds) } { // format info // TODO } }) { // Choose best scale for screen height const height = document.documentElement.clientHeight; const padding = getComputedStyle(document.body).paddingTop; // TODO } { // QR version related stuff const MICRO_4 = 17 // size for version M4, before version 1 const DIFF = 4 // difference between versions const MIN_VER = 1 const MAX_VER = 40 getCodeVersion = () => { const size = options.size.value return Math.floor((size - MICRO_4) / DIFF) } const updateSizeVersion = (versionStep) => { const ver = getCodeVersion() + versionStep if (ver > MAX_VER || ver < MIN_VER) { return } options.size.value = ver * DIFF + MICRO_4 options.ver.innerText = ver } document.getElementById("width-incr").addEventListener("click", () => { updateSizeVersion(+1) }) document.getElementById("width-decr").addEventListener("click", () => { updateSizeVersion(-1) }) options.size.addEventListener("input", () => { options.ver.innerText = getCodeVersion() }) } })