qr-core 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +3 -3
  2. package/dist/{src/api → api}/index.js +3 -3
  3. package/dist/{src/mapping → mapping}/zigzag.js +3 -2
  4. package/dist/{src/mask → mask}/selection.js +3 -1
  5. package/dist/{src/matrix → matrix}/bit-matrix.js +1 -3
  6. package/package.json +9 -6
  7. package/dist/bench/bench.d.ts +0 -1
  8. package/dist/bench/bench.js +0 -38
  9. package/dist/tests/golden/golden.test.d.ts +0 -1
  10. package/dist/tests/golden/golden.test.js +0 -26
  11. package/dist/tests/unit/api.test.d.ts +0 -1
  12. package/dist/tests/unit/api.test.js +0 -35
  13. package/dist/tests/unit/bit-matrix.test.d.ts +0 -1
  14. package/dist/tests/unit/bit-matrix.test.js +0 -46
  15. package/dist/tests/unit/core.test.d.ts +0 -1
  16. package/dist/tests/unit/core.test.js +0 -88
  17. package/dist/tests/unit/encoding.test.d.ts +0 -1
  18. package/dist/tests/unit/encoding.test.js +0 -63
  19. package/dist/tests/unit/layout.test.d.ts +0 -1
  20. package/dist/tests/unit/layout.test.js +0 -62
  21. package/dist/tests/unit/masking.test.d.ts +0 -1
  22. package/dist/tests/unit/masking.test.js +0 -31
  23. /package/dist/{src/api → api}/index.d.ts +0 -0
  24. /package/dist/{src/core → core}/bit-buffer.d.ts +0 -0
  25. /package/dist/{src/core → core}/bit-buffer.js +0 -0
  26. /package/dist/{src/core → core}/error.d.ts +0 -0
  27. /package/dist/{src/core → core}/error.js +0 -0
  28. /package/dist/{src/core → core}/gf.d.ts +0 -0
  29. /package/dist/{src/core → core}/gf.js +0 -0
  30. /package/dist/{src/core → core}/rs.d.ts +0 -0
  31. /package/dist/{src/core → core}/rs.js +0 -0
  32. /package/dist/{src/encoding → encoding}/bitstream.d.ts +0 -0
  33. /package/dist/{src/encoding → encoding}/bitstream.js +0 -0
  34. /package/dist/{src/encoding → encoding}/encoders.d.ts +0 -0
  35. /package/dist/{src/encoding → encoding}/encoders.js +0 -0
  36. /package/dist/{src/encoding → encoding}/interleave.d.ts +0 -0
  37. /package/dist/{src/encoding → encoding}/interleave.js +0 -0
  38. /package/dist/{src/encoding → encoding}/segmentation.d.ts +0 -0
  39. /package/dist/{src/encoding → encoding}/segmentation.js +0 -0
  40. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  41. /package/dist/{src/index.js → index.js} +0 -0
  42. /package/dist/{src/layout → layout}/patterns.d.ts +0 -0
  43. /package/dist/{src/layout → layout}/patterns.js +0 -0
  44. /package/dist/{src/mapping → mapping}/zigzag.d.ts +0 -0
  45. /package/dist/{src/mask → mask}/patterns.d.ts +0 -0
  46. /package/dist/{src/mask → mask}/patterns.js +0 -0
  47. /package/dist/{src/mask → mask}/penalty.d.ts +0 -0
  48. /package/dist/{src/mask → mask}/penalty.js +0 -0
  49. /package/dist/{src/mask → mask}/selection.d.ts +0 -0
  50. /package/dist/{src/matrix → matrix}/bit-matrix.d.ts +0 -0
  51. /package/dist/{src/spec → spec}/bch.d.ts +0 -0
  52. /package/dist/{src/spec → spec}/bch.js +0 -0
  53. /package/dist/{src/spec → spec}/constants.d.ts +0 -0
  54. /package/dist/{src/spec → spec}/constants.js +0 -0
  55. /package/dist/{src/spec → spec}/tables.d.ts +0 -0
  56. /package/dist/{src/spec → spec}/tables.js +0 -0
  57. /package/dist/{src/strategies → strategies}/index.d.ts +0 -0
  58. /package/dist/{src/strategies → strategies}/index.js +0 -0
  59. /package/dist/{src/strategies → strategies}/mask.d.ts +0 -0
  60. /package/dist/{src/strategies → strategies}/mask.js +0 -0
  61. /package/dist/{src/strategies → strategies}/segmentation.d.ts +0 -0
  62. /package/dist/{src/strategies → strategies}/segmentation.js +0 -0
  63. /package/dist/{src/strategies → strategies}/version.d.ts +0 -0
  64. /package/dist/{src/strategies → strategies}/version.js +0 -0
  65. /package/dist/{src/types → types}/index.d.ts +0 -0
  66. /package/dist/{src/types → types}/index.js +0 -0
package/README.md CHANGED
@@ -110,9 +110,9 @@ Run `npm run bench` to reproduce.
110
110
 
111
111
  | Scenario | Description | p50 (ms) | p95 (ms) |
112
112
  | :--- | :--- | :---: | :---: |
113
- | **Small** | Auto version/mask, ecc M ("HELLO WORLD") | **0.066** | **0.138** |
114
- | **Medium** | Auto version/mask, ecc M (~500 bytes URL‑ish payload) | **1.262** | **1.403** |
115
- | **Large** | Auto version/mask, ecc L (3000 bytes) | **3.849** | **3.984** |
113
+ | **Small** | Auto version/mask, ecc M ("HELLO WORLD") | **0.074** | **0.138** |
114
+ | **Medium** | Auto version/mask, ecc M (~500 bytes URL‑ish payload) | **1.275** | **1.527** |
115
+ | **Large** | Auto version/mask, ecc L (3000 bytes) | **4.082** | **4.536** |
116
116
 
117
117
  > Benchmarks captured on 2026‑01‑31 with Node v24.7.0 (darwin arm64). Goal from RFC: version‑40 near‑capacity with auto‑mask < 5ms — **met**.
118
118
 
@@ -153,9 +153,7 @@ function validateOptions(options, ecc, charset, quietZone) {
153
153
  throw new QrException("INVALID_OPTIONS", `Invalid charset: ${charset}`);
154
154
  }
155
155
  if (options.mode !== undefined && options.mode !== "auto") {
156
- if (options.mode !== "numeric" &&
157
- options.mode !== "alphanumeric" &&
158
- options.mode !== "byte") {
156
+ if (options.mode !== "numeric" && options.mode !== "alphanumeric" && options.mode !== "byte") {
159
157
  throw new QrException("INVALID_OPTIONS", `Invalid mode: ${options.mode}`);
160
158
  }
161
159
  }
@@ -196,6 +194,8 @@ function prepareSegments(segments, charset, strict) {
196
194
  const bytes = encodeByteString(segment.data, charset, strict);
197
195
  return { ...segment, bytes, count: bytes.length };
198
196
  }
197
+ default:
198
+ throw new QrException("INTERNAL_INVARIANT_BROKEN", `Unknown segment mode: ${segment.mode}`);
199
199
  }
200
200
  });
201
201
  }
@@ -5,10 +5,11 @@ export function zigzag(matrix, data) {
5
5
  const size = matrix.size;
6
6
  let bitIndex = 0;
7
7
  const totalBits = data.length * 8;
8
- for (let right = size - 1; right > 0; right -= 2) {
8
+ const last = size - 1;
9
+ for (let right = last, pairIndex = 0; right > 0; right -= 2, pairIndex++) {
9
10
  if (right === 6)
10
11
  right--;
11
- const upward = ((right + 1) / 2) % 2 !== 0;
12
+ const upward = pairIndex % 2 === 0;
12
13
  for (let i = 0; i < size; i++) {
13
14
  const y = upward ? size - 1 - i : i;
14
15
  for (let j = 0; j < 2; j++) {
@@ -141,7 +141,7 @@ export function writeFormatInformation(matrix, ecc, maskId) {
141
141
  const formatBits = getFormatInfo(ecc, maskId);
142
142
  const size = matrix.size;
143
143
  for (let i = 0; i < 15; i++) {
144
- const bit = (formatBits >>> i) & 1;
144
+ const bit = (formatBits >>> (14 - i)) & 1;
145
145
  // Top-left
146
146
  let x;
147
147
  let y;
@@ -168,6 +168,8 @@ export function writeFormatInformation(matrix, ecc, maskId) {
168
168
  if (i < 8) {
169
169
  x2 = 8;
170
170
  y2 = size - 1 - i;
171
+ if (y2 === size - 8)
172
+ continue; // skip dark module
171
173
  }
172
174
  else {
173
175
  x2 = size - 15 + i;
@@ -88,9 +88,7 @@ export class Matrix {
88
88
  toRows() {
89
89
  const rows = [];
90
90
  for (let y = 0; y < this.size; y++) {
91
- rows.push(this.data
92
- .slice(y * this.size, (y + 1) * this.size)
93
- .map((v) => (v & 1)));
91
+ rows.push(this.data.slice(y * this.size, (y + 1) * this.size).map((v) => (v & 1)));
94
92
  }
95
93
  return rows;
96
94
  }
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "qr-core",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Deterministic, zero-dependency QR Code Model 2 encoder (matrix-only)",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
6
+ "main": "./dist/src/index.js",
7
+ "types": "./dist/src/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./dist/index.d.ts",
11
- "default": "./dist/index.js"
10
+ "types": "./dist/src/index.d.ts",
11
+ "import": "./dist/src/index.js",
12
+ "default": "./dist/src/index.js"
12
13
  }
13
14
  },
14
15
  "scripts": {
15
- "build": "tsc",
16
+ "build": "tsc -p tsconfig.build.json",
16
17
  "prepublishOnly": "npm run build",
17
18
  "test": "vitest",
18
19
  "lint": "biome check src tests bench",
@@ -39,6 +40,8 @@
39
40
  "devDependencies": {
40
41
  "@biomejs/biome": "^2.3.13",
41
42
  "@types/node": "^25.1.0",
43
+ "jsqr": "^1.4.0",
44
+ "pngjs": "^7.0.0",
42
45
  "typescript": "^5.9.3",
43
46
  "vitest": "^3.2.4"
44
47
  }
@@ -1 +0,0 @@
1
- export {};
@@ -1,38 +0,0 @@
1
- import { encode } from "../src/index.js";
2
- function bench(name, fn, iterations = 100) {
3
- // Warmup
4
- for (let i = 0; i < 10; i++)
5
- fn();
6
- const times = [];
7
- for (let i = 0; i < iterations; i++) {
8
- const start = performance.now();
9
- fn();
10
- const end = performance.now();
11
- times.push(end - start);
12
- }
13
- times.sort((a, b) => a - b);
14
- const p50 = times[Math.floor(times.length * 0.5)] || 0;
15
- const p95 = times[Math.floor(times.length * 0.95)] || 0;
16
- const avg = times.reduce((a, b) => a + b, 0) / times.length;
17
- console.log(`${name}:`);
18
- console.log(` p50: ${p50.toFixed(3)}ms`);
19
- console.log(` p95: ${p95.toFixed(3)}ms`);
20
- console.log(` avg: ${avg.toFixed(3)}ms`);
21
- }
22
- async function runBenchmarks() {
23
- console.log("Running benchmarks...\n");
24
- const smallPayload = "HELLO WORLD";
25
- bench("Small Payload (auto version/mask, ecc M)", () => {
26
- encode(smallPayload, { ecc: "M" });
27
- });
28
- const mediumPayload = "https://example.com/search?q=" + "A".repeat(480);
29
- bench("Medium Payload (~500 bytes, auto version/mask, ecc M)", () => {
30
- encode(mediumPayload, { ecc: "M" });
31
- });
32
- const largePayload = "A".repeat(3000);
33
- // Version 40-L holds 3181 data codewords; 3000 bytes fits.
34
- bench("Large Payload (3000 bytes, auto version/mask, ecc L)", () => {
35
- encode(largePayload, { ecc: "L" });
36
- });
37
- }
38
- runBenchmarks();
@@ -1 +0,0 @@
1
- export {};
@@ -1,26 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { describe, expect, it } from "vitest";
4
- import { encode } from "../../src/api/index";
5
- const vectorsDir = path.join(__dirname, "../vectors");
6
- const vectorFiles = fs
7
- .readdirSync(vectorsDir)
8
- .filter((f) => f.endsWith(".json"));
9
- let vectors = [];
10
- for (const file of vectorFiles) {
11
- const content = JSON.parse(fs.readFileSync(path.join(vectorsDir, file), "utf-8"));
12
- vectors = vectors.concat(content);
13
- }
14
- describe("Golden Vectors", () => {
15
- for (const vec of vectors) {
16
- it(`should match vector: ${vec.name}`, () => {
17
- // Normalize inputs
18
- // biome-ignore lint/suspicious/noExplicitAny: generic casting for options
19
- const opts = { ...vec.options };
20
- const result = encode(vec.input.value, opts);
21
- expect(result.version).toBe(vec.expected.version);
22
- expect(result.size).toBe(vec.expected.size);
23
- // In a real golden test, we would compare the entire matrix hash/bits
24
- });
25
- }
26
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,35 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { encode, encodeSafe } from "../../src/api/index";
3
- describe("Public API", () => {
4
- it("should encode simple string", () => {
5
- const result = encode("Hello World");
6
- expect(result.version).toBeGreaterThan(0);
7
- expect(result.size).toBeGreaterThan(0);
8
- expect(result.matrix).toBeDefined();
9
- });
10
- it("should allow forcing version", () => {
11
- const result = encode("A", { version: 5 });
12
- expect(result.version).toBe(5);
13
- expect(result.size).toBe(21 + 4 * (5 - 1));
14
- });
15
- it("should throw if data too large for fixed version", () => {
16
- // Version 1 can only hold ~17 bytes (Ecc L)
17
- // "A" * 100 needs version ~4
18
- const largeData = "A".repeat(100);
19
- // Expect message to contain "Data requires version"
20
- expect(() => encode(largeData, { version: 1 })).toThrow(/Data requires version/);
21
- });
22
- it("should safe encode without throwing", () => {
23
- const largeData = "A".repeat(100);
24
- const result = encodeSafe(largeData, { version: 1 });
25
- expect(result.ok).toBe(false);
26
- if (!result.ok) {
27
- expect(result.error.code).toBe("DATA_TOO_LARGE");
28
- }
29
- });
30
- it("should support Uint8Array input", () => {
31
- const input = new Uint8Array([65, 66, 67]);
32
- const result = encode(input, { mode: "byte" });
33
- expect(result.version).toBe(1);
34
- });
35
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,46 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { Matrix } from "../../src/matrix/bit-matrix";
3
- describe("BitMatrix", () => {
4
- it("should initialize with correct size and default values", () => {
5
- const size = 21;
6
- const matrix = new Matrix(size);
7
- expect(matrix.size).toBe(size);
8
- expect(matrix.get(0, 0)).toBe(0);
9
- expect(matrix.get(size - 1, size - 1)).toBe(0);
10
- });
11
- it("should set and get values", () => {
12
- const matrix = new Matrix(21);
13
- matrix.set(5, 5, true);
14
- expect(matrix.get(5, 5)).toBe(1);
15
- matrix.set(5, 5, false);
16
- expect(matrix.get(5, 5)).toBe(0);
17
- });
18
- it("should handle reserved modules", () => {
19
- const matrix = new Matrix(21);
20
- matrix.setReserved(10, 10, true);
21
- expect(matrix.isReserved(10, 10)).toBe(true);
22
- expect(matrix.get(10, 10)).toBe(1);
23
- // Normal set should not overwrite reserved
24
- matrix.set(10, 10, false);
25
- expect(matrix.get(10, 10)).toBe(1);
26
- });
27
- it("should throw on out of bounds access", () => {
28
- const matrix = new Matrix(21);
29
- expect(() => matrix.get(-1, 0)).toThrow();
30
- expect(() => matrix.get(0, 21)).toThrow();
31
- expect(() => matrix.set(21, 0, true)).toThrow();
32
- expect(() => matrix.setReserved(0, -1, true)).toThrow();
33
- });
34
- it("should serialize to packed bytes correct MSB-first order", () => {
35
- // Size 9 -> 2 bytes per row
36
- // Row 0: 10000000 1.......
37
- const matrix = new Matrix(9);
38
- matrix.set(0, 0, true); // First bit
39
- matrix.set(8, 0, true); // 9th bit (first bit of second byte)
40
- const packed = matrix.toPacked();
41
- expect(packed.length).toBe(9 * 2);
42
- // Row 0
43
- expect(packed[0]).toBe(0x80); // 10000000
44
- expect(packed[1]).toBe(0x80); // 10000000 (padded)
45
- });
46
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,88 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { BitBuffer } from "../../src/core/bit-buffer";
3
- import { EXP, inv, LOG, mul } from "../../src/core/gf";
4
- import { computeEcc } from "../../src/core/rs";
5
- describe("GF(256)", () => {
6
- it("should have correct table sizes", () => {
7
- expect(EXP.length).toBe(512);
8
- expect(LOG.length).toBe(256);
9
- });
10
- it("EXP[0] should be 1", () => {
11
- expect(EXP[0]).toBe(1);
12
- expect(EXP[255]).toBe(1);
13
- });
14
- it("LOG[1] should be 0", () => {
15
- expect(LOG[1]).toBe(0);
16
- });
17
- it("multiplication should be correct", () => {
18
- expect(mul(0, 5)).toBe(0);
19
- expect(mul(5, 0)).toBe(0);
20
- expect(mul(1, 10)).toBe(10);
21
- // 2 * 2 = 4
22
- expect(mul(2, 2)).toBe(4);
23
- // Inverse check
24
- const a = 123;
25
- const invA = inv(a);
26
- expect(mul(a, invA)).toBe(1);
27
- });
28
- });
29
- describe("BitBuffer", () => {
30
- it("should append bits correctly", () => {
31
- const bb = new BitBuffer();
32
- bb.put(0b1, 1);
33
- bb.put(0b0, 1);
34
- bb.put(0b1, 1);
35
- // current: 101 (5)
36
- // length: 3
37
- expect(bb.length).toBe(3);
38
- const bytes = bb.toBytes();
39
- // 10100000 = 0xA0
40
- expect(bytes[0]).toBe(0xa0);
41
- });
42
- it("should handle multi-bit put", () => {
43
- const bb = new BitBuffer();
44
- // append 0xABC (101010111100) 12 bits
45
- bb.put(0xabc, 12);
46
- expect(bb.length).toBe(12);
47
- const bytes = bb.toBytes();
48
- // 10101011 1100xxxx
49
- // 0xAB 0xC0
50
- expect(bytes[0]).toBe(0xab);
51
- expect((bytes[1] ?? 0) & 0xf0).toBe(0xc0);
52
- });
53
- });
54
- describe("Reed-Solomon", () => {
55
- it("should return all zeros for zero data", () => {
56
- const data = new Uint8Array([0, 0, 0]);
57
- const ecc = computeEcc(data, 5);
58
- expect(ecc).toEqual(new Uint8Array(5));
59
- });
60
- // Simple known vector check could be added here if we had one.
61
- // Using a known property check:
62
- // If we encode M(x), then M(x)x^n + R(x) is a codeword.
63
- // This means it evaluates to 0 at the roots of G(x)?
64
- // Roots of G(x) are 2^0, 2^1 ... 2^{n-1}.
65
- // Let's check with eccLen=2. Roots 2^0=1, 2^1=2.
66
- // Codeword C(x) should satisfy C(1)=0 and C(2)=0.
67
- it("should produce valid codewords (eval to 0 at roots)", () => {
68
- const message = new Uint8Array([10, 20, 30]);
69
- const eccLen = 2; // Roots: 1, 2
70
- const ecc = computeEcc(message, eccLen);
71
- // Construct full codeword: message * x^n + ecc
72
- // Coefficients: [10, 20, 30, ecc[0], ecc[1]]
73
- const codeword = new Uint8Array([...message, ...ecc]);
74
- // Evaluate at root 1 (2^0) = 1
75
- // Eval(x) = c0*x^4 + c1*x^3 + ...
76
- function evalPoly(poly, x) {
77
- let res = 0;
78
- for (const coeff of poly) {
79
- res = mul(res, x) ^ coeff;
80
- }
81
- return res;
82
- }
83
- const root0 = EXP[0] ?? 0; // 1
84
- const root1 = EXP[1] ?? 0; // 2
85
- expect(evalPoly(codeword, root0)).toBe(0);
86
- expect(evalPoly(codeword, root1)).toBe(0);
87
- });
88
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,63 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { BitBuffer } from "../../src/core/bit-buffer";
3
- import { encodeAlphanumeric, encodeByte, encodeNumeric, } from "../../src/encoding/encoders";
4
- import { interleave } from "../../src/encoding/interleave";
5
- import { segmentize } from "../../src/encoding/segmentation";
6
- describe("Encoders", () => {
7
- it("should encode numeric data", () => {
8
- const buffer = new BitBuffer();
9
- // "01234567" -> "012" (10 bits) + "345" (10 bits) + "67" (7 bits)
10
- encodeNumeric("01234567", buffer);
11
- // 012 = 0000001100
12
- // 345 = 0101011001
13
- // 67 = 1000011
14
- expect(buffer.length).toBe(27);
15
- });
16
- it("should encode alphanumeric data", () => {
17
- const buffer = new BitBuffer();
18
- // "AC-42" -> "AC" (11) + "-4" (11) + "2" (6)
19
- encodeAlphanumeric("AC-42", buffer);
20
- expect(buffer.length).toBe(11 + 11 + 6);
21
- });
22
- it("should encode byte data", () => {
23
- const buffer = new BitBuffer();
24
- encodeByte("Hello", buffer);
25
- expect(buffer.length).toBe(5 * 8);
26
- });
27
- });
28
- describe("Segmentation", () => {
29
- it("should segment simple numeric", () => {
30
- const segments = segmentize("0123456789");
31
- expect(segments).toHaveLength(1);
32
- expect(segments[0]?.mode).toBe("numeric");
33
- });
34
- it("should segment simple alphanumeric", () => {
35
- const segments = segmentize("HELLO WORLD");
36
- expect(segments).toHaveLength(1);
37
- expect(segments[0]?.mode).toBe("alphanumeric");
38
- });
39
- it("should segment mixed content", () => {
40
- // "123ABC123" -> numeric, alphanumeric, numeric
41
- // Our greedy segmenter might merge if it's suboptimal, but let's see current behavior.
42
- // "123" (num) "ABC" (alnum) "123" (num)
43
- const segments = segmentize("123ABC123");
44
- // Actually, greedy might switch eagerly.
45
- // "123" -> numeric
46
- // "A" -> switch to Alnum
47
- // "123" -> switch to Numeric
48
- expect(segments.map((s) => s.mode)).toEqual([
49
- "numeric",
50
- "alphanumeric",
51
- "numeric",
52
- ]);
53
- });
54
- });
55
- describe("Interleave", () => {
56
- it("should interleave data and ecc correctly", () => {
57
- // Mock data that fits version 1-L (19 data bytes)
58
- const data = new Uint8Array(19).fill(0x11);
59
- // Version 1-L: 19 data bytes, 7 ECC bytes. Total 26.
60
- const result = interleave(data, 1, "L");
61
- expect(result.length).toBe(26);
62
- });
63
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,62 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { setupFunctionPatterns } from "../../src/layout/patterns";
3
- import { Matrix } from "../../src/matrix/bit-matrix";
4
- describe("Layout Patterns", () => {
5
- it("should set up finder patterns", () => {
6
- const matrix = new Matrix(21);
7
- setupFunctionPatterns(matrix, 1);
8
- // Top Left Finder
9
- expect(matrix.isReserved(3, 3)).toBe(true); // Center of 3x3
10
- // Finder is 7x7. (0,0) to (6,6). Center (3,3).
11
- // (3,3) is dark.
12
- expect(matrix.get(3, 3)).toBe(1);
13
- // (0,0) is dark (outer ring)
14
- expect(matrix.get(0, 0)).toBe(1);
15
- // (1,1) is light (gap between outer and inner) - wait no
16
- // 7x7 finder:
17
- // xxxxxxx
18
- // x.....x
19
- // x.xxx.x
20
- // x.xxx.x
21
- // x.xxx.x
22
- // x.....x
23
- // xxxxxxx
24
- // (0,0) dark
25
- // (1,1) light?
26
- // Logic: max(abs(x-3), abs(y-3))
27
- // (1,1): abs(-2)=2. max(2,2)=2.
28
- // isDark = (max === 3) || (max <= 1).
29
- // 2 is not 3, nor <= 1. So light. Correct.
30
- expect(matrix.get(1, 1)).toBe(0);
31
- });
32
- it("should set up timing patterns", () => {
33
- const matrix = new Matrix(21);
34
- setupFunctionPatterns(matrix, 1);
35
- // Row 6, Cols 8..Size-8 (8..13 for V1)
36
- // (8, 6) -> i=8. even -> dark (true)
37
- // (9, 6) -> light
38
- expect(matrix.isReserved(8, 6)).toBe(true);
39
- expect(matrix.get(8, 6)).toBe(1);
40
- expect(matrix.get(9, 6)).toBe(0);
41
- });
42
- it("should set up alignment patterns for V2", () => {
43
- const matrix = new Matrix(25); // V2 size
44
- setupFunctionPatterns(matrix, 2);
45
- // V2 alignment at 6, 18
46
- // (6,6) is finder.
47
- // (18,18) should be alignment center.
48
- // Alignment is 5x5 center (18,18).
49
- expect(matrix.isReserved(18, 18)).toBe(true);
50
- // Center is dark
51
- expect(matrix.get(18, 18)).toBe(1);
52
- });
53
- it("should set up version info for V7", () => {
54
- const matrix = new Matrix(45); // V7 size
55
- setupFunctionPatterns(matrix, 7);
56
- // Version info is 3x6 block above bottom-left finder and left of top-right finder.
57
- // Bottom-left finder is at (0, Size-7).
58
- // Version info above it: Row Size-11..Size-9. Col 0..5
59
- // Let's check reservation.
60
- expect(matrix.isReserved(0, 45 - 11)).toBe(true);
61
- });
62
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,31 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { applyMask, selectAndApplyBestMask } from "../../src/mask/selection";
3
- import { Matrix } from "../../src/matrix/bit-matrix";
4
- describe("Masking", () => {
5
- it("should allow applying a specific mask", () => {
6
- const matrix = new Matrix(21);
7
- // Mask 0: (x+y)%2 == 0
8
- applyMask(matrix, 0);
9
- // (0,0) -> even -> flip inv (0->1)
10
- expect(matrix.get(0, 0)).toBe(1);
11
- // (0,1) -> odd -> 0
12
- expect(matrix.get(0, 1)).toBe(0);
13
- });
14
- it("should select the best mask", () => {
15
- const matrix = new Matrix(21);
16
- // Empty matrix -> penalty calculation will favor a mask that breaks up large white areas
17
- // or one that doesn't create false patterns.
18
- const result = selectAndApplyBestMask(matrix, "M");
19
- expect(result.mask).toBeGreaterThanOrEqual(0);
20
- expect(result.mask).toBeLessThanOrEqual(7);
21
- expect(result.penalties).toHaveLength(8);
22
- // Ensure format info was written
23
- // (8,0) to (8,8) area
24
- let formatInfoWritten = false;
25
- for (let i = 0; i < 9; i++) {
26
- if (matrix.isReserved(i, 8))
27
- formatInfoWritten = true;
28
- }
29
- expect(formatInfoWritten).toBe(true);
30
- });
31
- });
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes