@pictogrammers/components 0.4.9 → 0.5.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.
- package/package.json +1 -1
- package/pg/annoy/annoy.css +1 -1
- package/pg/button/button.css +5 -4
- package/pg/buttonLink/buttonLink.css +1 -1
- package/pg/buttonMenu/__examples__/basic/basic.ts +2 -2
- package/pg/buttonMenu/buttonMenu.ts +4 -1
- package/pg/cardUser/cardUser.css +1 -1
- package/pg/database/__examples__/basic/basic.ts +0 -1
- package/pg/grid/__examples__/basic/basic.ts +1 -4
- package/pg/grid/grid.css +1 -1
- package/pg/header/header.css +1 -1
- package/pg/icon/__examples__/basic/basic.ts +1 -1
- package/pg/inputCheck/__examples__/basic/basic.ts +1 -1
- package/pg/inputCheck/inputCheck.css +5 -0
- package/pg/inputCheck/inputCheck.ts +4 -0
- package/pg/inputCheckList/__examples__/basic/basic.ts +1 -1
- package/pg/inputFileLocal/inputFileLocal.css +1 -1
- package/pg/inputNumber/README.md +27 -0
- package/pg/inputNumber/__examples__/basic/basic.html +9 -0
- package/pg/inputNumber/__examples__/basic/basic.ts +30 -0
- package/pg/inputNumber/inputNumber.css +34 -0
- package/pg/inputNumber/inputNumber.html +1 -0
- package/pg/inputNumber/inputNumber.spec.ts +59 -0
- package/pg/inputNumber/inputNumber.ts +63 -0
- package/pg/inputPixelEditor/README.md +212 -29
- package/pg/inputPixelEditor/__examples__/basic/basic.css +8 -0
- package/pg/inputPixelEditor/__examples__/basic/basic.html +29 -7
- package/pg/inputPixelEditor/__examples__/basic/basic.ts +274 -13
- package/pg/inputPixelEditor/__examples__/basic/constants.ts +62 -0
- package/pg/inputPixelEditor/inputPixelEditor.css +37 -2
- package/pg/inputPixelEditor/inputPixelEditor.html +22 -0
- package/pg/inputPixelEditor/inputPixelEditor.ts +826 -80
- package/pg/inputPixelEditor/utils/bitmapToMask.ts +22 -8
- package/pg/inputPixelEditor/utils/blobToImage.ts +11 -0
- package/pg/inputPixelEditor/utils/canvasToPngBuffer.ts +12 -0
- package/pg/inputPixelEditor/utils/constants.ts +55 -1
- package/pg/inputPixelEditor/utils/crc32.ts +116 -0
- package/pg/inputPixelEditor/utils/diffMap.ts +32 -0
- package/pg/inputPixelEditor/utils/generateGradient.ts +112 -0
- package/pg/inputPixelEditor/utils/getFloodFill.ts +83 -0
- package/pg/inputPixelEditor/utils/getGridColorIndexes.ts +13 -0
- package/pg/inputPixelEditor/utils/getOutline.ts +92 -0
- package/pg/inputPixelEditor/utils/inputMode.ts +7 -1
- package/pg/inputPixelEditor/utils/pixelSizes.ts +47 -0
- package/pg/inputPixelEditor/utils/pngMetadata.ts +487 -0
- package/pg/inputSelect/inputSelect.css +4 -4
- package/pg/inputText/inputText.css +14 -7
- package/pg/inputText/inputText.ts +5 -1
- package/pg/json/README.md +59 -0
- package/pg/json/__examples__/basic/basic.html +4 -0
- package/pg/json/__examples__/basic/basic.ts +31 -0
- package/pg/json/json.css +9 -0
- package/pg/json/json.html +1 -0
- package/pg/json/json.ts +124 -0
- package/pg/jsonArray/README.md +3 -0
- package/pg/jsonArray/jsonArray.css +15 -0
- package/pg/jsonArray/jsonArray.html +7 -0
- package/pg/jsonArray/jsonArray.ts +55 -0
- package/pg/jsonBoolean/README.md +3 -0
- package/pg/jsonBoolean/jsonBoolean.css +27 -0
- package/pg/jsonBoolean/jsonBoolean.html +5 -0
- package/pg/jsonBoolean/jsonBoolean.ts +69 -0
- package/pg/jsonNumber/README.md +3 -0
- package/pg/jsonNumber/jsonNumber.css +21 -0
- package/pg/jsonNumber/jsonNumber.html +5 -0
- package/pg/jsonNumber/jsonNumber.ts +42 -0
- package/pg/jsonObject/README.md +3 -0
- package/pg/jsonObject/jsonObject.css +11 -0
- package/pg/jsonObject/jsonObject.html +5 -0
- package/pg/jsonObject/jsonObject.ts +55 -0
- package/pg/jsonString/README.md +3 -0
- package/pg/jsonString/jsonString.css +21 -0
- package/pg/jsonString/jsonString.html +5 -0
- package/pg/jsonString/jsonString.ts +42 -0
- package/pg/menu/menu.ts +6 -5
- package/pg/menuItem/README.md +13 -2
- package/pg/menuItem/menuItem.css +17 -22
- package/pg/menuItem/menuItem.ts +8 -3
- package/pg/menuItemIcon/__examples__/basic/basic.html +1 -1
- package/pg/menuItemIcon/__examples__/basic/basic.ts +7 -0
- package/pg/menuItemIcon/menuItemIcon.css +18 -15
- package/pg/menuItemIcon/menuItemIcon.ts +8 -4
- package/pg/modification/__examples__/basic/basic.ts +1 -1
- package/pg/overlayMenu/overlayMenu.ts +6 -2
- package/pg/overlaySelectMenu/overlaySelectMenu.ts +6 -0
- package/pg/overlaySubMenu/overlaySubMenu.ts +6 -2
- package/pg/scroll/__examples__/basic/basic.ts +1 -1
- package/pg/search/search.css +1 -1
- package/pg/table/README.md +108 -0
- package/pg/table/__examples__/basic/basic.css +0 -0
- package/pg/table/__examples__/basic/basic.html +10 -0
- package/pg/table/__examples__/basic/basic.ts +111 -0
- package/pg/table/table.css +20 -0
- package/pg/table/table.html +6 -0
- package/pg/table/table.ts +86 -0
- package/pg/tableCellButtonIcon/README.md +3 -0
- package/pg/tableCellButtonIcon/tableCellButtonIcon.css +16 -0
- package/pg/tableCellButtonIcon/tableCellButtonIcon.html +5 -0
- package/pg/tableCellButtonIcon/tableCellButtonIcon.ts +34 -0
- package/pg/tableCellCheck/README.md +3 -0
- package/pg/tableCellCheck/tableCellCheck.css +15 -0
- package/pg/tableCellCheck/tableCellCheck.html +3 -0
- package/pg/tableCellCheck/tableCellCheck.ts +43 -0
- package/pg/tableCellNumber/README.md +3 -0
- package/pg/tableCellNumber/tableCellNumber.css +11 -0
- package/pg/tableCellNumber/tableCellNumber.html +3 -0
- package/pg/tableCellNumber/tableCellNumber.ts +40 -0
- package/pg/tableCellText/README.md +3 -0
- package/pg/tableCellText/tableCellText.css +11 -0
- package/pg/tableCellText/tableCellText.html +3 -0
- package/pg/tableCellText/tableCellText.ts +62 -0
- package/pg/tableColumn/README.md +3 -0
- package/pg/tableColumn/tableColumn.css +12 -0
- package/pg/tableColumn/tableColumn.html +1 -0
- package/pg/tableColumn/tableColumn.ts +29 -0
- package/pg/tableRow/README.md +3 -0
- package/pg/tableRow/tableRow.css +11 -0
- package/pg/tableRow/tableRow.html +1 -0
- package/pg/tableRow/tableRow.ts +77 -0
- package/pg/tabs/tabs.css +1 -1
- package/pg/tree/README.md +0 -3
- package/pg/tree/__examples__/basic/basic.ts +0 -1
- package/pg/treeItem/treeItem.css +3 -3
- package/favicon.svg +0 -20
- package/index.html +0 -321
- package/main.js +0 -2
- package/main.js.LICENSE.txt +0 -10
- package/pgAnnoy.js +0 -1
- package/pgApp.js +0 -1
- package/pgAvatar.js +0 -1
- package/pgButton.js +0 -1
- package/pgButtonGroup.js +0 -1
- package/pgButtonLink.js +0 -1
- package/pgButtonMenu.js +0 -1
- package/pgButtonToggle.js +0 -1
- package/pgCard.js +0 -1
- package/pgCardUser.js +0 -1
- package/pgColor.js +0 -1
- package/pgDatabase.js +0 -1
- package/pgDropdown.js +0 -1
- package/pgGrid.js +0 -1
- package/pgHeader.js +0 -1
- package/pgIcon.js +0 -1
- package/pgInputCheck.js +0 -1
- package/pgInputCheckList.js +0 -1
- package/pgInputFileLocal.js +0 -1
- package/pgInputHexRgb.js +0 -1
- package/pgInputPixelEditor.js +0 -1
- package/pgInputRange.js +0 -1
- package/pgInputSelect.js +0 -1
- package/pgInputText.js +0 -1
- package/pgInputTextIcon.js +0 -1
- package/pgInputUserSelect.js +0 -1
- package/pgListTag.js +0 -1
- package/pgMarkdown.js +0 -2
- package/pgMarkdown.js.LICENSE.txt +0 -10
- package/pgMenu.js +0 -1
- package/pgMenuDivider.js +0 -1
- package/pgMenuIcon.js +0 -1
- package/pgMenuItem.js +0 -1
- package/pgMenuItemIcon.js +0 -1
- package/pgModal.js +0 -1
- package/pgModalAlert.js +0 -1
- package/pgModification.js +0 -1
- package/pgNav.js +0 -1
- package/pgOverlay.js +0 -1
- package/pgOverlayContextMenu.js +0 -1
- package/pgOverlayMenu.js +0 -1
- package/pgOverlaySelectMenu.js +0 -1
- package/pgOverlaySubMenu.js +0 -1
- package/pgPicker.js +0 -1
- package/pgPreview.js +0 -1
- package/pgScroll.js +0 -1
- package/pgSearch.js +0 -1
- package/pgTab.js +0 -1
- package/pgTabs.js +0 -1
- package/pgToast.js +0 -1
- package/pgToasts.js +0 -1
- package/pgTooltip.js +0 -1
- package/pgTree.js +0 -1
- package/pgTreeButtonIcon.js +0 -1
- package/pgTreeItem.js +0 -1
|
@@ -6,28 +6,38 @@ interface Edge {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
interface Options {
|
|
9
|
+
x?: number;
|
|
10
|
+
y?: number;
|
|
9
11
|
width?: number;
|
|
10
12
|
height?: number;
|
|
11
13
|
scale?: number;
|
|
12
14
|
offsetX?: number;
|
|
13
15
|
offsetY?: number;
|
|
16
|
+
include?: number[];
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export function toIndex(x: number, y: number, width: number) {
|
|
17
20
|
return y * width + x;
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Convert a 2d array to a SVG path.
|
|
25
|
+
* @param data
|
|
26
|
+
* @param options
|
|
27
|
+
* @returns path string
|
|
28
|
+
*/
|
|
20
29
|
export default function bitmaskToPath(data: number[] | number[][], options: Options = {}) {
|
|
21
30
|
|
|
22
|
-
let bitmask: number[]
|
|
31
|
+
let bitmask: number[],
|
|
23
32
|
width: number,
|
|
24
33
|
height: number,
|
|
25
34
|
scale = 1,
|
|
26
35
|
offsetX = 0,
|
|
27
|
-
offsetY = 0
|
|
36
|
+
offsetY = 0,
|
|
37
|
+
include = [1];
|
|
28
38
|
|
|
29
39
|
if (options.width) {
|
|
30
|
-
bitmask = data;
|
|
40
|
+
bitmask = data as number[]; // already flat
|
|
31
41
|
width = options.width;
|
|
32
42
|
height = bitmask.length / width;
|
|
33
43
|
if (height % 1 !== 0) {
|
|
@@ -53,6 +63,10 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
53
63
|
offsetY = options.offsetY;
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
if (options.include) {
|
|
67
|
+
include = options.include;
|
|
68
|
+
}
|
|
69
|
+
|
|
56
70
|
// Naively copy into a new bitmask with a border of 1 to make sampling easier (no out of bounds checks)
|
|
57
71
|
const newWidth = width + 2;
|
|
58
72
|
const newHeight = height + 2;
|
|
@@ -65,7 +79,7 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
65
79
|
|
|
66
80
|
for (let y = 0; y < height; ++y) {
|
|
67
81
|
for (let x = 0; x < width; ++x) {
|
|
68
|
-
bm[BMXYToIndex(x, y)] = bitmask[toIndex(x, y, width)];
|
|
82
|
+
bm[BMXYToIndex(x, y)] = include.includes(bitmask[toIndex(x, y, width)]) ? 1 : 0;
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
|
|
@@ -101,7 +115,7 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
101
115
|
|
|
102
116
|
for (let y = 0; y < height; ++y) {
|
|
103
117
|
for (let x = 0; x < width; ++x) {
|
|
104
|
-
if (bm[BMXYToIndex(x, y)]
|
|
118
|
+
if (bm[BMXYToIndex(x, y)] === 1) {
|
|
105
119
|
const left = bm[BMXYToIndex(x - 1, y)];
|
|
106
120
|
if (left == 0) {
|
|
107
121
|
const edge = edges[EdgeYIndex(x, y)];
|
|
@@ -116,7 +130,7 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
116
130
|
UnionGroup(edge);
|
|
117
131
|
}
|
|
118
132
|
const right = bm[BMXYToIndex(x + 1, y)];
|
|
119
|
-
if (right
|
|
133
|
+
if (right === 0) {
|
|
120
134
|
const edge = edges[EdgeYIndex(x + 1, y)];
|
|
121
135
|
SetEdge(edge, x + 1, y);
|
|
122
136
|
if (bm[BMXYToIndex(x + 1, y + 1)]) {
|
|
@@ -129,7 +143,7 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
129
143
|
UnionGroup(edge);
|
|
130
144
|
}
|
|
131
145
|
const top = bm[BMXYToIndex(x, y - 1)];
|
|
132
|
-
if (top
|
|
146
|
+
if (top === 0) {
|
|
133
147
|
const edge: Edge = edges[EdgeXIndex(x, y)];
|
|
134
148
|
SetEdge(edge, x, y);
|
|
135
149
|
if (bm[BMXYToIndex(x + 1, y - 1)]) {
|
|
@@ -142,7 +156,7 @@ export default function bitmaskToPath(data: number[] | number[][], options: Opti
|
|
|
142
156
|
UnionGroup(edge);
|
|
143
157
|
}
|
|
144
158
|
const bottom = bm[BMXYToIndex(x, y + 1)];
|
|
145
|
-
if (bottom
|
|
159
|
+
if (bottom === 0) {
|
|
146
160
|
const edge = edges[EdgeXIndex(x, y + 1)];
|
|
147
161
|
SetEdge(edge, x + 1, y + 1);
|
|
148
162
|
if (bm[BMXYToIndex(x - 1, y + 1)]) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function canvasToPngBuffer(canvas: HTMLCanvasElement): Promise<ArrayBuffer> {
|
|
2
|
+
const blob = await new Promise((resolve: BlobCallback) =>
|
|
3
|
+
canvas.toBlob(resolve, 'image/png')
|
|
4
|
+
);
|
|
5
|
+
|
|
6
|
+
// `canvas.toBlob` can return null, so guard it
|
|
7
|
+
if (!blob) {
|
|
8
|
+
throw new Error("Failed to convert canvas to Blob");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return await blob.arrayBuffer();
|
|
12
|
+
}
|
|
@@ -1 +1,55 @@
|
|
|
1
|
-
export const WHITE = '#FFFFFF';
|
|
1
|
+
export const WHITE = '#FFFFFF';
|
|
2
|
+
|
|
3
|
+
export type Pixel = { x: number, y: number };
|
|
4
|
+
|
|
5
|
+
export type Point = [number, number];
|
|
6
|
+
|
|
7
|
+
export enum LayerType {
|
|
8
|
+
Pixel = 'pixel',
|
|
9
|
+
Reference = 'reference',
|
|
10
|
+
Pattern = 'pattern',
|
|
11
|
+
Linear = 'linear',
|
|
12
|
+
Radial = 'radial',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* stop 0 to 1
|
|
17
|
+
* colorIndex
|
|
18
|
+
*/
|
|
19
|
+
export type GradientStop = [number, number];
|
|
20
|
+
|
|
21
|
+
interface LayerPixel {
|
|
22
|
+
type: LayerType.Pixel;
|
|
23
|
+
path: string;
|
|
24
|
+
color: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface LayerReference {
|
|
28
|
+
type: LayerType.Reference;
|
|
29
|
+
id: string;
|
|
30
|
+
position: Point;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface LayerPattern {
|
|
34
|
+
type: LayerType.Pattern;
|
|
35
|
+
id: string;
|
|
36
|
+
path: string;
|
|
37
|
+
offset: Point;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface LayerLinear {
|
|
41
|
+
type: LayerType.Linear;
|
|
42
|
+
start: Point;
|
|
43
|
+
end: Point;
|
|
44
|
+
stops: GradientStop[];
|
|
45
|
+
dither: 'bayer4' | 'bayer8' | 'bayer16';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface LayerRadial {
|
|
49
|
+
type: LayerType.Radial;
|
|
50
|
+
start: Point;
|
|
51
|
+
end: Point;
|
|
52
|
+
stops: GradientStop[];
|
|
53
|
+
transform: [number, number, number, number, number, number];
|
|
54
|
+
dither: 'bayer4' | 'bayer8' | 'bayer16';
|
|
55
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute CRC32 lookup tables as described at:
|
|
3
|
+
* https://github.com/komrad36/CRC#option-6-1-byte-tabular
|
|
4
|
+
*
|
|
5
|
+
* An iteration of CRC computation can be performed on 8 bits of input at once. By pre-computing a
|
|
6
|
+
* table of the values of CRC(?) for all 2^8 = 256 possible byte values, during the final
|
|
7
|
+
* computation we can replace a loop over 8 bits with a single lookup in the table.
|
|
8
|
+
*
|
|
9
|
+
* For further speedup, we can also pre-compute the values of CRC(?0) for all possible bytes when a
|
|
10
|
+
* zero byte is appended. Then we can process two bytes of input at once by computing CRC(AB) =
|
|
11
|
+
* CRC(A0) ^ CRC(B), using one lookup in the CRC(?0) table and one lookup in the CRC(?) table.
|
|
12
|
+
*
|
|
13
|
+
* The same technique applies for any number of bytes to be processed at once, although the speed
|
|
14
|
+
* improvements diminish.
|
|
15
|
+
*
|
|
16
|
+
* @param polynomial The binary representation of the polynomial to use (reversed, i.e. most
|
|
17
|
+
* significant bit represents x^0).
|
|
18
|
+
* @param numTables The number of bytes of input that will be processed at once.
|
|
19
|
+
*/
|
|
20
|
+
function crc32GenerateTables({
|
|
21
|
+
polynomial,
|
|
22
|
+
numTables,
|
|
23
|
+
}: {
|
|
24
|
+
polynomial: number;
|
|
25
|
+
numTables: number;
|
|
26
|
+
}): Uint32Array {
|
|
27
|
+
const table = new Uint32Array(256 * numTables);
|
|
28
|
+
for (let i = 0; i < 256; i++) {
|
|
29
|
+
let r = i;
|
|
30
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
31
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
32
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
33
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
34
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
35
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
36
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
37
|
+
r = ((r & 1) * polynomial) ^ (r >>> 1);
|
|
38
|
+
table[i] = r;
|
|
39
|
+
}
|
|
40
|
+
for (let i = 256; i < table.length; i++) {
|
|
41
|
+
const value = table[i - 256]!;
|
|
42
|
+
table[i] = table[value & 0xff]! ^ (value >>> 8);
|
|
43
|
+
}
|
|
44
|
+
return table;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const CRC32_TABLE = crc32GenerateTables({ polynomial: 0xedb88320, numTables: 8 });
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initialize a CRC32 to all 1 bits.
|
|
51
|
+
*/
|
|
52
|
+
function crc32Init(): number {
|
|
53
|
+
return ~0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Update a streaming CRC32 calculation.
|
|
58
|
+
*
|
|
59
|
+
* For performance, this implementation processes the data 8 bytes at a time, using the algorithm
|
|
60
|
+
* presented at: https://github.com/komrad36/CRC#option-9-8-byte-tabular
|
|
61
|
+
*/
|
|
62
|
+
function crc32Update(prev: number, data: ArrayBufferView): number {
|
|
63
|
+
const byteLength = data.byteLength;
|
|
64
|
+
const view = new DataView(data.buffer, data.byteOffset, byteLength);
|
|
65
|
+
let r = prev;
|
|
66
|
+
let offset = 0;
|
|
67
|
+
|
|
68
|
+
// Process bytes one by one until we reach 4-byte alignment, which will speed up uint32 access.
|
|
69
|
+
const toAlign = -view.byteOffset & 3;
|
|
70
|
+
for (; offset < toAlign && offset < byteLength; offset++) {
|
|
71
|
+
r = CRC32_TABLE[(r ^ view.getUint8(offset)) & 0xff]! ^ (r >>> 8);
|
|
72
|
+
}
|
|
73
|
+
if (offset === byteLength) {
|
|
74
|
+
return r;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
offset = toAlign;
|
|
78
|
+
|
|
79
|
+
// Process 8 bytes (2 uint32s) at a time.
|
|
80
|
+
let remainingBytes = byteLength - offset;
|
|
81
|
+
for (; remainingBytes >= 8; offset += 8, remainingBytes -= 8) {
|
|
82
|
+
r ^= view.getUint32(offset, true);
|
|
83
|
+
const r2 = view.getUint32(offset + 4, true);
|
|
84
|
+
r =
|
|
85
|
+
CRC32_TABLE[0 * 256 + ((r2 >>> 24) & 0xff)]! ^
|
|
86
|
+
CRC32_TABLE[1 * 256 + ((r2 >>> 16) & 0xff)]! ^
|
|
87
|
+
CRC32_TABLE[2 * 256 + ((r2 >>> 8) & 0xff)]! ^
|
|
88
|
+
CRC32_TABLE[3 * 256 + ((r2 >>> 0) & 0xff)]! ^
|
|
89
|
+
CRC32_TABLE[4 * 256 + ((r >>> 24) & 0xff)]! ^
|
|
90
|
+
CRC32_TABLE[5 * 256 + ((r >>> 16) & 0xff)]! ^
|
|
91
|
+
CRC32_TABLE[6 * 256 + ((r >>> 8) & 0xff)]! ^
|
|
92
|
+
CRC32_TABLE[7 * 256 + ((r >>> 0) & 0xff)]!;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Process any remaining bytes one by one. (Perf note: inexplicably, using a temporary variable
|
|
96
|
+
// `i` rather than reusing `offset` here is faster in V8.)
|
|
97
|
+
for (let i = offset; i < byteLength; i++) {
|
|
98
|
+
r = CRC32_TABLE[(r ^ view.getUint8(i)) & 0xff]! ^ (r >>> 8);
|
|
99
|
+
}
|
|
100
|
+
return r;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Finalize a CRC32 by inverting the output value. An unsigned right-shift of 0 is used to ensure the result is a positive number.
|
|
105
|
+
*/
|
|
106
|
+
function crc32Final(prev: number): number {
|
|
107
|
+
return (prev ^ ~0) >>> 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Calculate a one-shot CRC32. If the data is being accumulated incrementally, use the functions
|
|
112
|
+
* `crc32Init`, `crc32Update`, and `crc32Final` instead.
|
|
113
|
+
*/
|
|
114
|
+
export function crc32(data: ArrayBufferView): number {
|
|
115
|
+
return crc32Final(crc32Update(crc32Init(), data));
|
|
116
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function diffMap(map1, map2) {
|
|
2
|
+
// 1. Get all keys from both maps as Sets
|
|
3
|
+
const keys1 = new Set(map1.keys());
|
|
4
|
+
const keys2 = new Set(map2.keys());
|
|
5
|
+
|
|
6
|
+
// 2. Combine all unique keys into a single Set
|
|
7
|
+
const allKeys = new Set([...new Set(map1.keys()), ...new Set(map2.keys())]);
|
|
8
|
+
|
|
9
|
+
// 3. Filter the combined keys to find those not present in either original Set
|
|
10
|
+
const uniqueKeys = new Set();
|
|
11
|
+
for (const key of allKeys) {
|
|
12
|
+
if (!keys1.has(key) || !keys2.has(key)) {
|
|
13
|
+
uniqueKeys.add(key);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return uniqueKeys;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function diffLeftMapPixels(map1: Map<string, number[]>, map2: Map<string, number[]>) {
|
|
21
|
+
const leftPixels: number[][] = [];
|
|
22
|
+
|
|
23
|
+
// Iterate over all keys in the first map
|
|
24
|
+
for (const key of map1.keys()) {
|
|
25
|
+
// If the second map does not have the current key, add it to the results
|
|
26
|
+
if (!map2.has(key)) {
|
|
27
|
+
leftPixels.push(map1.get(key) as number[]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return leftPixels;
|
|
32
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Not used but included
|
|
2
|
+
const byerMatrix2x2 = [
|
|
3
|
+
[0, 2],
|
|
4
|
+
[3, 1]
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
const bayerMatrix4x4 = [
|
|
8
|
+
[0, 8, 2, 10],
|
|
9
|
+
[12, 4, 14, 6],
|
|
10
|
+
[3, 11, 1, 9],
|
|
11
|
+
[15, 7, 13, 5]
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const bayerMatrix8x8 = [
|
|
15
|
+
[0, 32, 8, 40, 2, 34, 10, 42],
|
|
16
|
+
[48, 16, 56, 24, 50, 18, 58, 26],
|
|
17
|
+
[12, 44, 4, 36, 14, 46, 6, 38],
|
|
18
|
+
[60, 28, 52, 20, 62, 30, 54, 22],
|
|
19
|
+
[3, 35, 11, 43, 1, 33, 9, 41],
|
|
20
|
+
[51, 19, 59, 27, 49, 17, 57, 25],
|
|
21
|
+
[15, 47, 7, 39, 13, 45, 5, 37],
|
|
22
|
+
[63, 31, 55, 23, 61, 29, 53, 21]
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const bayerMatrix16x16 = [
|
|
26
|
+
[0, 128, 32, 160, 8, 136, 40, 168, 2, 130, 34, 162, 10, 138, 42, 170],
|
|
27
|
+
[192, 64, 224, 96, 200, 72, 232, 104, 194, 66, 226, 98, 202, 74, 234, 106],
|
|
28
|
+
[48, 176, 16, 144, 56, 184, 24, 152, 50, 178, 18, 146, 58, 186, 26, 154],
|
|
29
|
+
[240, 112, 208, 80, 248, 120, 216, 88, 242, 114, 210, 82, 250, 122, 218, 90],
|
|
30
|
+
[12, 140, 44, 172, 4, 132, 36, 164, 14, 142, 46, 174, 6, 134, 38, 166],
|
|
31
|
+
[204, 76, 236, 108, 196, 68, 228, 100, 206, 78, 238, 110, 198, 70, 230, 102],
|
|
32
|
+
[60, 188, 28, 156, 52, 180, 20, 148, 62, 190, 30, 158, 54, 182, 22, 150],
|
|
33
|
+
[252, 124, 220, 92, 244, 116, 212, 84, 254, 126, 222, 94, 246, 118, 214, 86],
|
|
34
|
+
[3, 131, 35, 163, 11, 139, 43, 171, 1, 129, 33, 161, 9, 137, 41, 169],
|
|
35
|
+
[195, 67, 227, 99, 203, 75, 235, 107, 193, 65, 225, 97, 201, 73, 233, 105],
|
|
36
|
+
[51, 179, 19, 147, 59, 187, 27, 155, 49, 177, 17, 145, 57, 185, 25, 153],
|
|
37
|
+
[243, 115, 211, 83, 251, 123, 219, 91, 241, 113, 209, 81, 249, 121, 217, 89],
|
|
38
|
+
[15, 143, 47, 175, 7, 135, 39, 167, 13, 141, 45, 173, 5, 133, 37, 165],
|
|
39
|
+
[207, 79, 239, 111, 199, 71, 231, 103, 205, 77, 237, 109, 197, 69, 229, 101],
|
|
40
|
+
[63, 191, 31, 159, 55, 183, 23, 151, 61, 189, 29, 157, 53, 181, 21, 149],
|
|
41
|
+
[255, 127, 223, 95, 247, 119, 215, 87, 253, 125, 221, 93, 245, 117, 213, 85]
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// Normalize the Bayer matrix values to a 0-1 range
|
|
45
|
+
const normalizeBayerMatrix = (matrix) => {
|
|
46
|
+
const maxVal = matrix.flat().reduce((max, val) => Math.max(max, val), 0);
|
|
47
|
+
return matrix.map(row => row.map(val => val / (maxVal + 1))); // +1 to ensure values are strictly less than 1
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const normalizedBayerMatrix = normalizeBayerMatrix(bayerMatrix4x4);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Applies Bayer dithering to an image data array between two specified colors.
|
|
54
|
+
* @param imageData The ImageData object containing pixel data.
|
|
55
|
+
* @param color1 The first color (e.g., [r, g, b]).
|
|
56
|
+
* @param color2 The second color (e.g., [r, g, b]).
|
|
57
|
+
* @returns The dithered ImageData object.
|
|
58
|
+
*/
|
|
59
|
+
function applyBayerDithering(
|
|
60
|
+
imageData,
|
|
61
|
+
color1,
|
|
62
|
+
color2
|
|
63
|
+
) {
|
|
64
|
+
const { data, width, height } = imageData;
|
|
65
|
+
const matrixSize = normalizedBayerMatrix.length;
|
|
66
|
+
|
|
67
|
+
for (let y = 0; y < height; y++) {
|
|
68
|
+
for (let x = 0; x < width; x++) {
|
|
69
|
+
const i = (y * width + x) * 4; // Index for R component
|
|
70
|
+
|
|
71
|
+
// Calculate grayscale intensity (luminance) of the original pixel
|
|
72
|
+
const r = data[i];
|
|
73
|
+
const g = data[i + 1];
|
|
74
|
+
const b = data[i + 2];
|
|
75
|
+
const intensity = (0.299 * r + 0.587 * g + 0.114 * b) / 255; // Normalize to 0-1
|
|
76
|
+
|
|
77
|
+
// Get the threshold from the normalized Bayer matrix
|
|
78
|
+
const threshold = normalizedBayerMatrix[y % matrixSize][x % matrixSize];
|
|
79
|
+
|
|
80
|
+
// Assign color based on intensity and threshold
|
|
81
|
+
if (intensity > threshold) {
|
|
82
|
+
data[i] = color2[0];
|
|
83
|
+
data[i + 1] = color2[1];
|
|
84
|
+
data[i + 2] = color2[2];
|
|
85
|
+
} else {
|
|
86
|
+
data[i] = color1[0];
|
|
87
|
+
data[i + 1] = color1[1];
|
|
88
|
+
data[i + 2] = color1[2];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return imageData;
|
|
93
|
+
}
|
|
94
|
+
/*
|
|
95
|
+
// Example usage (assuming you have an ImageData object from a canvas)
|
|
96
|
+
const canvas = document.querySelector('canvas');
|
|
97
|
+
const ctx = canvas.getContext('2d');
|
|
98
|
+
|
|
99
|
+
const linearGradient = ctx.createLinearGradient(0, 0, canvas.width, 0); // x0, y0, x1, y1
|
|
100
|
+
|
|
101
|
+
// Add color stops
|
|
102
|
+
linearGradient.addColorStop(0, 'white'); // Start color at 0%
|
|
103
|
+
linearGradient.addColorStop(1, 'black'); // End color at 100%
|
|
104
|
+
|
|
105
|
+
// Apply the gradient to fillStyle and draw a rectangle
|
|
106
|
+
ctx.fillStyle = linearGradient;
|
|
107
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
108
|
+
|
|
109
|
+
const originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
110
|
+
const ditheredImageData = applyBayerDithering(originalImageData, [0, 0, 0], [255, 255, 255]);
|
|
111
|
+
ctx.putImageData(ditheredImageData, 0, 0);
|
|
112
|
+
*/
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a coordinate in the 2D array.
|
|
3
|
+
*/
|
|
4
|
+
type Coordinate = [number, number];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Flood fills a 2D array starting from a specific coordinate to find all connected numbers
|
|
8
|
+
* that are within a specified set of target values.
|
|
9
|
+
*
|
|
10
|
+
* @param grid The 2D array (grid) of numbers.
|
|
11
|
+
* @param startX The starting x-coordinate.
|
|
12
|
+
* @param startY The starting y-coordinate.
|
|
13
|
+
* @param targetValues The set of numbers to search for (e.g., [1, 2, 5]).
|
|
14
|
+
* @returns A list of [x, y] coordinates that were found during the flood fill.
|
|
15
|
+
*/
|
|
16
|
+
export function getFloodFill(
|
|
17
|
+
grid: number[][],
|
|
18
|
+
startX: number,
|
|
19
|
+
startY: number,
|
|
20
|
+
targetValues: number[]
|
|
21
|
+
): Coordinate[] {
|
|
22
|
+
// Use a Set for quick lookups of target values
|
|
23
|
+
const targets = new Set(targetValues);
|
|
24
|
+
|
|
25
|
+
// Check if starting coordinates are valid and if the value at the start is a target
|
|
26
|
+
if (
|
|
27
|
+
startY < 0 ||
|
|
28
|
+
startY >= grid.length ||
|
|
29
|
+
startX < 0 ||
|
|
30
|
+
startX >= grid[0].length ||
|
|
31
|
+
!targets.has(grid[startY][startX])
|
|
32
|
+
) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Queue for Breadth-First Search (BFS)
|
|
37
|
+
const queue: Coordinate[] = [[startX, startY]];
|
|
38
|
+
// Set to keep track of visited coordinates to avoid cycles and redundant processing
|
|
39
|
+
const visited = new Set<string>();
|
|
40
|
+
const foundPixels: Coordinate[] = [];
|
|
41
|
+
|
|
42
|
+
// Helper to convert [x, y] to a string key for the 'visited' set
|
|
43
|
+
const toKey = (x: number, y: number) => `${x},${y}`;
|
|
44
|
+
|
|
45
|
+
// Mark the starting pixel as visited and add to found list
|
|
46
|
+
visited.add(toKey(startX, startY));
|
|
47
|
+
foundPixels.push([startX, startY]);
|
|
48
|
+
|
|
49
|
+
while (queue.length > 0) {
|
|
50
|
+
const [currentX, currentY] = queue.shift()!;
|
|
51
|
+
const currentValue = grid[currentY][currentX];
|
|
52
|
+
|
|
53
|
+
// Define potential neighbors (up, down, left, right)
|
|
54
|
+
const neighbors: Coordinate[] = [
|
|
55
|
+
[currentX + 1, currentY],
|
|
56
|
+
[currentX - 1, currentY],
|
|
57
|
+
[currentX, currentY + 1],
|
|
58
|
+
[currentX, currentY - 1],
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const [nextX, nextY] of neighbors) {
|
|
62
|
+
// Check bounds
|
|
63
|
+
if (
|
|
64
|
+
nextY >= 0 &&
|
|
65
|
+
nextY < grid.length &&
|
|
66
|
+
nextX >= 0 &&
|
|
67
|
+
nextX < grid[0].length
|
|
68
|
+
) {
|
|
69
|
+
const neighborValue = grid[nextY][nextX];
|
|
70
|
+
const key = toKey(nextX, nextY);
|
|
71
|
+
|
|
72
|
+
// If the neighbor has a target value and has not been visited yet
|
|
73
|
+
if (targets.has(neighborValue) && !visited.has(key)) {
|
|
74
|
+
visited.add(key);
|
|
75
|
+
foundPixels.push([nextX, nextY]);
|
|
76
|
+
queue.push([nextX, nextY]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return foundPixels;
|
|
83
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function getGridColorIndexes(arr: number[][]) {
|
|
2
|
+
const seen = new Set([0]);
|
|
3
|
+
const result: number[] = [];
|
|
4
|
+
for (const innerArray of arr) {
|
|
5
|
+
for (const value of innerArray) {
|
|
6
|
+
if (!seen.has(value)) {
|
|
7
|
+
seen.add(value);
|
|
8
|
+
result.push(value);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export function getOutline(pixels: number[][], ignoreInside: boolean = false, include: number[] = []) {
|
|
2
|
+
const moatPixels: number[][] = [];
|
|
3
|
+
const height = pixels.length;
|
|
4
|
+
if (height === 0) return moatPixels;
|
|
5
|
+
const width = pixels[0].length;
|
|
6
|
+
|
|
7
|
+
const directions = [
|
|
8
|
+
[0, 1],
|
|
9
|
+
[0, -1],
|
|
10
|
+
[1, 0],
|
|
11
|
+
[-1, 0]
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------
|
|
15
|
+
// STEP 1: If ignoring inside, flood-fill from the outside
|
|
16
|
+
// ---------------------------------------------------------
|
|
17
|
+
let outsideZero: boolean[][] = Array.from({ length: height }, () =>
|
|
18
|
+
Array(width).fill(false)
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
if (ignoreInside) {
|
|
22
|
+
const queue: [number, number][] = [];
|
|
23
|
+
|
|
24
|
+
// Seed flood-fill with all border zeros
|
|
25
|
+
for (let x = 0; x < width; x++) {
|
|
26
|
+
if (pixels[0][x] === 0) queue.push([x, 0]);
|
|
27
|
+
if (pixels[height - 1][x] === 0) queue.push([x, height - 1]);
|
|
28
|
+
}
|
|
29
|
+
for (let y = 0; y < height; y++) {
|
|
30
|
+
if (pixels[y][0] === 0) queue.push([0, y]);
|
|
31
|
+
if (pixels[y][width - 1] === 0) queue.push([width - 1, y]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// BFS flood-fill
|
|
35
|
+
while (queue.length > 0) {
|
|
36
|
+
const [x, y] = queue.shift()!;
|
|
37
|
+
if (outsideZero[y][x]) continue;
|
|
38
|
+
outsideZero[y][x] = true;
|
|
39
|
+
|
|
40
|
+
for (const [dx, dy] of directions) {
|
|
41
|
+
const nx = x + dx;
|
|
42
|
+
const ny = y + dy;
|
|
43
|
+
if (
|
|
44
|
+
nx >= 0 &&
|
|
45
|
+
nx < width &&
|
|
46
|
+
ny >= 0 &&
|
|
47
|
+
ny < height &&
|
|
48
|
+
pixels[ny][nx] === 0 &&
|
|
49
|
+
!outsideZero[ny][nx]
|
|
50
|
+
) {
|
|
51
|
+
queue.push([nx, ny]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------
|
|
58
|
+
// STEP 2: Collect moat pixels
|
|
59
|
+
// ---------------------------------------------------------
|
|
60
|
+
const moatSet = new Set<string>();
|
|
61
|
+
|
|
62
|
+
let condition = (pixel) => pixel === 0;
|
|
63
|
+
if (include.length !== 0) {
|
|
64
|
+
condition = (pixel) => !include.includes(pixel);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (let y = 0; y < height; y++) {
|
|
68
|
+
for (let x = 0; x < width; x++) {
|
|
69
|
+
if (condition(pixels[y][x])) continue;
|
|
70
|
+
|
|
71
|
+
for (const [dx, dy] of directions) {
|
|
72
|
+
const nx = x + dx;
|
|
73
|
+
const ny = y + dy;
|
|
74
|
+
|
|
75
|
+
if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue;
|
|
76
|
+
|
|
77
|
+
if (pixels[ny][nx] === 0) {
|
|
78
|
+
// If ignoring inside, only count zeros reachable from outside
|
|
79
|
+
if (ignoreInside && !outsideZero[ny][nx]) continue;
|
|
80
|
+
|
|
81
|
+
const key = `${nx},${ny}`;
|
|
82
|
+
if (!moatSet.has(key)) {
|
|
83
|
+
moatSet.add(key);
|
|
84
|
+
moatPixels.push([nx, ny]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return moatPixels;
|
|
92
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type Pixel = { x: number, y: number };
|
|
2
|
+
|
|
3
|
+
export const pixelSizes: { [key: number]: number[][] } = {
|
|
4
|
+
1: [
|
|
5
|
+
[0, 0]
|
|
6
|
+
],
|
|
7
|
+
3: [
|
|
8
|
+
[0, -1],
|
|
9
|
+
[-1, 0],
|
|
10
|
+
[0, 0],
|
|
11
|
+
[1, 0],
|
|
12
|
+
[0, 1]
|
|
13
|
+
],
|
|
14
|
+
5: [
|
|
15
|
+
[0, -2],
|
|
16
|
+
[-1, -1],
|
|
17
|
+
[0, -1],
|
|
18
|
+
[1, -1],
|
|
19
|
+
[-2, 0],
|
|
20
|
+
[-1, 0],
|
|
21
|
+
[0, 0],
|
|
22
|
+
[1, 0],
|
|
23
|
+
[2, 0],
|
|
24
|
+
[-1, 1],
|
|
25
|
+
[0, 1],
|
|
26
|
+
[1, 1],
|
|
27
|
+
[0, 2],
|
|
28
|
+
],
|
|
29
|
+
7: [
|
|
30
|
+
[-1, -3],
|
|
31
|
+
[0, -3],
|
|
32
|
+
[1, -3],
|
|
33
|
+
[-2, -2],
|
|
34
|
+
[-1, -1],
|
|
35
|
+
[0, -1],
|
|
36
|
+
[1, -1],
|
|
37
|
+
[-2, 0],
|
|
38
|
+
[-1, 0],
|
|
39
|
+
[0, 0],
|
|
40
|
+
[1, 0],
|
|
41
|
+
[2, 0],
|
|
42
|
+
[-1, 1],
|
|
43
|
+
[0, 1],
|
|
44
|
+
[1, 1],
|
|
45
|
+
[0, 2],
|
|
46
|
+
]
|
|
47
|
+
}
|