@tldraw/tlschema 4.2.2 → 4.2.3
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/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
- package/dist-cjs/createTLSchema.js.map +2 -2
- package/dist-cjs/index.d.ts +71 -242
- package/dist-cjs/index.js +1 -4
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/misc/TLOpacity.js +5 -1
- package/dist-cjs/misc/TLOpacity.js.map +2 -2
- package/dist-cjs/misc/TLRichText.js +1 -5
- package/dist-cjs/misc/TLRichText.js.map +2 -2
- package/dist-cjs/records/TLAsset.js.map +1 -1
- package/dist-cjs/records/TLBinding.js.map +2 -2
- package/dist-cjs/records/TLShape.js.map +2 -2
- package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
- package/dist-cjs/shapes/TLArrowShape.js +13 -26
- package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
- package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
- package/dist-cjs/shapes/TLDrawShape.js +4 -37
- package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
- package/dist-cjs/shapes/TLEmbedShape.js +0 -17
- package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
- package/dist-cjs/shapes/TLGeoShape.js +1 -12
- package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
- package/dist-cjs/shapes/TLHighlightShape.js +2 -29
- package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
- package/dist-cjs/shapes/TLNoteShape.js +1 -12
- package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
- package/dist-cjs/shapes/TLTextShape.js +1 -12
- package/dist-cjs/shapes/TLTextShape.js.map +2 -2
- package/dist-cjs/store-migrations.js +15 -15
- package/dist-cjs/store-migrations.js.map +2 -2
- package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
- package/dist-esm/createTLSchema.mjs.map +2 -2
- package/dist-esm/index.d.mts +71 -242
- package/dist-esm/index.mjs +1 -5
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/misc/TLOpacity.mjs +5 -1
- package/dist-esm/misc/TLOpacity.mjs.map +2 -2
- package/dist-esm/misc/TLRichText.mjs +1 -5
- package/dist-esm/misc/TLRichText.mjs.map +2 -2
- package/dist-esm/records/TLAsset.mjs.map +1 -1
- package/dist-esm/records/TLBinding.mjs.map +2 -2
- package/dist-esm/records/TLShape.mjs.map +2 -2
- package/dist-esm/shapes/TLArrowShape.mjs +13 -26
- package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
- package/dist-esm/shapes/TLDrawShape.mjs +4 -37
- package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
- package/dist-esm/shapes/TLEmbedShape.mjs +0 -17
- package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGeoShape.mjs +1 -12
- package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
- package/dist-esm/shapes/TLHighlightShape.mjs +2 -29
- package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
- package/dist-esm/shapes/TLNoteShape.mjs +1 -12
- package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
- package/dist-esm/shapes/TLTextShape.mjs +1 -12
- package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
- package/dist-esm/store-migrations.mjs +15 -15
- package/dist-esm/store-migrations.mjs.map +2 -2
- package/package.json +8 -8
- package/src/__tests__/migrationTestUtils.ts +3 -9
- package/src/assets/TLBookmarkAsset.test.ts +96 -0
- package/src/assets/TLImageAsset.test.ts +213 -0
- package/src/assets/TLVideoAsset.test.ts +105 -0
- package/src/bindings/TLArrowBinding.test.ts +55 -0
- package/src/bindings/TLBaseBinding.ts +14 -25
- package/src/createTLSchema.ts +2 -8
- package/src/index.ts +0 -9
- package/src/migrations.test.ts +1 -149
- package/src/misc/TLOpacity.ts +5 -1
- package/src/misc/TLRichText.ts +1 -6
- package/src/misc/id-validator.test.ts +50 -0
- package/src/records/TLAsset.test.ts +234 -0
- package/src/records/TLAsset.ts +2 -2
- package/src/records/TLBinding.test.ts +22 -0
- package/src/records/TLBinding.ts +23 -65
- package/src/records/TLCamera.test.ts +19 -0
- package/src/records/TLDocument.test.ts +35 -0
- package/src/records/TLInstance.test.ts +201 -0
- package/src/records/TLPage.test.ts +110 -0
- package/src/records/TLPageState.test.ts +228 -0
- package/src/records/TLPointer.test.ts +63 -0
- package/src/records/TLPresence.test.ts +190 -0
- package/src/records/TLRecord.test.ts +70 -0
- package/src/records/TLShape.test.ts +232 -0
- package/src/records/TLShape.ts +5 -100
- package/src/shapes/ShapeWithCrop.test.ts +18 -0
- package/src/shapes/ShapeWithCrop.ts +2 -2
- package/src/shapes/TLArrowShape.test.ts +505 -0
- package/src/shapes/TLArrowShape.ts +14 -28
- package/src/shapes/TLBaseShape.test.ts +142 -0
- package/src/shapes/TLBaseShape.ts +10 -34
- package/src/shapes/TLBookmarkShape.test.ts +122 -0
- package/src/shapes/TLDrawShape.test.ts +177 -0
- package/src/shapes/TLDrawShape.ts +12 -59
- package/src/shapes/TLEmbedShape.test.ts +286 -0
- package/src/shapes/TLEmbedShape.ts +0 -17
- package/src/shapes/TLFrameShape.test.ts +71 -0
- package/src/shapes/TLGeoShape.test.ts +247 -0
- package/src/shapes/TLGeoShape.ts +1 -14
- package/src/shapes/TLGroupShape.test.ts +59 -0
- package/src/shapes/TLHighlightShape.test.ts +325 -0
- package/src/shapes/TLHighlightShape.ts +0 -37
- package/src/shapes/TLImageShape.test.ts +534 -0
- package/src/shapes/TLLineShape.test.ts +269 -0
- package/src/shapes/TLNoteShape.test.ts +1568 -0
- package/src/shapes/TLNoteShape.ts +1 -15
- package/src/shapes/TLTextShape.test.ts +407 -0
- package/src/shapes/TLTextShape.ts +2 -16
- package/src/shapes/TLVideoShape.test.ts +112 -0
- package/src/store-migrations.ts +16 -17
- package/src/styles/TLColorStyle.test.ts +439 -0
- package/dist-cjs/misc/b64Vecs.js +0 -224
- package/dist-cjs/misc/b64Vecs.js.map +0 -7
- package/dist-esm/misc/b64Vecs.mjs +0 -204
- package/dist-esm/misc/b64Vecs.mjs.map +0 -7
- package/src/misc/b64Vecs.ts +0 -308
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/misc/b64Vecs.ts"],
|
|
4
|
-
"sourcesContent": ["import { VecModel } from './geometry-types'\n\n// Each point = 3 Float16s = 6 bytes = 8 base64 chars\nconst POINT_B64_LENGTH = 8\n\n// O(1) lookup table for base64 decoding (maps char code -> 6-bit value)\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nconst B64_LOOKUP = new Uint8Array(128)\nfor (let i = 0; i < 64; i++) {\n\tB64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i\n}\n\n// Precomputed powers of 2 for Float16 exponents (exp - 15, so indices 0-30 map to 2^-15 to 2^15)\nconst POW2 = new Float64Array(31)\nfor (let i = 0; i < 31; i++) {\n\tPOW2[i] = Math.pow(2, i - 15)\n}\nconst POW2_SUBNORMAL = Math.pow(2, -14) / 1024 // For subnormal numbers\n\n// Precomputed mantissa values: 1 + frac/1024 for all 1024 possible frac values\n// Avoids division in hot path\nconst MANTISSA = new Float64Array(1024)\nfor (let i = 0; i < 1024; i++) {\n\tMANTISSA[i] = 1 + i / 1024\n}\n\n/**\n * Convert a Uint16Array (containing Float16 bits) to base64.\n * Processes bytes in groups of 3 to produce 4 base64 characters.\n *\n * @internal\n */\nfunction uint16ArrayToBase64(uint16Array: Uint16Array): string {\n\tconst uint8Array = new Uint8Array(\n\t\tuint16Array.buffer,\n\t\tuint16Array.byteOffset,\n\t\tuint16Array.byteLength\n\t)\n\tlet result = ''\n\n\t// Process bytes in groups of 3 -> 4 base64 chars\n\tfor (let i = 0; i < uint8Array.length; i += 3) {\n\t\tconst byte1 = uint8Array[i]\n\t\tconst byte2 = uint8Array[i + 1] // Always exists for our use case (multiple of 6 bytes)\n\t\tconst byte3 = uint8Array[i + 2]\n\n\t\tconst bitmap = (byte1 << 16) | (byte2 << 8) | byte3\n\t\tresult +=\n\t\t\tBASE64_CHARS[(bitmap >> 18) & 63] +\n\t\t\tBASE64_CHARS[(bitmap >> 12) & 63] +\n\t\t\tBASE64_CHARS[(bitmap >> 6) & 63] +\n\t\t\tBASE64_CHARS[bitmap & 63]\n\t}\n\n\treturn result\n}\n\n/**\n * Convert a base64 string to Uint16Array containing Float16 bits.\n * The base64 string must have a length that is a multiple of 4.\n *\n * @param base64 - The base64-encoded string to decode\n * @returns A Uint16Array containing the decoded Float16 bit values\n * @public\n */\nfunction base64ToUint16Array(base64: string): Uint16Array {\n\t// Calculate exact number of bytes (4 base64 chars = 3 bytes)\n\tconst numBytes = Math.floor((base64.length * 3) / 4)\n\tconst bytes = new Uint8Array(numBytes)\n\tlet byteIndex = 0\n\n\t// Process in groups of 4 base64 characters\n\tfor (let i = 0; i < base64.length; i += 4) {\n\t\tconst c0 = B64_LOOKUP[base64.charCodeAt(i)]\n\t\tconst c1 = B64_LOOKUP[base64.charCodeAt(i + 1)]\n\t\tconst c2 = B64_LOOKUP[base64.charCodeAt(i + 2)]\n\t\tconst c3 = B64_LOOKUP[base64.charCodeAt(i + 3)]\n\n\t\tconst bitmap = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3\n\n\t\tbytes[byteIndex++] = (bitmap >> 16) & 255\n\t\tbytes[byteIndex++] = (bitmap >> 8) & 255\n\t\tbytes[byteIndex++] = bitmap & 255\n\t}\n\n\treturn new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2)\n}\n\n/**\n * Convert Float16 bits to a number using optimized lookup tables.\n * Handles normal numbers, subnormal numbers, zero, infinity, and NaN.\n *\n * @param bits - The 16-bit Float16 value to decode\n * @returns The decoded number value\n */\nfunction float16BitsToNumber(bits: number): number {\n\tconst sign = bits >> 15\n\tconst exp = (bits >> 10) & 0x1f\n\tconst frac = bits & 0x3ff\n\n\tif (exp === 0) {\n\t\t// Subnormal or zero - rare case\n\t\treturn sign ? -frac * POW2_SUBNORMAL : frac * POW2_SUBNORMAL\n\t}\n\tif (exp === 31) {\n\t\t// Infinity or NaN - very rare\n\t\treturn frac ? NaN : sign ? -Infinity : Infinity\n\t}\n\t// Normal case - two table lookups, one multiply, no division\n\tconst magnitude = POW2[exp] * MANTISSA[frac]\n\treturn sign ? -magnitude : magnitude\n}\n\n/**\n * Convert a number to Float16 bits.\n * Handles normal numbers, subnormal numbers, zero, infinity, and NaN.\n *\n * @param value - The number to encode as Float16\n * @returns The 16-bit Float16 representation of the number\n * @internal\n */\nfunction numberToFloat16Bits(value: number): number {\n\tif (value === 0) return Object.is(value, -0) ? 0x8000 : 0\n\tif (!Number.isFinite(value)) {\n\t\tif (Number.isNaN(value)) return 0x7e00\n\t\treturn value > 0 ? 0x7c00 : 0xfc00\n\t}\n\n\tconst sign = value < 0 ? 1 : 0\n\tvalue = Math.abs(value)\n\n\t// Find exponent and mantissa\n\tconst exp = Math.floor(Math.log2(value))\n\tlet expBiased = exp + 15\n\n\tif (expBiased >= 31) {\n\t\t// Overflow to infinity\n\t\treturn (sign << 15) | 0x7c00\n\t}\n\tif (expBiased <= 0) {\n\t\t// Subnormal or underflow\n\t\tconst frac = Math.round(value * Math.pow(2, 14) * 1024)\n\t\treturn (sign << 15) | (frac & 0x3ff)\n\t}\n\n\t// Normal number\n\tconst mantissa = value / Math.pow(2, exp) - 1\n\tlet frac = Math.round(mantissa * 1024)\n\n\t// Handle rounding overflow: if frac rounds to 1024, increment exponent\n\tif (frac >= 1024) {\n\t\tfrac = 0\n\t\texpBiased++\n\t\tif (expBiased >= 31) {\n\t\t\t// Overflow to infinity\n\t\t\treturn (sign << 15) | 0x7c00\n\t\t}\n\t}\n\n\treturn (sign << 15) | (expBiased << 10) | frac\n}\n\n/**\n * Utilities for encoding and decoding points using base64 and Float16 encoding.\n * Provides functions for converting between VecModel arrays and compact base64 strings,\n * as well as individual point encoding/decoding operations.\n *\n * @public\n */\nexport class b64Vecs {\n\t/**\n\t * Encode a single point (x, y, z) to 8 base64 characters.\n\t * Each coordinate is encoded as a Float16 value, resulting in 6 bytes total.\n\t *\n\t * @param x - The x coordinate\n\t * @param y - The y coordinate\n\t * @param z - The z coordinate\n\t * @returns An 8-character base64 string representing the point\n\t */\n\tstatic encodePoint(x: number, y: number, z: number): string {\n\t\tconst xBits = numberToFloat16Bits(x)\n\t\tconst yBits = numberToFloat16Bits(y)\n\t\tconst zBits = numberToFloat16Bits(z)\n\n\t\t// Convert Float16 bits to 6 bytes (little-endian)\n\t\tconst b0 = xBits & 0xff\n\t\tconst b1 = (xBits >> 8) & 0xff\n\t\tconst b2 = yBits & 0xff\n\t\tconst b3 = (yBits >> 8) & 0xff\n\t\tconst b4 = zBits & 0xff\n\t\tconst b5 = (zBits >> 8) & 0xff\n\n\t\t// Convert 6 bytes to 8 base64 chars\n\t\tconst bitmap1 = (b0 << 16) | (b1 << 8) | b2\n\t\tconst bitmap2 = (b3 << 16) | (b4 << 8) | b5\n\n\t\treturn (\n\t\t\tBASE64_CHARS[(bitmap1 >> 18) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap1 >> 12) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap1 >> 6) & 0x3f] +\n\t\t\tBASE64_CHARS[bitmap1 & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap2 >> 18) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap2 >> 12) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap2 >> 6) & 0x3f] +\n\t\t\tBASE64_CHARS[bitmap2 & 0x3f]\n\t\t)\n\t}\n\n\t/**\n\t * Convert an array of VecModels to a base64 string for compact storage.\n\t * Uses Float16 encoding for each coordinate (x, y, z). If a point's z value is\n\t * undefined, it defaults to 0.5.\n\t *\n\t * @param points - An array of VecModel objects to encode\n\t * @returns A base64-encoded string containing all points\n\t */\n\tstatic encodePoints(points: VecModel[]): string {\n\t\tconst uint16s = new Uint16Array(points.length * 3)\n\t\tfor (let i = 0; i < points.length; i++) {\n\t\t\tconst p = points[i]\n\t\t\tuint16s[i * 3] = numberToFloat16Bits(p.x)\n\t\t\tuint16s[i * 3 + 1] = numberToFloat16Bits(p.y)\n\t\t\tuint16s[i * 3 + 2] = numberToFloat16Bits(p.z ?? 0.5)\n\t\t}\n\t\treturn uint16ArrayToBase64(uint16s)\n\t}\n\n\t/**\n\t * Convert a base64 string back to an array of VecModels.\n\t * Decodes Float16-encoded coordinates (x, y, z) from the base64 string.\n\t *\n\t * @param base64 - The base64-encoded string containing point data\n\t * @returns An array of VecModel objects decoded from the string\n\t */\n\tstatic decodePoints(base64: string): VecModel[] {\n\t\tconst uint16s = base64ToUint16Array(base64)\n\t\tconst result: VecModel[] = []\n\t\tfor (let i = 0; i < uint16s.length; i += 3) {\n\t\t\tresult.push({\n\t\t\t\tx: float16BitsToNumber(uint16s[i]),\n\t\t\t\ty: float16BitsToNumber(uint16s[i + 1]),\n\t\t\t\tz: float16BitsToNumber(uint16s[i + 2]),\n\t\t\t})\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Decode a single point (8 base64 chars) starting at the given offset.\n\t * Each point is encoded as 3 Float16 values (x, y, z) in 8 base64 characters.\n\t *\n\t * @param b64Points - The base64-encoded string containing point data\n\t * @param charOffset - The character offset where the point starts (must be a multiple of 8)\n\t * @returns A VecModel object with x, y, and z coordinates\n\t * @internal\n\t */\n\tstatic decodePointAt(b64Points: string, charOffset: number): VecModel {\n\t\t// Decode 8 base64 chars -> 6 bytes -> 3 Float16s using O(1) lookup\n\t\tconst c0 = B64_LOOKUP[b64Points.charCodeAt(charOffset)]\n\t\tconst c1 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 1)]\n\t\tconst c2 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 2)]\n\t\tconst c3 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 3)]\n\t\tconst c4 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 4)]\n\t\tconst c5 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 5)]\n\t\tconst c6 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 6)]\n\t\tconst c7 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 7)]\n\n\t\t// 4 base64 chars -> 24 bits -> 3 bytes\n\t\tconst bitmap1 = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3\n\t\tconst bitmap2 = (c4 << 18) | (c5 << 12) | (c6 << 6) | c7\n\n\t\t// Extract Float16 bits directly (little-endian byte order)\n\t\t// bitmap1 = [byte0:8][byte1:8][byte2:8], bitmap2 = [byte3:8][byte4:8][byte5:8]\n\t\t// xBits = byte0 | (byte1 << 8), yBits = byte2 | (byte3 << 8), zBits = byte4 | (byte5 << 8)\n\t\tconst xBits = ((bitmap1 >> 16) & 0xff) | (bitmap1 & 0xff00)\n\t\tconst yBits = (bitmap1 & 0xff) | ((bitmap2 >> 8) & 0xff00)\n\t\tconst zBits = ((bitmap2 >> 8) & 0xff) | ((bitmap2 << 8) & 0xff00)\n\n\t\treturn {\n\t\t\tx: float16BitsToNumber(xBits),\n\t\t\ty: float16BitsToNumber(yBits),\n\t\t\tz: float16BitsToNumber(zBits),\n\t\t}\n\t}\n\n\t/**\n\t * Get the first point from a base64-encoded string of points.\n\t *\n\t * @param b64Points - The base64-encoded string containing point data\n\t * @returns The first point as a VecModel, or null if the string is too short\n\t * @public\n\t */\n\tstatic decodeFirstPoint(b64Points: string): VecModel | null {\n\t\tif (b64Points.length < POINT_B64_LENGTH) return null\n\t\treturn b64Vecs.decodePointAt(b64Points, 0)\n\t}\n\n\t/**\n\t * Get the last point from a base64-encoded string of points.\n\t *\n\t * @param b64Points - The base64-encoded string containing point data\n\t * @returns The last point as a VecModel, or null if the string is too short\n\t */\n\tstatic decodeLastPoint(b64Points: string): VecModel | null {\n\t\tif (b64Points.length < POINT_B64_LENGTH) return null\n\t\treturn b64Vecs.decodePointAt(b64Points, b64Points.length - POINT_B64_LENGTH)\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["frac"]
|
|
7
|
-
}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
const POINT_B64_LENGTH = 8;
|
|
2
|
-
const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3
|
-
const B64_LOOKUP = new Uint8Array(128);
|
|
4
|
-
for (let i = 0; i < 64; i++) {
|
|
5
|
-
B64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;
|
|
6
|
-
}
|
|
7
|
-
const POW2 = new Float64Array(31);
|
|
8
|
-
for (let i = 0; i < 31; i++) {
|
|
9
|
-
POW2[i] = Math.pow(2, i - 15);
|
|
10
|
-
}
|
|
11
|
-
const POW2_SUBNORMAL = Math.pow(2, -14) / 1024;
|
|
12
|
-
const MANTISSA = new Float64Array(1024);
|
|
13
|
-
for (let i = 0; i < 1024; i++) {
|
|
14
|
-
MANTISSA[i] = 1 + i / 1024;
|
|
15
|
-
}
|
|
16
|
-
function uint16ArrayToBase64(uint16Array) {
|
|
17
|
-
const uint8Array = new Uint8Array(
|
|
18
|
-
uint16Array.buffer,
|
|
19
|
-
uint16Array.byteOffset,
|
|
20
|
-
uint16Array.byteLength
|
|
21
|
-
);
|
|
22
|
-
let result = "";
|
|
23
|
-
for (let i = 0; i < uint8Array.length; i += 3) {
|
|
24
|
-
const byte1 = uint8Array[i];
|
|
25
|
-
const byte2 = uint8Array[i + 1];
|
|
26
|
-
const byte3 = uint8Array[i + 2];
|
|
27
|
-
const bitmap = byte1 << 16 | byte2 << 8 | byte3;
|
|
28
|
-
result += BASE64_CHARS[bitmap >> 18 & 63] + BASE64_CHARS[bitmap >> 12 & 63] + BASE64_CHARS[bitmap >> 6 & 63] + BASE64_CHARS[bitmap & 63];
|
|
29
|
-
}
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
32
|
-
function base64ToUint16Array(base64) {
|
|
33
|
-
const numBytes = Math.floor(base64.length * 3 / 4);
|
|
34
|
-
const bytes = new Uint8Array(numBytes);
|
|
35
|
-
let byteIndex = 0;
|
|
36
|
-
for (let i = 0; i < base64.length; i += 4) {
|
|
37
|
-
const c0 = B64_LOOKUP[base64.charCodeAt(i)];
|
|
38
|
-
const c1 = B64_LOOKUP[base64.charCodeAt(i + 1)];
|
|
39
|
-
const c2 = B64_LOOKUP[base64.charCodeAt(i + 2)];
|
|
40
|
-
const c3 = B64_LOOKUP[base64.charCodeAt(i + 3)];
|
|
41
|
-
const bitmap = c0 << 18 | c1 << 12 | c2 << 6 | c3;
|
|
42
|
-
bytes[byteIndex++] = bitmap >> 16 & 255;
|
|
43
|
-
bytes[byteIndex++] = bitmap >> 8 & 255;
|
|
44
|
-
bytes[byteIndex++] = bitmap & 255;
|
|
45
|
-
}
|
|
46
|
-
return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
|
|
47
|
-
}
|
|
48
|
-
function float16BitsToNumber(bits) {
|
|
49
|
-
const sign = bits >> 15;
|
|
50
|
-
const exp = bits >> 10 & 31;
|
|
51
|
-
const frac = bits & 1023;
|
|
52
|
-
if (exp === 0) {
|
|
53
|
-
return sign ? -frac * POW2_SUBNORMAL : frac * POW2_SUBNORMAL;
|
|
54
|
-
}
|
|
55
|
-
if (exp === 31) {
|
|
56
|
-
return frac ? NaN : sign ? -Infinity : Infinity;
|
|
57
|
-
}
|
|
58
|
-
const magnitude = POW2[exp] * MANTISSA[frac];
|
|
59
|
-
return sign ? -magnitude : magnitude;
|
|
60
|
-
}
|
|
61
|
-
function numberToFloat16Bits(value) {
|
|
62
|
-
if (value === 0) return Object.is(value, -0) ? 32768 : 0;
|
|
63
|
-
if (!Number.isFinite(value)) {
|
|
64
|
-
if (Number.isNaN(value)) return 32256;
|
|
65
|
-
return value > 0 ? 31744 : 64512;
|
|
66
|
-
}
|
|
67
|
-
const sign = value < 0 ? 1 : 0;
|
|
68
|
-
value = Math.abs(value);
|
|
69
|
-
const exp = Math.floor(Math.log2(value));
|
|
70
|
-
let expBiased = exp + 15;
|
|
71
|
-
if (expBiased >= 31) {
|
|
72
|
-
return sign << 15 | 31744;
|
|
73
|
-
}
|
|
74
|
-
if (expBiased <= 0) {
|
|
75
|
-
const frac2 = Math.round(value * Math.pow(2, 14) * 1024);
|
|
76
|
-
return sign << 15 | frac2 & 1023;
|
|
77
|
-
}
|
|
78
|
-
const mantissa = value / Math.pow(2, exp) - 1;
|
|
79
|
-
let frac = Math.round(mantissa * 1024);
|
|
80
|
-
if (frac >= 1024) {
|
|
81
|
-
frac = 0;
|
|
82
|
-
expBiased++;
|
|
83
|
-
if (expBiased >= 31) {
|
|
84
|
-
return sign << 15 | 31744;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return sign << 15 | expBiased << 10 | frac;
|
|
88
|
-
}
|
|
89
|
-
class b64Vecs {
|
|
90
|
-
/**
|
|
91
|
-
* Encode a single point (x, y, z) to 8 base64 characters.
|
|
92
|
-
* Each coordinate is encoded as a Float16 value, resulting in 6 bytes total.
|
|
93
|
-
*
|
|
94
|
-
* @param x - The x coordinate
|
|
95
|
-
* @param y - The y coordinate
|
|
96
|
-
* @param z - The z coordinate
|
|
97
|
-
* @returns An 8-character base64 string representing the point
|
|
98
|
-
*/
|
|
99
|
-
static encodePoint(x, y, z) {
|
|
100
|
-
const xBits = numberToFloat16Bits(x);
|
|
101
|
-
const yBits = numberToFloat16Bits(y);
|
|
102
|
-
const zBits = numberToFloat16Bits(z);
|
|
103
|
-
const b0 = xBits & 255;
|
|
104
|
-
const b1 = xBits >> 8 & 255;
|
|
105
|
-
const b2 = yBits & 255;
|
|
106
|
-
const b3 = yBits >> 8 & 255;
|
|
107
|
-
const b4 = zBits & 255;
|
|
108
|
-
const b5 = zBits >> 8 & 255;
|
|
109
|
-
const bitmap1 = b0 << 16 | b1 << 8 | b2;
|
|
110
|
-
const bitmap2 = b3 << 16 | b4 << 8 | b5;
|
|
111
|
-
return BASE64_CHARS[bitmap1 >> 18 & 63] + BASE64_CHARS[bitmap1 >> 12 & 63] + BASE64_CHARS[bitmap1 >> 6 & 63] + BASE64_CHARS[bitmap1 & 63] + BASE64_CHARS[bitmap2 >> 18 & 63] + BASE64_CHARS[bitmap2 >> 12 & 63] + BASE64_CHARS[bitmap2 >> 6 & 63] + BASE64_CHARS[bitmap2 & 63];
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Convert an array of VecModels to a base64 string for compact storage.
|
|
115
|
-
* Uses Float16 encoding for each coordinate (x, y, z). If a point's z value is
|
|
116
|
-
* undefined, it defaults to 0.5.
|
|
117
|
-
*
|
|
118
|
-
* @param points - An array of VecModel objects to encode
|
|
119
|
-
* @returns A base64-encoded string containing all points
|
|
120
|
-
*/
|
|
121
|
-
static encodePoints(points) {
|
|
122
|
-
const uint16s = new Uint16Array(points.length * 3);
|
|
123
|
-
for (let i = 0; i < points.length; i++) {
|
|
124
|
-
const p = points[i];
|
|
125
|
-
uint16s[i * 3] = numberToFloat16Bits(p.x);
|
|
126
|
-
uint16s[i * 3 + 1] = numberToFloat16Bits(p.y);
|
|
127
|
-
uint16s[i * 3 + 2] = numberToFloat16Bits(p.z ?? 0.5);
|
|
128
|
-
}
|
|
129
|
-
return uint16ArrayToBase64(uint16s);
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Convert a base64 string back to an array of VecModels.
|
|
133
|
-
* Decodes Float16-encoded coordinates (x, y, z) from the base64 string.
|
|
134
|
-
*
|
|
135
|
-
* @param base64 - The base64-encoded string containing point data
|
|
136
|
-
* @returns An array of VecModel objects decoded from the string
|
|
137
|
-
*/
|
|
138
|
-
static decodePoints(base64) {
|
|
139
|
-
const uint16s = base64ToUint16Array(base64);
|
|
140
|
-
const result = [];
|
|
141
|
-
for (let i = 0; i < uint16s.length; i += 3) {
|
|
142
|
-
result.push({
|
|
143
|
-
x: float16BitsToNumber(uint16s[i]),
|
|
144
|
-
y: float16BitsToNumber(uint16s[i + 1]),
|
|
145
|
-
z: float16BitsToNumber(uint16s[i + 2])
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Decode a single point (8 base64 chars) starting at the given offset.
|
|
152
|
-
* Each point is encoded as 3 Float16 values (x, y, z) in 8 base64 characters.
|
|
153
|
-
*
|
|
154
|
-
* @param b64Points - The base64-encoded string containing point data
|
|
155
|
-
* @param charOffset - The character offset where the point starts (must be a multiple of 8)
|
|
156
|
-
* @returns A VecModel object with x, y, and z coordinates
|
|
157
|
-
* @internal
|
|
158
|
-
*/
|
|
159
|
-
static decodePointAt(b64Points, charOffset) {
|
|
160
|
-
const c0 = B64_LOOKUP[b64Points.charCodeAt(charOffset)];
|
|
161
|
-
const c1 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 1)];
|
|
162
|
-
const c2 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 2)];
|
|
163
|
-
const c3 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 3)];
|
|
164
|
-
const c4 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 4)];
|
|
165
|
-
const c5 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 5)];
|
|
166
|
-
const c6 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 6)];
|
|
167
|
-
const c7 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 7)];
|
|
168
|
-
const bitmap1 = c0 << 18 | c1 << 12 | c2 << 6 | c3;
|
|
169
|
-
const bitmap2 = c4 << 18 | c5 << 12 | c6 << 6 | c7;
|
|
170
|
-
const xBits = bitmap1 >> 16 & 255 | bitmap1 & 65280;
|
|
171
|
-
const yBits = bitmap1 & 255 | bitmap2 >> 8 & 65280;
|
|
172
|
-
const zBits = bitmap2 >> 8 & 255 | bitmap2 << 8 & 65280;
|
|
173
|
-
return {
|
|
174
|
-
x: float16BitsToNumber(xBits),
|
|
175
|
-
y: float16BitsToNumber(yBits),
|
|
176
|
-
z: float16BitsToNumber(zBits)
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Get the first point from a base64-encoded string of points.
|
|
181
|
-
*
|
|
182
|
-
* @param b64Points - The base64-encoded string containing point data
|
|
183
|
-
* @returns The first point as a VecModel, or null if the string is too short
|
|
184
|
-
* @public
|
|
185
|
-
*/
|
|
186
|
-
static decodeFirstPoint(b64Points) {
|
|
187
|
-
if (b64Points.length < POINT_B64_LENGTH) return null;
|
|
188
|
-
return b64Vecs.decodePointAt(b64Points, 0);
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Get the last point from a base64-encoded string of points.
|
|
192
|
-
*
|
|
193
|
-
* @param b64Points - The base64-encoded string containing point data
|
|
194
|
-
* @returns The last point as a VecModel, or null if the string is too short
|
|
195
|
-
*/
|
|
196
|
-
static decodeLastPoint(b64Points) {
|
|
197
|
-
if (b64Points.length < POINT_B64_LENGTH) return null;
|
|
198
|
-
return b64Vecs.decodePointAt(b64Points, b64Points.length - POINT_B64_LENGTH);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
export {
|
|
202
|
-
b64Vecs
|
|
203
|
-
};
|
|
204
|
-
//# sourceMappingURL=b64Vecs.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/misc/b64Vecs.ts"],
|
|
4
|
-
"sourcesContent": ["import { VecModel } from './geometry-types'\n\n// Each point = 3 Float16s = 6 bytes = 8 base64 chars\nconst POINT_B64_LENGTH = 8\n\n// O(1) lookup table for base64 decoding (maps char code -> 6-bit value)\nconst BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nconst B64_LOOKUP = new Uint8Array(128)\nfor (let i = 0; i < 64; i++) {\n\tB64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i\n}\n\n// Precomputed powers of 2 for Float16 exponents (exp - 15, so indices 0-30 map to 2^-15 to 2^15)\nconst POW2 = new Float64Array(31)\nfor (let i = 0; i < 31; i++) {\n\tPOW2[i] = Math.pow(2, i - 15)\n}\nconst POW2_SUBNORMAL = Math.pow(2, -14) / 1024 // For subnormal numbers\n\n// Precomputed mantissa values: 1 + frac/1024 for all 1024 possible frac values\n// Avoids division in hot path\nconst MANTISSA = new Float64Array(1024)\nfor (let i = 0; i < 1024; i++) {\n\tMANTISSA[i] = 1 + i / 1024\n}\n\n/**\n * Convert a Uint16Array (containing Float16 bits) to base64.\n * Processes bytes in groups of 3 to produce 4 base64 characters.\n *\n * @internal\n */\nfunction uint16ArrayToBase64(uint16Array: Uint16Array): string {\n\tconst uint8Array = new Uint8Array(\n\t\tuint16Array.buffer,\n\t\tuint16Array.byteOffset,\n\t\tuint16Array.byteLength\n\t)\n\tlet result = ''\n\n\t// Process bytes in groups of 3 -> 4 base64 chars\n\tfor (let i = 0; i < uint8Array.length; i += 3) {\n\t\tconst byte1 = uint8Array[i]\n\t\tconst byte2 = uint8Array[i + 1] // Always exists for our use case (multiple of 6 bytes)\n\t\tconst byte3 = uint8Array[i + 2]\n\n\t\tconst bitmap = (byte1 << 16) | (byte2 << 8) | byte3\n\t\tresult +=\n\t\t\tBASE64_CHARS[(bitmap >> 18) & 63] +\n\t\t\tBASE64_CHARS[(bitmap >> 12) & 63] +\n\t\t\tBASE64_CHARS[(bitmap >> 6) & 63] +\n\t\t\tBASE64_CHARS[bitmap & 63]\n\t}\n\n\treturn result\n}\n\n/**\n * Convert a base64 string to Uint16Array containing Float16 bits.\n * The base64 string must have a length that is a multiple of 4.\n *\n * @param base64 - The base64-encoded string to decode\n * @returns A Uint16Array containing the decoded Float16 bit values\n * @public\n */\nfunction base64ToUint16Array(base64: string): Uint16Array {\n\t// Calculate exact number of bytes (4 base64 chars = 3 bytes)\n\tconst numBytes = Math.floor((base64.length * 3) / 4)\n\tconst bytes = new Uint8Array(numBytes)\n\tlet byteIndex = 0\n\n\t// Process in groups of 4 base64 characters\n\tfor (let i = 0; i < base64.length; i += 4) {\n\t\tconst c0 = B64_LOOKUP[base64.charCodeAt(i)]\n\t\tconst c1 = B64_LOOKUP[base64.charCodeAt(i + 1)]\n\t\tconst c2 = B64_LOOKUP[base64.charCodeAt(i + 2)]\n\t\tconst c3 = B64_LOOKUP[base64.charCodeAt(i + 3)]\n\n\t\tconst bitmap = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3\n\n\t\tbytes[byteIndex++] = (bitmap >> 16) & 255\n\t\tbytes[byteIndex++] = (bitmap >> 8) & 255\n\t\tbytes[byteIndex++] = bitmap & 255\n\t}\n\n\treturn new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2)\n}\n\n/**\n * Convert Float16 bits to a number using optimized lookup tables.\n * Handles normal numbers, subnormal numbers, zero, infinity, and NaN.\n *\n * @param bits - The 16-bit Float16 value to decode\n * @returns The decoded number value\n */\nfunction float16BitsToNumber(bits: number): number {\n\tconst sign = bits >> 15\n\tconst exp = (bits >> 10) & 0x1f\n\tconst frac = bits & 0x3ff\n\n\tif (exp === 0) {\n\t\t// Subnormal or zero - rare case\n\t\treturn sign ? -frac * POW2_SUBNORMAL : frac * POW2_SUBNORMAL\n\t}\n\tif (exp === 31) {\n\t\t// Infinity or NaN - very rare\n\t\treturn frac ? NaN : sign ? -Infinity : Infinity\n\t}\n\t// Normal case - two table lookups, one multiply, no division\n\tconst magnitude = POW2[exp] * MANTISSA[frac]\n\treturn sign ? -magnitude : magnitude\n}\n\n/**\n * Convert a number to Float16 bits.\n * Handles normal numbers, subnormal numbers, zero, infinity, and NaN.\n *\n * @param value - The number to encode as Float16\n * @returns The 16-bit Float16 representation of the number\n * @internal\n */\nfunction numberToFloat16Bits(value: number): number {\n\tif (value === 0) return Object.is(value, -0) ? 0x8000 : 0\n\tif (!Number.isFinite(value)) {\n\t\tif (Number.isNaN(value)) return 0x7e00\n\t\treturn value > 0 ? 0x7c00 : 0xfc00\n\t}\n\n\tconst sign = value < 0 ? 1 : 0\n\tvalue = Math.abs(value)\n\n\t// Find exponent and mantissa\n\tconst exp = Math.floor(Math.log2(value))\n\tlet expBiased = exp + 15\n\n\tif (expBiased >= 31) {\n\t\t// Overflow to infinity\n\t\treturn (sign << 15) | 0x7c00\n\t}\n\tif (expBiased <= 0) {\n\t\t// Subnormal or underflow\n\t\tconst frac = Math.round(value * Math.pow(2, 14) * 1024)\n\t\treturn (sign << 15) | (frac & 0x3ff)\n\t}\n\n\t// Normal number\n\tconst mantissa = value / Math.pow(2, exp) - 1\n\tlet frac = Math.round(mantissa * 1024)\n\n\t// Handle rounding overflow: if frac rounds to 1024, increment exponent\n\tif (frac >= 1024) {\n\t\tfrac = 0\n\t\texpBiased++\n\t\tif (expBiased >= 31) {\n\t\t\t// Overflow to infinity\n\t\t\treturn (sign << 15) | 0x7c00\n\t\t}\n\t}\n\n\treturn (sign << 15) | (expBiased << 10) | frac\n}\n\n/**\n * Utilities for encoding and decoding points using base64 and Float16 encoding.\n * Provides functions for converting between VecModel arrays and compact base64 strings,\n * as well as individual point encoding/decoding operations.\n *\n * @public\n */\nexport class b64Vecs {\n\t/**\n\t * Encode a single point (x, y, z) to 8 base64 characters.\n\t * Each coordinate is encoded as a Float16 value, resulting in 6 bytes total.\n\t *\n\t * @param x - The x coordinate\n\t * @param y - The y coordinate\n\t * @param z - The z coordinate\n\t * @returns An 8-character base64 string representing the point\n\t */\n\tstatic encodePoint(x: number, y: number, z: number): string {\n\t\tconst xBits = numberToFloat16Bits(x)\n\t\tconst yBits = numberToFloat16Bits(y)\n\t\tconst zBits = numberToFloat16Bits(z)\n\n\t\t// Convert Float16 bits to 6 bytes (little-endian)\n\t\tconst b0 = xBits & 0xff\n\t\tconst b1 = (xBits >> 8) & 0xff\n\t\tconst b2 = yBits & 0xff\n\t\tconst b3 = (yBits >> 8) & 0xff\n\t\tconst b4 = zBits & 0xff\n\t\tconst b5 = (zBits >> 8) & 0xff\n\n\t\t// Convert 6 bytes to 8 base64 chars\n\t\tconst bitmap1 = (b0 << 16) | (b1 << 8) | b2\n\t\tconst bitmap2 = (b3 << 16) | (b4 << 8) | b5\n\n\t\treturn (\n\t\t\tBASE64_CHARS[(bitmap1 >> 18) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap1 >> 12) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap1 >> 6) & 0x3f] +\n\t\t\tBASE64_CHARS[bitmap1 & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap2 >> 18) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap2 >> 12) & 0x3f] +\n\t\t\tBASE64_CHARS[(bitmap2 >> 6) & 0x3f] +\n\t\t\tBASE64_CHARS[bitmap2 & 0x3f]\n\t\t)\n\t}\n\n\t/**\n\t * Convert an array of VecModels to a base64 string for compact storage.\n\t * Uses Float16 encoding for each coordinate (x, y, z). If a point's z value is\n\t * undefined, it defaults to 0.5.\n\t *\n\t * @param points - An array of VecModel objects to encode\n\t * @returns A base64-encoded string containing all points\n\t */\n\tstatic encodePoints(points: VecModel[]): string {\n\t\tconst uint16s = new Uint16Array(points.length * 3)\n\t\tfor (let i = 0; i < points.length; i++) {\n\t\t\tconst p = points[i]\n\t\t\tuint16s[i * 3] = numberToFloat16Bits(p.x)\n\t\t\tuint16s[i * 3 + 1] = numberToFloat16Bits(p.y)\n\t\t\tuint16s[i * 3 + 2] = numberToFloat16Bits(p.z ?? 0.5)\n\t\t}\n\t\treturn uint16ArrayToBase64(uint16s)\n\t}\n\n\t/**\n\t * Convert a base64 string back to an array of VecModels.\n\t * Decodes Float16-encoded coordinates (x, y, z) from the base64 string.\n\t *\n\t * @param base64 - The base64-encoded string containing point data\n\t * @returns An array of VecModel objects decoded from the string\n\t */\n\tstatic decodePoints(base64: string): VecModel[] {\n\t\tconst uint16s = base64ToUint16Array(base64)\n\t\tconst result: VecModel[] = []\n\t\tfor (let i = 0; i < uint16s.length; i += 3) {\n\t\t\tresult.push({\n\t\t\t\tx: float16BitsToNumber(uint16s[i]),\n\t\t\t\ty: float16BitsToNumber(uint16s[i + 1]),\n\t\t\t\tz: float16BitsToNumber(uint16s[i + 2]),\n\t\t\t})\n\t\t}\n\t\treturn result\n\t}\n\n\t/**\n\t * Decode a single point (8 base64 chars) starting at the given offset.\n\t * Each point is encoded as 3 Float16 values (x, y, z) in 8 base64 characters.\n\t *\n\t * @param b64Points - The base64-encoded string containing point data\n\t * @param charOffset - The character offset where the point starts (must be a multiple of 8)\n\t * @returns A VecModel object with x, y, and z coordinates\n\t * @internal\n\t */\n\tstatic decodePointAt(b64Points: string, charOffset: number): VecModel {\n\t\t// Decode 8 base64 chars -> 6 bytes -> 3 Float16s using O(1) lookup\n\t\tconst c0 = B64_LOOKUP[b64Points.charCodeAt(charOffset)]\n\t\tconst c1 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 1)]\n\t\tconst c2 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 2)]\n\t\tconst c3 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 3)]\n\t\tconst c4 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 4)]\n\t\tconst c5 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 5)]\n\t\tconst c6 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 6)]\n\t\tconst c7 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 7)]\n\n\t\t// 4 base64 chars -> 24 bits -> 3 bytes\n\t\tconst bitmap1 = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3\n\t\tconst bitmap2 = (c4 << 18) | (c5 << 12) | (c6 << 6) | c7\n\n\t\t// Extract Float16 bits directly (little-endian byte order)\n\t\t// bitmap1 = [byte0:8][byte1:8][byte2:8], bitmap2 = [byte3:8][byte4:8][byte5:8]\n\t\t// xBits = byte0 | (byte1 << 8), yBits = byte2 | (byte3 << 8), zBits = byte4 | (byte5 << 8)\n\t\tconst xBits = ((bitmap1 >> 16) & 0xff) | (bitmap1 & 0xff00)\n\t\tconst yBits = (bitmap1 & 0xff) | ((bitmap2 >> 8) & 0xff00)\n\t\tconst zBits = ((bitmap2 >> 8) & 0xff) | ((bitmap2 << 8) & 0xff00)\n\n\t\treturn {\n\t\t\tx: float16BitsToNumber(xBits),\n\t\t\ty: float16BitsToNumber(yBits),\n\t\t\tz: float16BitsToNumber(zBits),\n\t\t}\n\t}\n\n\t/**\n\t * Get the first point from a base64-encoded string of points.\n\t *\n\t * @param b64Points - The base64-encoded string containing point data\n\t * @returns The first point as a VecModel, or null if the string is too short\n\t * @public\n\t */\n\tstatic decodeFirstPoint(b64Points: string): VecModel | null {\n\t\tif (b64Points.length < POINT_B64_LENGTH) return null\n\t\treturn b64Vecs.decodePointAt(b64Points, 0)\n\t}\n\n\t/**\n\t * Get the last point from a base64-encoded string of points.\n\t *\n\t * @param b64Points - The base64-encoded string containing point data\n\t * @returns The last point as a VecModel, or null if the string is too short\n\t */\n\tstatic decodeLastPoint(b64Points: string): VecModel | null {\n\t\tif (b64Points.length < POINT_B64_LENGTH) return null\n\t\treturn b64Vecs.decodePointAt(b64Points, b64Points.length - POINT_B64_LENGTH)\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["frac"]
|
|
7
|
-
}
|
package/src/misc/b64Vecs.ts
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import { VecModel } from './geometry-types'
|
|
2
|
-
|
|
3
|
-
// Each point = 3 Float16s = 6 bytes = 8 base64 chars
|
|
4
|
-
const POINT_B64_LENGTH = 8
|
|
5
|
-
|
|
6
|
-
// O(1) lookup table for base64 decoding (maps char code -> 6-bit value)
|
|
7
|
-
const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
8
|
-
const B64_LOOKUP = new Uint8Array(128)
|
|
9
|
-
for (let i = 0; i < 64; i++) {
|
|
10
|
-
B64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Precomputed powers of 2 for Float16 exponents (exp - 15, so indices 0-30 map to 2^-15 to 2^15)
|
|
14
|
-
const POW2 = new Float64Array(31)
|
|
15
|
-
for (let i = 0; i < 31; i++) {
|
|
16
|
-
POW2[i] = Math.pow(2, i - 15)
|
|
17
|
-
}
|
|
18
|
-
const POW2_SUBNORMAL = Math.pow(2, -14) / 1024 // For subnormal numbers
|
|
19
|
-
|
|
20
|
-
// Precomputed mantissa values: 1 + frac/1024 for all 1024 possible frac values
|
|
21
|
-
// Avoids division in hot path
|
|
22
|
-
const MANTISSA = new Float64Array(1024)
|
|
23
|
-
for (let i = 0; i < 1024; i++) {
|
|
24
|
-
MANTISSA[i] = 1 + i / 1024
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Convert a Uint16Array (containing Float16 bits) to base64.
|
|
29
|
-
* Processes bytes in groups of 3 to produce 4 base64 characters.
|
|
30
|
-
*
|
|
31
|
-
* @internal
|
|
32
|
-
*/
|
|
33
|
-
function uint16ArrayToBase64(uint16Array: Uint16Array): string {
|
|
34
|
-
const uint8Array = new Uint8Array(
|
|
35
|
-
uint16Array.buffer,
|
|
36
|
-
uint16Array.byteOffset,
|
|
37
|
-
uint16Array.byteLength
|
|
38
|
-
)
|
|
39
|
-
let result = ''
|
|
40
|
-
|
|
41
|
-
// Process bytes in groups of 3 -> 4 base64 chars
|
|
42
|
-
for (let i = 0; i < uint8Array.length; i += 3) {
|
|
43
|
-
const byte1 = uint8Array[i]
|
|
44
|
-
const byte2 = uint8Array[i + 1] // Always exists for our use case (multiple of 6 bytes)
|
|
45
|
-
const byte3 = uint8Array[i + 2]
|
|
46
|
-
|
|
47
|
-
const bitmap = (byte1 << 16) | (byte2 << 8) | byte3
|
|
48
|
-
result +=
|
|
49
|
-
BASE64_CHARS[(bitmap >> 18) & 63] +
|
|
50
|
-
BASE64_CHARS[(bitmap >> 12) & 63] +
|
|
51
|
-
BASE64_CHARS[(bitmap >> 6) & 63] +
|
|
52
|
-
BASE64_CHARS[bitmap & 63]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return result
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Convert a base64 string to Uint16Array containing Float16 bits.
|
|
60
|
-
* The base64 string must have a length that is a multiple of 4.
|
|
61
|
-
*
|
|
62
|
-
* @param base64 - The base64-encoded string to decode
|
|
63
|
-
* @returns A Uint16Array containing the decoded Float16 bit values
|
|
64
|
-
* @public
|
|
65
|
-
*/
|
|
66
|
-
function base64ToUint16Array(base64: string): Uint16Array {
|
|
67
|
-
// Calculate exact number of bytes (4 base64 chars = 3 bytes)
|
|
68
|
-
const numBytes = Math.floor((base64.length * 3) / 4)
|
|
69
|
-
const bytes = new Uint8Array(numBytes)
|
|
70
|
-
let byteIndex = 0
|
|
71
|
-
|
|
72
|
-
// Process in groups of 4 base64 characters
|
|
73
|
-
for (let i = 0; i < base64.length; i += 4) {
|
|
74
|
-
const c0 = B64_LOOKUP[base64.charCodeAt(i)]
|
|
75
|
-
const c1 = B64_LOOKUP[base64.charCodeAt(i + 1)]
|
|
76
|
-
const c2 = B64_LOOKUP[base64.charCodeAt(i + 2)]
|
|
77
|
-
const c3 = B64_LOOKUP[base64.charCodeAt(i + 3)]
|
|
78
|
-
|
|
79
|
-
const bitmap = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3
|
|
80
|
-
|
|
81
|
-
bytes[byteIndex++] = (bitmap >> 16) & 255
|
|
82
|
-
bytes[byteIndex++] = (bitmap >> 8) & 255
|
|
83
|
-
bytes[byteIndex++] = bitmap & 255
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Convert Float16 bits to a number using optimized lookup tables.
|
|
91
|
-
* Handles normal numbers, subnormal numbers, zero, infinity, and NaN.
|
|
92
|
-
*
|
|
93
|
-
* @param bits - The 16-bit Float16 value to decode
|
|
94
|
-
* @returns The decoded number value
|
|
95
|
-
*/
|
|
96
|
-
function float16BitsToNumber(bits: number): number {
|
|
97
|
-
const sign = bits >> 15
|
|
98
|
-
const exp = (bits >> 10) & 0x1f
|
|
99
|
-
const frac = bits & 0x3ff
|
|
100
|
-
|
|
101
|
-
if (exp === 0) {
|
|
102
|
-
// Subnormal or zero - rare case
|
|
103
|
-
return sign ? -frac * POW2_SUBNORMAL : frac * POW2_SUBNORMAL
|
|
104
|
-
}
|
|
105
|
-
if (exp === 31) {
|
|
106
|
-
// Infinity or NaN - very rare
|
|
107
|
-
return frac ? NaN : sign ? -Infinity : Infinity
|
|
108
|
-
}
|
|
109
|
-
// Normal case - two table lookups, one multiply, no division
|
|
110
|
-
const magnitude = POW2[exp] * MANTISSA[frac]
|
|
111
|
-
return sign ? -magnitude : magnitude
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Convert a number to Float16 bits.
|
|
116
|
-
* Handles normal numbers, subnormal numbers, zero, infinity, and NaN.
|
|
117
|
-
*
|
|
118
|
-
* @param value - The number to encode as Float16
|
|
119
|
-
* @returns The 16-bit Float16 representation of the number
|
|
120
|
-
* @internal
|
|
121
|
-
*/
|
|
122
|
-
function numberToFloat16Bits(value: number): number {
|
|
123
|
-
if (value === 0) return Object.is(value, -0) ? 0x8000 : 0
|
|
124
|
-
if (!Number.isFinite(value)) {
|
|
125
|
-
if (Number.isNaN(value)) return 0x7e00
|
|
126
|
-
return value > 0 ? 0x7c00 : 0xfc00
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const sign = value < 0 ? 1 : 0
|
|
130
|
-
value = Math.abs(value)
|
|
131
|
-
|
|
132
|
-
// Find exponent and mantissa
|
|
133
|
-
const exp = Math.floor(Math.log2(value))
|
|
134
|
-
let expBiased = exp + 15
|
|
135
|
-
|
|
136
|
-
if (expBiased >= 31) {
|
|
137
|
-
// Overflow to infinity
|
|
138
|
-
return (sign << 15) | 0x7c00
|
|
139
|
-
}
|
|
140
|
-
if (expBiased <= 0) {
|
|
141
|
-
// Subnormal or underflow
|
|
142
|
-
const frac = Math.round(value * Math.pow(2, 14) * 1024)
|
|
143
|
-
return (sign << 15) | (frac & 0x3ff)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Normal number
|
|
147
|
-
const mantissa = value / Math.pow(2, exp) - 1
|
|
148
|
-
let frac = Math.round(mantissa * 1024)
|
|
149
|
-
|
|
150
|
-
// Handle rounding overflow: if frac rounds to 1024, increment exponent
|
|
151
|
-
if (frac >= 1024) {
|
|
152
|
-
frac = 0
|
|
153
|
-
expBiased++
|
|
154
|
-
if (expBiased >= 31) {
|
|
155
|
-
// Overflow to infinity
|
|
156
|
-
return (sign << 15) | 0x7c00
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return (sign << 15) | (expBiased << 10) | frac
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Utilities for encoding and decoding points using base64 and Float16 encoding.
|
|
165
|
-
* Provides functions for converting between VecModel arrays and compact base64 strings,
|
|
166
|
-
* as well as individual point encoding/decoding operations.
|
|
167
|
-
*
|
|
168
|
-
* @public
|
|
169
|
-
*/
|
|
170
|
-
export class b64Vecs {
|
|
171
|
-
/**
|
|
172
|
-
* Encode a single point (x, y, z) to 8 base64 characters.
|
|
173
|
-
* Each coordinate is encoded as a Float16 value, resulting in 6 bytes total.
|
|
174
|
-
*
|
|
175
|
-
* @param x - The x coordinate
|
|
176
|
-
* @param y - The y coordinate
|
|
177
|
-
* @param z - The z coordinate
|
|
178
|
-
* @returns An 8-character base64 string representing the point
|
|
179
|
-
*/
|
|
180
|
-
static encodePoint(x: number, y: number, z: number): string {
|
|
181
|
-
const xBits = numberToFloat16Bits(x)
|
|
182
|
-
const yBits = numberToFloat16Bits(y)
|
|
183
|
-
const zBits = numberToFloat16Bits(z)
|
|
184
|
-
|
|
185
|
-
// Convert Float16 bits to 6 bytes (little-endian)
|
|
186
|
-
const b0 = xBits & 0xff
|
|
187
|
-
const b1 = (xBits >> 8) & 0xff
|
|
188
|
-
const b2 = yBits & 0xff
|
|
189
|
-
const b3 = (yBits >> 8) & 0xff
|
|
190
|
-
const b4 = zBits & 0xff
|
|
191
|
-
const b5 = (zBits >> 8) & 0xff
|
|
192
|
-
|
|
193
|
-
// Convert 6 bytes to 8 base64 chars
|
|
194
|
-
const bitmap1 = (b0 << 16) | (b1 << 8) | b2
|
|
195
|
-
const bitmap2 = (b3 << 16) | (b4 << 8) | b5
|
|
196
|
-
|
|
197
|
-
return (
|
|
198
|
-
BASE64_CHARS[(bitmap1 >> 18) & 0x3f] +
|
|
199
|
-
BASE64_CHARS[(bitmap1 >> 12) & 0x3f] +
|
|
200
|
-
BASE64_CHARS[(bitmap1 >> 6) & 0x3f] +
|
|
201
|
-
BASE64_CHARS[bitmap1 & 0x3f] +
|
|
202
|
-
BASE64_CHARS[(bitmap2 >> 18) & 0x3f] +
|
|
203
|
-
BASE64_CHARS[(bitmap2 >> 12) & 0x3f] +
|
|
204
|
-
BASE64_CHARS[(bitmap2 >> 6) & 0x3f] +
|
|
205
|
-
BASE64_CHARS[bitmap2 & 0x3f]
|
|
206
|
-
)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Convert an array of VecModels to a base64 string for compact storage.
|
|
211
|
-
* Uses Float16 encoding for each coordinate (x, y, z). If a point's z value is
|
|
212
|
-
* undefined, it defaults to 0.5.
|
|
213
|
-
*
|
|
214
|
-
* @param points - An array of VecModel objects to encode
|
|
215
|
-
* @returns A base64-encoded string containing all points
|
|
216
|
-
*/
|
|
217
|
-
static encodePoints(points: VecModel[]): string {
|
|
218
|
-
const uint16s = new Uint16Array(points.length * 3)
|
|
219
|
-
for (let i = 0; i < points.length; i++) {
|
|
220
|
-
const p = points[i]
|
|
221
|
-
uint16s[i * 3] = numberToFloat16Bits(p.x)
|
|
222
|
-
uint16s[i * 3 + 1] = numberToFloat16Bits(p.y)
|
|
223
|
-
uint16s[i * 3 + 2] = numberToFloat16Bits(p.z ?? 0.5)
|
|
224
|
-
}
|
|
225
|
-
return uint16ArrayToBase64(uint16s)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Convert a base64 string back to an array of VecModels.
|
|
230
|
-
* Decodes Float16-encoded coordinates (x, y, z) from the base64 string.
|
|
231
|
-
*
|
|
232
|
-
* @param base64 - The base64-encoded string containing point data
|
|
233
|
-
* @returns An array of VecModel objects decoded from the string
|
|
234
|
-
*/
|
|
235
|
-
static decodePoints(base64: string): VecModel[] {
|
|
236
|
-
const uint16s = base64ToUint16Array(base64)
|
|
237
|
-
const result: VecModel[] = []
|
|
238
|
-
for (let i = 0; i < uint16s.length; i += 3) {
|
|
239
|
-
result.push({
|
|
240
|
-
x: float16BitsToNumber(uint16s[i]),
|
|
241
|
-
y: float16BitsToNumber(uint16s[i + 1]),
|
|
242
|
-
z: float16BitsToNumber(uint16s[i + 2]),
|
|
243
|
-
})
|
|
244
|
-
}
|
|
245
|
-
return result
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Decode a single point (8 base64 chars) starting at the given offset.
|
|
250
|
-
* Each point is encoded as 3 Float16 values (x, y, z) in 8 base64 characters.
|
|
251
|
-
*
|
|
252
|
-
* @param b64Points - The base64-encoded string containing point data
|
|
253
|
-
* @param charOffset - The character offset where the point starts (must be a multiple of 8)
|
|
254
|
-
* @returns A VecModel object with x, y, and z coordinates
|
|
255
|
-
* @internal
|
|
256
|
-
*/
|
|
257
|
-
static decodePointAt(b64Points: string, charOffset: number): VecModel {
|
|
258
|
-
// Decode 8 base64 chars -> 6 bytes -> 3 Float16s using O(1) lookup
|
|
259
|
-
const c0 = B64_LOOKUP[b64Points.charCodeAt(charOffset)]
|
|
260
|
-
const c1 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 1)]
|
|
261
|
-
const c2 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 2)]
|
|
262
|
-
const c3 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 3)]
|
|
263
|
-
const c4 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 4)]
|
|
264
|
-
const c5 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 5)]
|
|
265
|
-
const c6 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 6)]
|
|
266
|
-
const c7 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 7)]
|
|
267
|
-
|
|
268
|
-
// 4 base64 chars -> 24 bits -> 3 bytes
|
|
269
|
-
const bitmap1 = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3
|
|
270
|
-
const bitmap2 = (c4 << 18) | (c5 << 12) | (c6 << 6) | c7
|
|
271
|
-
|
|
272
|
-
// Extract Float16 bits directly (little-endian byte order)
|
|
273
|
-
// bitmap1 = [byte0:8][byte1:8][byte2:8], bitmap2 = [byte3:8][byte4:8][byte5:8]
|
|
274
|
-
// xBits = byte0 | (byte1 << 8), yBits = byte2 | (byte3 << 8), zBits = byte4 | (byte5 << 8)
|
|
275
|
-
const xBits = ((bitmap1 >> 16) & 0xff) | (bitmap1 & 0xff00)
|
|
276
|
-
const yBits = (bitmap1 & 0xff) | ((bitmap2 >> 8) & 0xff00)
|
|
277
|
-
const zBits = ((bitmap2 >> 8) & 0xff) | ((bitmap2 << 8) & 0xff00)
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
x: float16BitsToNumber(xBits),
|
|
281
|
-
y: float16BitsToNumber(yBits),
|
|
282
|
-
z: float16BitsToNumber(zBits),
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Get the first point from a base64-encoded string of points.
|
|
288
|
-
*
|
|
289
|
-
* @param b64Points - The base64-encoded string containing point data
|
|
290
|
-
* @returns The first point as a VecModel, or null if the string is too short
|
|
291
|
-
* @public
|
|
292
|
-
*/
|
|
293
|
-
static decodeFirstPoint(b64Points: string): VecModel | null {
|
|
294
|
-
if (b64Points.length < POINT_B64_LENGTH) return null
|
|
295
|
-
return b64Vecs.decodePointAt(b64Points, 0)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Get the last point from a base64-encoded string of points.
|
|
300
|
-
*
|
|
301
|
-
* @param b64Points - The base64-encoded string containing point data
|
|
302
|
-
* @returns The last point as a VecModel, or null if the string is too short
|
|
303
|
-
*/
|
|
304
|
-
static decodeLastPoint(b64Points: string): VecModel | null {
|
|
305
|
-
if (b64Points.length < POINT_B64_LENGTH) return null
|
|
306
|
-
return b64Vecs.decodePointAt(b64Points, b64Points.length - POINT_B64_LENGTH)
|
|
307
|
-
}
|
|
308
|
-
}
|