@ideare/kit 0.0.0-stage → 0.1.0-next.0
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.
- package/LICENSE +21 -0
- package/README.md +148 -2
- package/THIRD_PARTY.md +52 -0
- package/dist/brand/schema.d.ts +265 -0
- package/dist/brand/schema.js +271 -0
- package/dist/check/copy.d.ts +27 -0
- package/dist/check/copy.js +139 -0
- package/dist/check/index.d.ts +54 -0
- package/dist/check/index.js +492 -0
- package/dist/color/contrast.d.ts +41 -0
- package/dist/color/contrast.js +84 -0
- package/dist/color/palette.d.ts +27 -0
- package/dist/color/palette.js +114 -0
- package/dist/color/wcag.d.ts +22 -0
- package/dist/color/wcag.js +50 -0
- package/dist/draft/draft.d.ts +61 -0
- package/dist/draft/draft.js +383 -0
- package/dist/editor/history.d.ts +78 -0
- package/dist/editor/history.js +221 -0
- package/dist/editor/operations.d.ts +112 -0
- package/dist/editor/operations.js +621 -0
- package/dist/files/docs.d.ts +49 -0
- package/dist/files/docs.js +392 -0
- package/dist/files/generate.d.ts +47 -0
- package/dist/files/generate.js +254 -0
- package/dist/files/png.d.ts +40 -0
- package/dist/files/png.js +168 -0
- package/dist/files/raster.d.ts +14 -0
- package/dist/files/raster.js +0 -0
- package/dist/files/templates.d.ts +162 -0
- package/dist/files/templates.js +390 -0
- package/dist/files/tokens.d.ts +22 -0
- package/dist/files/tokens.js +204 -0
- package/dist/fonts/catalog.d.ts +3 -0
- package/dist/fonts/catalog.js +345 -0
- package/dist/fonts/ofl.d.ts +5 -0
- package/dist/fonts/ofl.js +5 -0
- package/dist/fonts/pick.d.ts +33 -0
- package/dist/fonts/pick.js +73 -0
- package/dist/imports/folder.d.ts +80 -0
- package/dist/imports/folder.js +124 -0
- package/dist/imports/unzip.d.ts +32 -0
- package/dist/imports/unzip.js +128 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +34 -0
- package/dist/marks/draw.d.ts +48 -0
- package/dist/marks/draw.js +149 -0
- package/dist/marks/icons.data.d.ts +5 -0
- package/dist/marks/icons.data.js +600 -0
- package/dist/marks/svg.d.ts +46 -0
- package/dist/marks/svg.js +341 -0
- package/dist/node/index.d.ts +13 -0
- package/dist/node/index.js +41 -0
- package/dist/posts/pix.d.ts +19 -0
- package/dist/posts/pix.js +49 -0
- package/dist/posts/templates.d.ts +45 -0
- package/dist/posts/templates.js +868 -0
- package/dist/resvg/index.d.ts +2 -0
- package/dist/resvg/index.js +22 -0
- package/dist/scene/brand-art.d.ts +49 -0
- package/dist/scene/brand-art.js +154 -0
- package/dist/scene/frame.d.ts +89 -0
- package/dist/scene/frame.js +213 -0
- package/dist/scene/grain.d.ts +7 -0
- package/dist/scene/grain.js +7 -0
- package/dist/scene/qr.d.ts +10 -0
- package/dist/scene/qr.js +359 -0
- package/dist/scene/render.d.ts +76 -0
- package/dist/scene/render.js +557 -0
- package/dist/scene/schema.d.ts +271 -0
- package/dist/scene/schema.js +257 -0
- package/dist/sign.d.ts +47 -0
- package/dist/sign.js +88 -0
- package/dist/text/engine.d.ts +128 -0
- package/dist/text/engine.js +245 -0
- package/dist/text/fonts.d.ts +56 -0
- package/dist/text/fonts.js +65 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/wasm/index.d.ts +15 -0
- package/dist/wasm/index.js +46 -0
- package/package.json +78 -5
- package/schema/brand.v1.json +442 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A seeded 128 px noise tile (RGBA PNG, 26247 bytes) from the S1b render spike. Pages tile it with
|
|
3
|
+
* a userSpaceOnUse <pattern> for grain: 0% difference between resvg and Chrome, where
|
|
4
|
+
* feTurbulence differed on 10.8% of pixels (docs/spikes/render.md, rule 8).
|
|
5
|
+
*/
|
|
6
|
+
export const GRAIN_TILE_SIZE = 128;
|
|
7
|
+
export const GRAIN_TILE_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAgAElEQVR4nE3dCdim5fzG8bsQM6hkC/nHWFJxo0xUShnLZELWUvasCZVQMVT2XSjGviWZJoQhS7ZEhhbbhEQ0hbIXivI/Ptcx347e43jnfeZ57vu6fst5nr/fdT/3Mq1YsWI+5JBD5p///OfzypUr53vc4x7za1/72vkhD3nI/KEPfWj+1a9+NR922GHzbrvtNl944YXzaaedNj/iEY+Yt9tuu/nII4+cP/3pT8/vf//75w022GBs97Of/Wy+6U1vOr/73e+eb3SjG80PetCD5k033XS+5z3vOX/ve9+bX/e61803vOEN57vf/e7z2rVr55e//OXzWWedNT/qUY+aX/SiF80LFy4c+7/1rW+d3/Oe98wPeMAD5uOPP35evHjx/PCHP3z+6le/Ov/yl7+cjz766Pk+97nPvHTp0nnZsmXzzW9+8/mTn/zkfPLJJ8+f/exn5z/96U/zrW51q/lvf/vb/IIXvGD+61//Ot/pTneat9hii/n617/+/MQnPnF+2cteNmxft27dfItb3GL+4he/OP/5z3+eH/rQh86f//znZz+vec1r5h/96EfzlltuOV9zzTXz29/+9vmPf/zj/MIXvnB+17veNT/+8Y8fdj760Y+ev/zlLw9b+ME3dlx99dXz7373u2HDm970pvH5bW972/mUU06Z99prrxGTSy65ZN58883nww8/fMx1xzvecf73v/89cvGFL3xhbLvjjjvOz33uc+dtttlmPuGEE8a8YvLhD394/u1vfzs/5jGPmb///e/Pz3jGM+ZFixbN559//vzkJz953nDDDUfMPvrRj86//vWv59///vdjv/e+973z8uXL50libnKTmwyHTfawhz1sJN8EEnW/+91vOPGOd7xj/u9//zsMkLy3ve1tw5lnPvOZIxjPe97zBhie9axnjX0vuuiiYdQrXvGKEWTOH3fccfP973//8f6ee+45ggZ05th2221HwATlDne4w/h74xvfeH7nO985DJaspz3tafPq1avnP/zhD/PZZ589H3TQQfNWW201QAq0wGdeCbjLXe4yf/CDHxzgsv/+++8/bPjPf/4zHAeitWvXDkBvvPHG82abbTZsOOqoo+ZVq1bN3/rWt4bf55xz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";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A QR code encoder: byte mode (UTF-8), versions 1 to 40, all four error correction levels, the
|
|
3
|
+
* mask chosen by the standard's penalty score. Ported from Project Nayuki's QR Code generator
|
|
4
|
+
* (MIT), trimmed to what a scene needs: a module matrix.
|
|
5
|
+
*/
|
|
6
|
+
export type QrEcc = "L" | "M" | "Q" | "H";
|
|
7
|
+
/** The QR code for `text` as rows of modules (true is dark), without the quiet zone. */
|
|
8
|
+
export declare function qrMatrix(text: string, ecc?: QrEcc): boolean[][];
|
|
9
|
+
/** The dark modules as one path, `scale` px per module, offset by the quiet zone. */
|
|
10
|
+
export declare function qrPath(modules: boolean[][], scale: number, x0: number, y0: number): string;
|
package/dist/scene/qr.js
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A QR code encoder: byte mode (UTF-8), versions 1 to 40, all four error correction levels, the
|
|
3
|
+
* mask chosen by the standard's penalty score. Ported from Project Nayuki's QR Code generator
|
|
4
|
+
* (MIT), trimmed to what a scene needs: a module matrix.
|
|
5
|
+
*/
|
|
6
|
+
const ORDINAL = { L: 0, M: 1, Q: 2, H: 3 };
|
|
7
|
+
const FORMAT_BITS = { L: 1, M: 0, Q: 3, H: 2 };
|
|
8
|
+
// prettier-ignore
|
|
9
|
+
const ECC_PER_BLOCK = [
|
|
10
|
+
[-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
|
|
11
|
+
[-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28],
|
|
12
|
+
[-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
|
|
13
|
+
[-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
|
|
14
|
+
];
|
|
15
|
+
// prettier-ignore
|
|
16
|
+
const BLOCKS = [
|
|
17
|
+
[-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25],
|
|
18
|
+
[-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49],
|
|
19
|
+
[-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68],
|
|
20
|
+
[-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81],
|
|
21
|
+
];
|
|
22
|
+
function table(rows, ecc, version) {
|
|
23
|
+
return rows[ORDINAL[ecc]]?.[version] ?? 0;
|
|
24
|
+
}
|
|
25
|
+
function rawDataModules(version) {
|
|
26
|
+
let result = (16 * version + 128) * version + 64;
|
|
27
|
+
if (version >= 2) {
|
|
28
|
+
const align = Math.floor(version / 7) + 2;
|
|
29
|
+
result -= (25 * align - 10) * align - 55;
|
|
30
|
+
if (version >= 7)
|
|
31
|
+
result -= 36;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function dataCodewords(version, ecc) {
|
|
36
|
+
return (Math.floor(rawDataModules(version) / 8) -
|
|
37
|
+
table(ECC_PER_BLOCK, ecc, version) * table(BLOCKS, ecc, version));
|
|
38
|
+
}
|
|
39
|
+
function gfMultiply(x, y) {
|
|
40
|
+
let z = 0;
|
|
41
|
+
for (let i = 7; i >= 0; i--) {
|
|
42
|
+
z = (z << 1) ^ ((z >>> 7) * 0x11d);
|
|
43
|
+
z ^= ((y >>> i) & 1) * x;
|
|
44
|
+
}
|
|
45
|
+
return z & 0xff;
|
|
46
|
+
}
|
|
47
|
+
function rsDivisor(degree) {
|
|
48
|
+
const result = new Array(degree).fill(0);
|
|
49
|
+
result[degree - 1] = 1;
|
|
50
|
+
let root = 1;
|
|
51
|
+
for (let i = 0; i < degree; i++) {
|
|
52
|
+
for (let j = 0; j < result.length; j++) {
|
|
53
|
+
result[j] = gfMultiply(result[j] ?? 0, root);
|
|
54
|
+
if (j + 1 < result.length)
|
|
55
|
+
result[j] = (result[j] ?? 0) ^ (result[j + 1] ?? 0);
|
|
56
|
+
}
|
|
57
|
+
root = gfMultiply(root, 0x02);
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
function rsRemainder(data, divisor) {
|
|
62
|
+
const result = divisor.map(() => 0);
|
|
63
|
+
for (const byte of data) {
|
|
64
|
+
const factor = byte ^ (result.shift() ?? 0);
|
|
65
|
+
result.push(0);
|
|
66
|
+
divisor.forEach((coefficient, index) => {
|
|
67
|
+
result[index] = (result[index] ?? 0) ^ gfMultiply(coefficient, factor);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
const bit = (value, index) => ((value >>> index) & 1) !== 0;
|
|
73
|
+
/** The QR code for `text` as rows of modules (true is dark), without the quiet zone. */
|
|
74
|
+
export function qrMatrix(text, ecc = "M") {
|
|
75
|
+
const bytes = [...new TextEncoder().encode(text)];
|
|
76
|
+
let version = 1;
|
|
77
|
+
for (; version <= 40; version++) {
|
|
78
|
+
const countBits = version < 10 ? 8 : 16;
|
|
79
|
+
if (4 + countBits + bytes.length * 8 <= dataCodewords(version, ecc) * 8)
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
if (version > 40)
|
|
83
|
+
throw new RangeError("That text is too long for a QR code.");
|
|
84
|
+
// The data bits: mode, count, bytes, terminator, padding.
|
|
85
|
+
const bits = [];
|
|
86
|
+
const push = (value, length) => {
|
|
87
|
+
for (let i = length - 1; i >= 0; i--)
|
|
88
|
+
bits.push((value >>> i) & 1);
|
|
89
|
+
};
|
|
90
|
+
push(0b0100, 4);
|
|
91
|
+
push(bytes.length, version < 10 ? 8 : 16);
|
|
92
|
+
for (const byte of bytes)
|
|
93
|
+
push(byte, 8);
|
|
94
|
+
const capacity = dataCodewords(version, ecc) * 8;
|
|
95
|
+
push(0, Math.min(4, capacity - bits.length));
|
|
96
|
+
push(0, (8 - (bits.length % 8)) % 8);
|
|
97
|
+
for (let pad = 0xec; bits.length < capacity; pad ^= 0xec ^ 0x11)
|
|
98
|
+
push(pad, 8);
|
|
99
|
+
const data = [];
|
|
100
|
+
for (let i = 0; i < bits.length; i += 8)
|
|
101
|
+
data.push(bits.slice(i, i + 8).reduce((acc, b) => (acc << 1) | b, 0));
|
|
102
|
+
// Error correction, split into blocks and interleaved.
|
|
103
|
+
const blockCount = table(BLOCKS, ecc, version);
|
|
104
|
+
const eccLength = table(ECC_PER_BLOCK, ecc, version);
|
|
105
|
+
const raw = Math.floor(rawDataModules(version) / 8);
|
|
106
|
+
const shortBlocks = blockCount - (raw % blockCount);
|
|
107
|
+
const shortLength = Math.floor(raw / blockCount);
|
|
108
|
+
const divisor = rsDivisor(eccLength);
|
|
109
|
+
const blocks = [];
|
|
110
|
+
for (let i = 0, k = 0; i < blockCount; i++) {
|
|
111
|
+
const chunk = data.slice(k, k + shortLength - eccLength + (i < shortBlocks ? 0 : 1));
|
|
112
|
+
k += chunk.length;
|
|
113
|
+
const remainder = rsRemainder(chunk, divisor);
|
|
114
|
+
if (i < shortBlocks)
|
|
115
|
+
chunk.push(0);
|
|
116
|
+
blocks.push(chunk.concat(remainder));
|
|
117
|
+
}
|
|
118
|
+
const codewords = [];
|
|
119
|
+
for (let i = 0; i < (blocks[0]?.length ?? 0); i++) {
|
|
120
|
+
blocks.forEach((block, j) => {
|
|
121
|
+
if (i !== shortLength - eccLength || j >= shortBlocks)
|
|
122
|
+
codewords.push(block[i] ?? 0);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const size = version * 4 + 17;
|
|
126
|
+
const modules = Array.from({ length: size }, () => new Array(size).fill(false));
|
|
127
|
+
const fixed = Array.from({ length: size }, () => new Array(size).fill(false));
|
|
128
|
+
const set = (x, y, dark) => {
|
|
129
|
+
const row = modules[y];
|
|
130
|
+
const fixedRow = fixed[y];
|
|
131
|
+
if (!row || !fixedRow)
|
|
132
|
+
return;
|
|
133
|
+
row[x] = dark;
|
|
134
|
+
fixedRow[x] = true;
|
|
135
|
+
};
|
|
136
|
+
for (let i = 0; i < size; i++) {
|
|
137
|
+
set(6, i, i % 2 === 0);
|
|
138
|
+
set(i, 6, i % 2 === 0);
|
|
139
|
+
}
|
|
140
|
+
const finder = (cx, cy) => {
|
|
141
|
+
for (let dy = -4; dy <= 4; dy++) {
|
|
142
|
+
for (let dx = -4; dx <= 4; dx++) {
|
|
143
|
+
const distance = Math.max(Math.abs(dx), Math.abs(dy));
|
|
144
|
+
const x = cx + dx;
|
|
145
|
+
const y = cy + dy;
|
|
146
|
+
if (x >= 0 && x < size && y >= 0 && y < size)
|
|
147
|
+
set(x, y, distance !== 2 && distance !== 4);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
finder(3, 3);
|
|
152
|
+
finder(size - 4, 3);
|
|
153
|
+
finder(3, size - 4);
|
|
154
|
+
if (version > 1) {
|
|
155
|
+
const count = Math.floor(version / 7) + 2;
|
|
156
|
+
const step = Math.floor((version * 8 + count * 3 + 5) / (count * 4 - 4)) * 2;
|
|
157
|
+
const positions = [6];
|
|
158
|
+
for (let pos = size - 7; positions.length < count; pos -= step)
|
|
159
|
+
positions.splice(1, 0, pos);
|
|
160
|
+
positions.forEach((py, i) => {
|
|
161
|
+
positions.forEach((px, j) => {
|
|
162
|
+
const corner = (i === 0 && j === 0) ||
|
|
163
|
+
(i === 0 && j === count - 1) ||
|
|
164
|
+
(i === count - 1 && j === 0);
|
|
165
|
+
if (corner)
|
|
166
|
+
return;
|
|
167
|
+
for (let dy = -2; dy <= 2; dy++) {
|
|
168
|
+
for (let dx = -2; dx <= 2; dx++)
|
|
169
|
+
set(px + dx, py + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const drawFormat = (mask) => {
|
|
175
|
+
const value = (FORMAT_BITS[ecc] << 3) | mask;
|
|
176
|
+
let rem = value;
|
|
177
|
+
for (let i = 0; i < 10; i++)
|
|
178
|
+
rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
|
|
179
|
+
const word = ((value << 10) | rem) ^ 0x5412;
|
|
180
|
+
for (let i = 0; i <= 5; i++)
|
|
181
|
+
set(8, i, bit(word, i));
|
|
182
|
+
set(8, 7, bit(word, 6));
|
|
183
|
+
set(8, 8, bit(word, 7));
|
|
184
|
+
set(7, 8, bit(word, 8));
|
|
185
|
+
for (let i = 9; i < 15; i++)
|
|
186
|
+
set(14 - i, 8, bit(word, i));
|
|
187
|
+
for (let i = 0; i < 8; i++)
|
|
188
|
+
set(size - 1 - i, 8, bit(word, i));
|
|
189
|
+
for (let i = 8; i < 15; i++)
|
|
190
|
+
set(8, size - 15 + i, bit(word, i));
|
|
191
|
+
set(8, size - 8, true);
|
|
192
|
+
};
|
|
193
|
+
drawFormat(0);
|
|
194
|
+
if (version >= 7) {
|
|
195
|
+
let rem = version;
|
|
196
|
+
for (let i = 0; i < 12; i++)
|
|
197
|
+
rem = (rem << 1) ^ ((rem >>> 11) * 0x1f25);
|
|
198
|
+
const word = (version << 12) | rem;
|
|
199
|
+
for (let i = 0; i < 18; i++) {
|
|
200
|
+
const a = size - 11 + (i % 3);
|
|
201
|
+
const b = Math.floor(i / 3);
|
|
202
|
+
set(a, b, bit(word, i));
|
|
203
|
+
set(b, a, bit(word, i));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// The codewords, in the zigzag the standard fixes.
|
|
207
|
+
let index = 0;
|
|
208
|
+
for (let right = size - 1; right >= 1; right -= 2) {
|
|
209
|
+
if (right === 6)
|
|
210
|
+
right = 5;
|
|
211
|
+
for (let vertical = 0; vertical < size; vertical++) {
|
|
212
|
+
for (let j = 0; j < 2; j++) {
|
|
213
|
+
const x = right - j;
|
|
214
|
+
const upward = ((right + 1) & 2) === 0;
|
|
215
|
+
const y = upward ? size - 1 - vertical : vertical;
|
|
216
|
+
const row = modules[y];
|
|
217
|
+
if (row && !fixed[y]?.[x] && index < codewords.length * 8) {
|
|
218
|
+
row[x] = bit(codewords[index >>> 3] ?? 0, 7 - (index & 7));
|
|
219
|
+
index++;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const masked = (mask, x, y) => {
|
|
225
|
+
switch (mask) {
|
|
226
|
+
case 0:
|
|
227
|
+
return (x + y) % 2 === 0;
|
|
228
|
+
case 1:
|
|
229
|
+
return y % 2 === 0;
|
|
230
|
+
case 2:
|
|
231
|
+
return x % 3 === 0;
|
|
232
|
+
case 3:
|
|
233
|
+
return (x + y) % 3 === 0;
|
|
234
|
+
case 4:
|
|
235
|
+
return (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0;
|
|
236
|
+
case 5:
|
|
237
|
+
return ((x * y) % 2) + ((x * y) % 3) === 0;
|
|
238
|
+
case 6:
|
|
239
|
+
return (((x * y) % 2) + ((x * y) % 3)) % 2 === 0;
|
|
240
|
+
default:
|
|
241
|
+
return (((x + y) % 2) + ((x * y) % 3)) % 2 === 0;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const applyMask = (mask) => {
|
|
245
|
+
for (let y = 0; y < size; y++) {
|
|
246
|
+
const row = modules[y];
|
|
247
|
+
if (!row)
|
|
248
|
+
continue;
|
|
249
|
+
for (let x = 0; x < size; x++)
|
|
250
|
+
if (!fixed[y]?.[x] && masked(mask, x, y))
|
|
251
|
+
row[x] = !row[x];
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
let best = 0;
|
|
255
|
+
let bestPenalty = Number.POSITIVE_INFINITY;
|
|
256
|
+
for (let mask = 0; mask < 8; mask++) {
|
|
257
|
+
applyMask(mask);
|
|
258
|
+
drawFormat(mask);
|
|
259
|
+
const score = penalty(modules);
|
|
260
|
+
if (score < bestPenalty) {
|
|
261
|
+
best = mask;
|
|
262
|
+
bestPenalty = score;
|
|
263
|
+
}
|
|
264
|
+
applyMask(mask);
|
|
265
|
+
}
|
|
266
|
+
applyMask(best);
|
|
267
|
+
drawFormat(best);
|
|
268
|
+
return modules;
|
|
269
|
+
}
|
|
270
|
+
function penalty(modules) {
|
|
271
|
+
const size = modules.length;
|
|
272
|
+
const at = (x, y) => modules[y]?.[x] ?? false;
|
|
273
|
+
let result = 0;
|
|
274
|
+
const lines = (get) => {
|
|
275
|
+
for (let i = 0; i < size; i++) {
|
|
276
|
+
let color = false;
|
|
277
|
+
let run = 0;
|
|
278
|
+
const history = [0, 0, 0, 0, 0, 0, 0];
|
|
279
|
+
const addHistory = (length) => {
|
|
280
|
+
if (history[0] === 0)
|
|
281
|
+
length += size;
|
|
282
|
+
history.pop();
|
|
283
|
+
history.unshift(length);
|
|
284
|
+
};
|
|
285
|
+
const patterns = () => {
|
|
286
|
+
const n = history[1] ?? 0;
|
|
287
|
+
const core = n > 0 &&
|
|
288
|
+
history[2] === n &&
|
|
289
|
+
history[3] === n * 3 &&
|
|
290
|
+
history[4] === n &&
|
|
291
|
+
history[5] === n;
|
|
292
|
+
return ((core && (history[0] ?? 0) >= n * 4 && (history[6] ?? 0) >= n ? 1 : 0) +
|
|
293
|
+
(core && (history[6] ?? 0) >= n * 4 && (history[0] ?? 0) >= n ? 1 : 0));
|
|
294
|
+
};
|
|
295
|
+
for (let j = 0; j < size; j++) {
|
|
296
|
+
if (get(i, j) === color) {
|
|
297
|
+
run++;
|
|
298
|
+
if (run === 5)
|
|
299
|
+
result += 3;
|
|
300
|
+
else if (run > 5)
|
|
301
|
+
result++;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
addHistory(run);
|
|
305
|
+
if (!color)
|
|
306
|
+
result += patterns() * 40;
|
|
307
|
+
color = get(i, j);
|
|
308
|
+
run = 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (color) {
|
|
312
|
+
addHistory(run);
|
|
313
|
+
run = 0;
|
|
314
|
+
}
|
|
315
|
+
addHistory(run + size);
|
|
316
|
+
result += patterns() * 40;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
lines((y, x) => at(x, y));
|
|
320
|
+
lines((x, y) => at(x, y));
|
|
321
|
+
let dark = 0;
|
|
322
|
+
for (let y = 0; y < size; y++) {
|
|
323
|
+
for (let x = 0; x < size; x++) {
|
|
324
|
+
const color = at(x, y);
|
|
325
|
+
if (color)
|
|
326
|
+
dark++;
|
|
327
|
+
if (x < size - 1 &&
|
|
328
|
+
y < size - 1 &&
|
|
329
|
+
color === at(x + 1, y) &&
|
|
330
|
+
color === at(x, y + 1) &&
|
|
331
|
+
color === at(x + 1, y + 1)) {
|
|
332
|
+
result += 3;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const total = size * size;
|
|
337
|
+
result += (Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1) * 10;
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
/** The dark modules as one path, `scale` px per module, offset by the quiet zone. */
|
|
341
|
+
export function qrPath(modules, scale, x0, y0) {
|
|
342
|
+
const parts = [];
|
|
343
|
+
const round = (value) => Math.round(value * 100) / 100;
|
|
344
|
+
modules.forEach((row, y) => {
|
|
345
|
+
let x = 0;
|
|
346
|
+
while (x < row.length) {
|
|
347
|
+
if (!row[x]) {
|
|
348
|
+
x++;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
let end = x;
|
|
352
|
+
while (end < row.length && row[end])
|
|
353
|
+
end++;
|
|
354
|
+
parts.push(`M${round(x0 + x * scale)} ${round(y0 + y * scale)}h${round((end - x) * scale)}v${round(scale)}h${round(-(end - x) * scale)}z`);
|
|
355
|
+
x = end;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
return parts.join("");
|
|
359
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Brand } from "../brand/schema.ts";
|
|
2
|
+
import type { FontResolver } from "../text/fonts.ts";
|
|
3
|
+
import { TextEngine, type TextLayout } from "../text/engine.ts";
|
|
4
|
+
import type { AssetRef, Frame, Scene } from "./schema.ts";
|
|
5
|
+
/**
|
|
6
|
+
* renderScene: one scene page and a brand in, one SVG string out. The studio shows the string
|
|
7
|
+
* inline and the server rasterizes the same string with resvg, so the editor, the derived files
|
|
8
|
+
* and live images match by construction. Only the features docs/spikes/render.md allows are
|
|
9
|
+
* written: glyph paths for text, linear and radial gradients (never `fr`), clipPaths, blend
|
|
10
|
+
* modes inside an isolated group, filters with userSpaceOnUse regions inside a budget, a PNG
|
|
11
|
+
* grain tile, and pre-sized images at whole pixels.
|
|
12
|
+
*
|
|
13
|
+
* Each layer is drawn in its own space inside `<g transform="translate(x y) rotate(r w/2 h/2)">`,
|
|
14
|
+
* so its position and turn live in that one attribute (docs/spikes/editor.md, rule E6). With a
|
|
15
|
+
* `cache`, a layer whose content did not change reuses its markup, and a drag frame rewrites
|
|
16
|
+
* one transform instead of every glyph path.
|
|
17
|
+
*/
|
|
18
|
+
/** Bump when the output for the same scene and brand changes, so revisions change too. */
|
|
19
|
+
export declare const RENDERER_VERSION = "2";
|
|
20
|
+
/** Filter region pixels allowed per page: about two large shadows on a share card. */
|
|
21
|
+
export declare const FILTER_BUDGET_PX = 500000;
|
|
22
|
+
export type AssetResolver = (asset: AssetRef, drawn: {
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}) => Promise<{
|
|
26
|
+
href: string;
|
|
27
|
+
} | undefined>;
|
|
28
|
+
/** Keeps each layer's markup between renders. Make one per editor stage with createRenderCache. */
|
|
29
|
+
export type RenderCache = {
|
|
30
|
+
readonly prefix: string;
|
|
31
|
+
};
|
|
32
|
+
export type RenderContext = {
|
|
33
|
+
fonts: FontResolver;
|
|
34
|
+
/** Turns an asset into an href (a data: URI or an https URL), sized to how it is drawn. */
|
|
35
|
+
assets?: AssetResolver;
|
|
36
|
+
/** Slot values, as `t.<slot>` (what live image URLs carry) or the bare slot name. */
|
|
37
|
+
params?: Record<string, string>;
|
|
38
|
+
/** Which page to draw. Default 0. */
|
|
39
|
+
page?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Prefix for every id in the SVG. Default: the cache's own prefix, or one from the revision,
|
|
42
|
+
* so two scenes never clash.
|
|
43
|
+
*/
|
|
44
|
+
idPrefix?: string;
|
|
45
|
+
/** Reuse the markup of layers that did not change since the last render with this cache. */
|
|
46
|
+
cache?: RenderCache;
|
|
47
|
+
};
|
|
48
|
+
export type SceneCheck = {
|
|
49
|
+
rule: string;
|
|
50
|
+
level: "error" | "warning";
|
|
51
|
+
layer?: string;
|
|
52
|
+
/** Plain words for the owner. */
|
|
53
|
+
message: string;
|
|
54
|
+
/** The numbers behind it, for "Details". */
|
|
55
|
+
details?: Record<string, string | number>;
|
|
56
|
+
};
|
|
57
|
+
/** A layer's frame in the page's space. Text lines are in the layer's own space. */
|
|
58
|
+
export type LayerBox = Frame & {
|
|
59
|
+
lines?: TextLayout["lines"];
|
|
60
|
+
fontSize?: number;
|
|
61
|
+
};
|
|
62
|
+
export type RenderResult = {
|
|
63
|
+
svg: string;
|
|
64
|
+
boxes: Record<string, LayerBox>;
|
|
65
|
+
checks: SceneCheck[];
|
|
66
|
+
revision: string;
|
|
67
|
+
};
|
|
68
|
+
/** One text engine per font resolver, shared by every render. */
|
|
69
|
+
export declare function engineFor(fonts: FontResolver): Promise<TextEngine>;
|
|
70
|
+
/** A cache for one editor stage. Its prefix keeps its ids apart from other stages on the page. */
|
|
71
|
+
export declare function createRenderCache(): RenderCache;
|
|
72
|
+
/** Sorted keys and no undefined values, so equal content always gives the same string. */
|
|
73
|
+
export declare function canonical(value: unknown): string;
|
|
74
|
+
/** sha256 of the canonical scene, the brand (its version when approved) and the renderer. */
|
|
75
|
+
export declare function sceneRevision(scene: Scene, brand: Brand, params?: Record<string, string>): Promise<string>;
|
|
76
|
+
export declare function renderScene(input: Scene, brand: Brand, ctx: RenderContext): Promise<RenderResult>;
|