hexatess-code 0.3.0__tar.gz → 0.3.1__tar.gz

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.

Potentially problematic release.


This version of hexatess-code might be problematic. Click here for more details.

Files changed (34) hide show
  1. {hexatess_code-0.3.0/src/hexatess_code.egg-info → hexatess_code-0.3.1}/PKG-INFO +44 -13
  2. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/README.md +43 -12
  3. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/pyproject.toml +1 -1
  4. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/__init__.py +12 -8
  5. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/camera.py +434 -157
  6. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/cli.py +21 -9
  7. hexatess_code-0.3.1/src/hexatess/decoder.py +110 -0
  8. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/encoder.py +39 -6
  9. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/header.py +27 -3
  10. {hexatess_code-0.3.0 → hexatess_code-0.3.1/src/hexatess_code.egg-info}/PKG-INFO +44 -13
  11. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_camera.py +12 -0
  12. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_codec.py +72 -1
  13. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_conformance.py +6 -3
  14. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_header.py +32 -0
  15. hexatess_code-0.3.0/src/hexatess/decoder.py +0 -75
  16. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/LICENSE +0 -0
  17. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/setup.cfg +0 -0
  18. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/galois.py +0 -0
  19. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/geometry.py +0 -0
  20. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/masks.py +0 -0
  21. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/reedsolomon.py +0 -0
  22. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/render.py +0 -0
  23. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess/resilience.py +0 -0
  24. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/SOURCES.txt +0 -0
  25. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/dependency_links.txt +0 -0
  26. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/entry_points.txt +0 -0
  27. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/requires.txt +0 -0
  28. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/src/hexatess_code.egg-info/top_level.txt +0 -0
  29. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_galois.py +0 -0
  30. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_geometry.py +0 -0
  31. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_masks.py +0 -0
  32. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_reedsolomon.py +0 -0
  33. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_render.py +0 -0
  34. {hexatess_code-0.3.0 → hexatess_code-0.3.1}/tests/test_resilience.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexatess-code
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Hexatess Code - an experimental 2D barcode on a hexagonal grid with Reed-Solomon error correction
5
5
  Author: The Hexatess Code Authors
6
6
  License: MIT License
@@ -115,10 +115,12 @@ text, stats = decode(grid) # ('Hello, Hexatess!', {...})
115
115
  > specification and reference implementation are solid and heavily
116
116
  > tested (2,500+ tests, conformance vectors). A **camera decoder**
117
117
  > (`hexatess.camera`, optional `[camera]` extra) already reads symbols
118
- > from real photographs — printed labels, foil transparencies, tilted
119
- > and rotated shots. See the roadmap below. Adopting a young format
120
- > is a deliberate bet; the [full format specification](SPECIFICATION.md)
121
- > is the insurance.
118
+ > from real photographs in about a second — printed labels, foil
119
+ > transparencies, tilted and rotated shots. Since spec v0.3 payload
120
+ > text is zlib-compressed automatically, so long texts fit into
121
+ > considerably smaller symbols. See the roadmap below. Adopting a
122
+ > young format is a deliberate bet; the [full format
123
+ > specification](SPECIFICATION.md) is the insurance.
122
124
 
123
125
  ## Installation
124
126
 
@@ -141,19 +143,43 @@ hexatess --demo # demo symbol + robustness statistics
141
143
  hexatess decode-photo photo1.jpg photo2.jpg # read symbols from photos
142
144
  ```
143
145
 
146
+ Payload text is zlib-compressed automatically when that saves space
147
+ (`--no-compress` disables it; the header flag keeps decoders fully
148
+ backward compatible).
149
+
150
+ ## Payload compression (spec v0.3)
151
+
152
+ One header bit marks the payload as a zlib stream. The encoder applies
153
+ it only when it strictly helps, and decoders inflate transparently —
154
+ symbols without the flag are byte-identical to v0.2. What that means
155
+ in practice (EC 30 unless noted):
156
+
157
+ | payload | raw | stored | symbol |
158
+ |---|---|---|---|
159
+ | 80 digits | 80 B | 21 B | rmax 17 → 11 |
160
+ | `"X" × 250` | 250 B | 12 B | rmax 30 → 10 |
161
+ | 849-byte Slovene paragraph | 849 B | 203 B | would not fit → rmax 28 |
162
+ | short strings (≤ ~30 B) | — | unchanged | overhead wins |
163
+
164
+ The maximum *stored* capacity is unchanged (329 bytes at EC 5), so
165
+ incompressible data behaves exactly as before.
166
+
144
167
  ## API
145
168
 
146
169
  | Function | Description |
147
170
  |---|---|
148
- | `encode(text, ec_pct=30, mask_id="auto", min_rings=None)` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
149
- | `decode(grid)` | grid → `(text, stats)`; RS-corrects transparently |
171
+ | `encode(text, ec_pct=30, mask_id="auto", min_rings=None, compress="auto")` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
172
+ | `decode(grid)` | grid → `(text, stats)`; RS-corrects and inflates transparently |
150
173
  | `render(grid, path, size_px=18, ...)` | grid → PNG (pointy-top hexagons, quiet zone, supersampling) |
151
174
  | `sample_grid_from_image(path, rmax, ...)` | ideal re-sampling of a rendered PNG (self-test helper) |
152
175
  | `run_tests(...)` | noise/blob robustness statistics |
153
176
  | `hexatess.camera.decode_photo(path)` | photograph → `(text, stats)`; finder detection, perspective handling, adaptive sampling (optional `[camera]` extra) |
154
177
 
155
178
  `params` / `stats` contain `rmax` (radius in rings), `mask`, `ec`,
156
- `blocks` (list of `(data_bytes, ecc_bytes)`) and `data_len`.
179
+ `blocks` (list of `(data_bytes, ecc_bytes)`), `data_len` (stored
180
+ length) and `compressed`; `stats` also reports `repair_bits` (the RS
181
+ correction ledger) and, for camera decodes, `sector` and
182
+ `finder_hits`.
157
183
 
158
184
  ## Error-correction budget
159
185
 
@@ -177,7 +203,7 @@ concentrate inside whole bytes.
177
203
  The format is deliberately **specification-first**: everything needed
178
204
  for an independent implementation is in
179
205
  [`SPECIFICATION.md`](SPECIFICATION.md), and
180
- [`test_vectors/vectors_v0.2.json`](test_vectors/vectors_v0.2.json)
206
+ [`test_vectors/vectors_v0.3.json`](test_vectors/vectors_v0.3.json)
181
207
  contains fixed inputs/outputs (grids, headers, damaged symbols, expected
182
208
  results) to verify conformance. If your Rust/Go/JS decoder passes the
183
209
  vectors, it speaks Hexatess Code.
@@ -187,12 +213,17 @@ vectors, it speaks Hexatess Code.
187
213
  1. ~~v0.2/0.3 — camera decoding~~ **done (v0.3.0):** `hexatess.camera`
188
214
  reads symbols from photographs — bullseye detection, homography +
189
215
  correction-field warp handling, adaptive sampling; validated on
190
- printed foil with curl and glare.
191
- 2. **Erasure decoding:** declare blob-occluded modules as
216
+ printed foil with curl and glare. **v0.3.1:** ≈10× faster
217
+ (a typical 12 MP photo now takes about a second) plus stable
218
+ outer-ring sampling and mis-decode-proof pose selection.
219
+ 2. ~~v0.3 — payload compression~~ **done (v0.3.1):** zlib flag bit in
220
+ the header, applied automatically when it helps.
221
+ 3. **Erasure decoding:** declare blob-occluded modules as
192
222
  erasures → doubles correctable symbol counts.
193
- 3. **JavaScript/TypeScript SDK** + online playground (generate a code
223
+ 4. **JavaScript/TypeScript SDK** + online playground (generate a code
194
224
  in the browser in 10 seconds).
195
- 4. Larger radii / capacity beyond 329 bytes (breaking header change).
225
+ 5. Larger radii / capacity beyond 329 stored bytes (breaking header
226
+ change).
196
227
 
197
228
  Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
198
229
 
@@ -51,10 +51,12 @@ text, stats = decode(grid) # ('Hello, Hexatess!', {...})
51
51
  > specification and reference implementation are solid and heavily
52
52
  > tested (2,500+ tests, conformance vectors). A **camera decoder**
53
53
  > (`hexatess.camera`, optional `[camera]` extra) already reads symbols
54
- > from real photographs — printed labels, foil transparencies, tilted
55
- > and rotated shots. See the roadmap below. Adopting a young format
56
- > is a deliberate bet; the [full format specification](SPECIFICATION.md)
57
- > is the insurance.
54
+ > from real photographs in about a second — printed labels, foil
55
+ > transparencies, tilted and rotated shots. Since spec v0.3 payload
56
+ > text is zlib-compressed automatically, so long texts fit into
57
+ > considerably smaller symbols. See the roadmap below. Adopting a
58
+ > young format is a deliberate bet; the [full format
59
+ > specification](SPECIFICATION.md) is the insurance.
58
60
 
59
61
  ## Installation
60
62
 
@@ -77,19 +79,43 @@ hexatess --demo # demo symbol + robustness statistics
77
79
  hexatess decode-photo photo1.jpg photo2.jpg # read symbols from photos
78
80
  ```
79
81
 
82
+ Payload text is zlib-compressed automatically when that saves space
83
+ (`--no-compress` disables it; the header flag keeps decoders fully
84
+ backward compatible).
85
+
86
+ ## Payload compression (spec v0.3)
87
+
88
+ One header bit marks the payload as a zlib stream. The encoder applies
89
+ it only when it strictly helps, and decoders inflate transparently —
90
+ symbols without the flag are byte-identical to v0.2. What that means
91
+ in practice (EC 30 unless noted):
92
+
93
+ | payload | raw | stored | symbol |
94
+ |---|---|---|---|
95
+ | 80 digits | 80 B | 21 B | rmax 17 → 11 |
96
+ | `"X" × 250` | 250 B | 12 B | rmax 30 → 10 |
97
+ | 849-byte Slovene paragraph | 849 B | 203 B | would not fit → rmax 28 |
98
+ | short strings (≤ ~30 B) | — | unchanged | overhead wins |
99
+
100
+ The maximum *stored* capacity is unchanged (329 bytes at EC 5), so
101
+ incompressible data behaves exactly as before.
102
+
80
103
  ## API
81
104
 
82
105
  | Function | Description |
83
106
  |---|---|
84
- | `encode(text, ec_pct=30, mask_id="auto", min_rings=None)` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
85
- | `decode(grid)` | grid → `(text, stats)`; RS-corrects transparently |
107
+ | `encode(text, ec_pct=30, mask_id="auto", min_rings=None, compress="auto")` | UTF-8 text → `(grid, params)`; `grid` maps axial `(q, r)` to `0/1` |
108
+ | `decode(grid)` | grid → `(text, stats)`; RS-corrects and inflates transparently |
86
109
  | `render(grid, path, size_px=18, ...)` | grid → PNG (pointy-top hexagons, quiet zone, supersampling) |
87
110
  | `sample_grid_from_image(path, rmax, ...)` | ideal re-sampling of a rendered PNG (self-test helper) |
88
111
  | `run_tests(...)` | noise/blob robustness statistics |
89
112
  | `hexatess.camera.decode_photo(path)` | photograph → `(text, stats)`; finder detection, perspective handling, adaptive sampling (optional `[camera]` extra) |
90
113
 
91
114
  `params` / `stats` contain `rmax` (radius in rings), `mask`, `ec`,
92
- `blocks` (list of `(data_bytes, ecc_bytes)`) and `data_len`.
115
+ `blocks` (list of `(data_bytes, ecc_bytes)`), `data_len` (stored
116
+ length) and `compressed`; `stats` also reports `repair_bits` (the RS
117
+ correction ledger) and, for camera decodes, `sector` and
118
+ `finder_hits`.
93
119
 
94
120
  ## Error-correction budget
95
121
 
@@ -113,7 +139,7 @@ concentrate inside whole bytes.
113
139
  The format is deliberately **specification-first**: everything needed
114
140
  for an independent implementation is in
115
141
  [`SPECIFICATION.md`](SPECIFICATION.md), and
116
- [`test_vectors/vectors_v0.2.json`](test_vectors/vectors_v0.2.json)
142
+ [`test_vectors/vectors_v0.3.json`](test_vectors/vectors_v0.3.json)
117
143
  contains fixed inputs/outputs (grids, headers, damaged symbols, expected
118
144
  results) to verify conformance. If your Rust/Go/JS decoder passes the
119
145
  vectors, it speaks Hexatess Code.
@@ -123,12 +149,17 @@ vectors, it speaks Hexatess Code.
123
149
  1. ~~v0.2/0.3 — camera decoding~~ **done (v0.3.0):** `hexatess.camera`
124
150
  reads symbols from photographs — bullseye detection, homography +
125
151
  correction-field warp handling, adaptive sampling; validated on
126
- printed foil with curl and glare.
127
- 2. **Erasure decoding:** declare blob-occluded modules as
152
+ printed foil with curl and glare. **v0.3.1:** ≈10× faster
153
+ (a typical 12 MP photo now takes about a second) plus stable
154
+ outer-ring sampling and mis-decode-proof pose selection.
155
+ 2. ~~v0.3 — payload compression~~ **done (v0.3.1):** zlib flag bit in
156
+ the header, applied automatically when it helps.
157
+ 3. **Erasure decoding:** declare blob-occluded modules as
128
158
  erasures → doubles correctable symbol counts.
129
- 3. **JavaScript/TypeScript SDK** + online playground (generate a code
159
+ 4. **JavaScript/TypeScript SDK** + online playground (generate a code
130
160
  in the browser in 10 seconds).
131
- 4. Larger radii / capacity beyond 329 bytes (breaking header change).
161
+ 5. Larger radii / capacity beyond 329 stored bytes (breaking header
162
+ change).
132
163
 
133
164
  Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
134
165
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hexatess-code"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Hexatess Code - an experimental 2D barcode on a hexagonal grid with Reed-Solomon error correction"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -1,10 +1,12 @@
1
1
  """Hexatess Code - an experimental 2D barcode on a hexagonal grid.
2
2
 
3
- Reference implementation of specification v0.2. The symbol is a
3
+ Reference implementation of specification v0.3. The symbol is a
4
4
  hexagonal lattice with a hexagonal bullseye finder, an
5
5
  orientation key ring, spiral serialization from the centre outwards,
6
6
  a Reed-Solomon protected header and a continuously selectable
7
- error-correction budget of 5-90 percent (Aztec-style).
7
+ error-correction budget of 5-90 percent (Aztec-style). Since spec
8
+ v0.3 the payload can optionally be zlib-compressed (one header flag
9
+ bit; applied automatically whenever it saves space).
8
10
 
9
11
  Quick start
10
12
  -----------
@@ -18,13 +20,13 @@ Quick start
18
20
  'Hello, Hexatess!'
19
21
 
20
22
  See SPECIFICATION.md in the repository for the full format
21
- specification, and test_vectors/vectors_v0.2.json for conformance
23
+ specification, and test_vectors/vectors_v0.3.json for conformance
22
24
  data usable by independent implementations.
23
25
  """
24
26
 
25
27
  from __future__ import annotations
26
28
 
27
- from .decoder import decode
29
+ from .decoder import decode, payload_to_text
28
30
  from .encoder import encode
29
31
  from .geometry import (
30
32
  DIRS,
@@ -37,6 +39,7 @@ from .geometry import (
37
39
  from .header import (
38
40
  BULLSEYE_RINGS,
39
41
  BLOCK_DATA_MAX,
42
+ COMPRESSED_FLAG,
40
43
  DATA_RING0,
41
44
  KEY_RING,
42
45
  MAX_DATA_BYTES,
@@ -51,14 +54,15 @@ from .header import (
51
54
  pack_mode,
52
55
  plan_blocks,
53
56
  unpack_mode,
57
+ unpack_mode_ex,
54
58
  )
55
59
  from .masks import evaluate_mask, mask_bit, mask_payload, select_mask
56
60
  from .reedsolomon import rs_correct_msg, rs_encode_msg
57
61
  from .render import render, sample_grid_from_image
58
62
  from .resilience import add_blob_damage, add_random_noise, run_tests
59
63
 
60
- __version__ = "0.3.0"
61
- SPEC_VERSION = "0.2"
64
+ __version__ = "0.3.1"
65
+ SPEC_VERSION = "0.3"
62
66
 
63
67
  __all__ = [
64
68
  # high-level API
@@ -69,7 +73,7 @@ __all__ = [
69
73
  "ring_capacity",
70
74
  # framing
71
75
  "bytes_to_bits", "bits_to_bytes", "pack_mode", "unpack_mode",
72
- "plan_blocks",
76
+ "unpack_mode_ex", "plan_blocks", "payload_to_text",
73
77
  # masks
74
78
  "mask_bit", "mask_payload", "evaluate_mask", "select_mask",
75
79
  # error correction
@@ -77,7 +81,7 @@ __all__ = [
77
81
  # constants
78
82
  "BULLSEYE_RINGS", "KEY_RING", "DATA_RING0", "MAX_RINGS",
79
83
  "BLOCK_DATA_MAX", "MODE_BYTES", "MODE_ECC", "MODE_BITS",
80
- "MIN_EC_PCT", "MAX_EC_PCT", "MAX_DATA_BYTES",
84
+ "MIN_EC_PCT", "MAX_EC_PCT", "MAX_DATA_BYTES", "COMPRESSED_FLAG",
81
85
  # meta
82
86
  "__version__", "SPEC_VERSION",
83
87
  ]