@ifc-lite/pointcloud 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +28 -0
  2. package/dist/formats/ascii-points.d.ts +61 -0
  3. package/dist/formats/ascii-points.d.ts.map +1 -0
  4. package/dist/formats/ascii-points.js +243 -0
  5. package/dist/formats/ascii-points.js.map +1 -0
  6. package/dist/formats/e57-decode.d.ts +28 -0
  7. package/dist/formats/e57-decode.d.ts.map +1 -0
  8. package/dist/formats/e57-decode.js +425 -0
  9. package/dist/formats/e57-decode.js.map +1 -0
  10. package/dist/formats/e57-page.d.ts +42 -0
  11. package/dist/formats/e57-page.d.ts.map +1 -0
  12. package/dist/formats/e57-page.js +107 -0
  13. package/dist/formats/e57-page.js.map +1 -0
  14. package/dist/formats/e57-xml.d.ts +55 -0
  15. package/dist/formats/e57-xml.d.ts.map +1 -0
  16. package/dist/formats/e57-xml.js +127 -0
  17. package/dist/formats/e57-xml.js.map +1 -0
  18. package/dist/formats/e57.d.ts +29 -117
  19. package/dist/formats/e57.d.ts.map +1 -1
  20. package/dist/formats/e57.js +51 -444
  21. package/dist/formats/e57.js.map +1 -1
  22. package/dist/formats/ifcx-points.js +13 -1
  23. package/dist/formats/ifcx-points.js.map +1 -1
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/streaming/ascii-points-source.d.ts +31 -0
  29. package/dist/streaming/ascii-points-source.d.ts.map +1 -0
  30. package/dist/streaming/ascii-points-source.js +83 -0
  31. package/dist/streaming/ascii-points-source.js.map +1 -0
  32. package/dist/streaming/decode-worker.js +41 -18
  33. package/dist/streaming/decode-worker.js.map +1 -1
  34. package/dist/streaming/e57-source.js +6 -1
  35. package/dist/streaming/e57-source.js.map +1 -1
  36. package/dist/streaming/inline-worker.d.ts +21 -0
  37. package/dist/streaming/inline-worker.d.ts.map +1 -0
  38. package/dist/streaming/inline-worker.js +5 -0
  39. package/dist/streaming/inline-worker.js.map +1 -0
  40. package/dist/streaming/laz-source.d.ts.map +1 -1
  41. package/dist/streaming/laz-source.js +33 -1
  42. package/dist/streaming/laz-source.js.map +1 -1
  43. package/dist/streaming/protocol.d.ts +1 -1
  44. package/dist/streaming/protocol.d.ts.map +1 -1
  45. package/dist/streaming/worker-client.d.ts +16 -3
  46. package/dist/streaming/worker-client.d.ts.map +1 -1
  47. package/dist/streaming/worker-client.js +77 -23
  48. package/dist/streaming/worker-client.js.map +1 -1
  49. package/package.json +3 -2
@@ -0,0 +1,55 @@
1
+ export interface PrototypeField {
2
+ name: string;
3
+ kind: 'Float' | 'ScaledInteger' | 'Integer';
4
+ precision?: 'single' | 'double';
5
+ scale?: number;
6
+ offset?: number;
7
+ minimum?: number;
8
+ maximum?: number;
9
+ }
10
+ /**
11
+ * Per-scan pose: rotation (unit quaternion w + xi + yj + zk) +
12
+ * translation (in source-frame metres). Optional because most
13
+ * single-scan exports don't need one — when absent we treat the
14
+ * scan as already in the file's global frame (identity pose).
15
+ */
16
+ export interface E57Pose {
17
+ rotation: {
18
+ w: number;
19
+ x: number;
20
+ y: number;
21
+ z: number;
22
+ };
23
+ translation: {
24
+ x: number;
25
+ y: number;
26
+ z: number;
27
+ };
28
+ }
29
+ export interface Data3DEntry {
30
+ guid: string;
31
+ name?: string;
32
+ recordCount: number;
33
+ /** Logical offset into the file where the binary section begins. */
34
+ binaryFileOffset: number;
35
+ /** Field declarations in record order. */
36
+ prototype: PrototypeField[];
37
+ /**
38
+ * Per-Data3D pose that places the scan into the file's global
39
+ * frame. Applied by `decodeE57` before merging multi-scan files;
40
+ * single-scan files where the pose is identity / absent are no-ops.
41
+ */
42
+ pose?: E57Pose;
43
+ }
44
+ /**
45
+ * Parse the E57 XML section.
46
+ *
47
+ * Uses our own minimal SAX-style parser (`xml-mini.ts`) instead of
48
+ * `DOMParser` because dedicated Web Workers — where the decode runs —
49
+ * don't expose DOMParser. The shape we need (e57Root → data3D →
50
+ * vectorChild → prototype) is shallow and attribute-heavy, well within
51
+ * the mini parser's scope.
52
+ */
53
+ export declare function parseE57Xml(xmlText: string): Data3DEntry[];
54
+ export declare function findField(proto: PrototypeField[], name: string): PrototypeField | undefined;
55
+ //# sourceMappingURL=e57-xml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e57-xml.d.ts","sourceRoot":"","sources":["../../src/formats/e57-xml.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAAC;IAC5C,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,WAAW,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAmE1D;AA0BD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE3F"}
@@ -0,0 +1,127 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * E57 XML model + parser.
6
+ *
7
+ * The XML carries the `data3D` structure: per-scan record counts, the
8
+ * binary CompressedVector fileOffset, and the prototype field
9
+ * declarations (`Float`, `Integer`, `ScaledInteger`) that describe the
10
+ * binary record layout.
11
+ */
12
+ import { childByName, childrenByName, parseXml, textChild, } from '../xml-mini.js';
13
+ /**
14
+ * Parse the E57 XML section.
15
+ *
16
+ * Uses our own minimal SAX-style parser (`xml-mini.ts`) instead of
17
+ * `DOMParser` because dedicated Web Workers — where the decode runs —
18
+ * don't expose DOMParser. The shape we need (e57Root → data3D →
19
+ * vectorChild → prototype) is shallow and attribute-heavy, well within
20
+ * the mini parser's scope.
21
+ */
22
+ export function parseE57Xml(xmlText) {
23
+ const root = parseXml(xmlText);
24
+ if (root.name !== 'e57Root') {
25
+ throw new Error(`E57: XML root is not <e57Root> (saw <${root.name || '?'}>)`);
26
+ }
27
+ const data3D = childByName(root, 'data3D');
28
+ if (!data3D)
29
+ return [];
30
+ const entries = [];
31
+ for (const scan of childrenByName(data3D, 'vectorChild')) {
32
+ const points = childByName(scan, 'points');
33
+ if (!points)
34
+ continue;
35
+ if (points.attrs.get('type') !== 'CompressedVector') {
36
+ // Skip non-compressed-vector points (rare).
37
+ continue;
38
+ }
39
+ const fileOffsetAttr = points.attrs.get('fileOffset');
40
+ const recordCountAttr = points.attrs.get('recordCount');
41
+ if (!fileOffsetAttr || !recordCountAttr)
42
+ continue;
43
+ // Reject NaN / negative parses up front. Without this guard a
44
+ // malformed XML attribute (e.g. fileOffset="-1" or
45
+ // recordCount="bogus") would flow into decodeE57Scan and either
46
+ // overrun the bytestream walk or allocate a zero-byte position
47
+ // buffer — neither produces a useful diagnostic.
48
+ const binaryFileOffset = Number(fileOffsetAttr);
49
+ const recordCount = Number(recordCountAttr);
50
+ if (!Number.isFinite(binaryFileOffset) || binaryFileOffset < 0)
51
+ continue;
52
+ if (!Number.isFinite(recordCount) || recordCount < 0)
53
+ continue;
54
+ const proto = childByName(points, 'prototype');
55
+ if (!proto)
56
+ continue;
57
+ const fields = [];
58
+ for (const f of proto.children) {
59
+ const type = f.attrs.get('type') ?? '';
60
+ if (type === 'Float') {
61
+ fields.push({
62
+ name: f.name,
63
+ kind: 'Float',
64
+ precision: f.attrs.get('precision') === 'single' ? 'single' : 'double',
65
+ });
66
+ }
67
+ else if (type === 'ScaledInteger') {
68
+ fields.push({
69
+ name: f.name,
70
+ kind: 'ScaledInteger',
71
+ scale: Number(f.attrs.get('scale') ?? '1'),
72
+ offset: Number(f.attrs.get('offset') ?? '0'),
73
+ minimum: Number(f.attrs.get('minimum') ?? '0'),
74
+ maximum: Number(f.attrs.get('maximum') ?? '0'),
75
+ });
76
+ }
77
+ else if (type === 'Integer') {
78
+ fields.push({
79
+ name: f.name,
80
+ kind: 'Integer',
81
+ minimum: Number(f.attrs.get('minimum') ?? '0'),
82
+ maximum: Number(f.attrs.get('maximum') ?? '0'),
83
+ });
84
+ }
85
+ // Other types (e.g. String) ignored — never carry point data.
86
+ }
87
+ entries.push({
88
+ guid: textChild(scan, 'guid') ?? '',
89
+ name: textChild(scan, 'name') ?? undefined,
90
+ recordCount,
91
+ binaryFileOffset,
92
+ prototype: fields,
93
+ pose: parsePoseElement(childByName(scan, 'pose')) ?? undefined,
94
+ });
95
+ }
96
+ return entries;
97
+ }
98
+ /**
99
+ * Parse a `<pose>` element to a quaternion + translation pair.
100
+ * Returns null when the element is missing or malformed (any field
101
+ * unparseable → fall back to identity rather than reject the file).
102
+ */
103
+ function parsePoseElement(poseEl) {
104
+ if (!poseEl)
105
+ return null;
106
+ const rotation = childByName(poseEl, 'rotation');
107
+ const translation = childByName(poseEl, 'translation');
108
+ if (!rotation || !translation)
109
+ return null;
110
+ const qw = Number(textChild(rotation, 'w') ?? '1');
111
+ const qx = Number(textChild(rotation, 'x') ?? '0');
112
+ const qy = Number(textChild(rotation, 'y') ?? '0');
113
+ const qz = Number(textChild(rotation, 'z') ?? '0');
114
+ const tx = Number(textChild(translation, 'x') ?? '0');
115
+ const ty = Number(textChild(translation, 'y') ?? '0');
116
+ const tz = Number(textChild(translation, 'z') ?? '0');
117
+ if (![qw, qx, qy, qz, tx, ty, tz].every(Number.isFinite))
118
+ return null;
119
+ return {
120
+ rotation: { w: qw, x: qx, y: qy, z: qz },
121
+ translation: { x: tx, y: ty, z: tz },
122
+ };
123
+ }
124
+ export function findField(proto, name) {
125
+ return proto.find((p) => p.name === name);
126
+ }
127
+ //# sourceMappingURL=e57-xml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e57-xml.js","sourceRoot":"","sources":["../../src/formats/e57-xml.ts"],"names":[],"mappings":"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"}
@@ -1,129 +1,41 @@
1
1
  /**
2
- * E57 (ASTM E2807-11) reader — point cloud subset.
2
+ * E57 (ASTM E2807-11) reader — top-level orchestrator.
3
3
  *
4
- * Scope:
5
- * - File header (48 bytes) magic + xmlPhysicalOffset/Length + pageSize.
6
- * - Physical logical view: every 1024-byte page ends with a 4-byte
7
- * CRC32-C; we strip those to get the logical byte stream the XML +
8
- * binary indices reference. CRCs are NOT validated (faster + still
9
- * correct for well-formed files).
10
- * - XML parsed with DOMParser to find Data3D entries with FloatNodes
11
- * for cartesianX / cartesianY / cartesianZ and optional colorRed /
12
- * colorGreen / colorBlue / intensity.
13
- * - Binary section: walk DataPackets at the prototype's CompressedVector
14
- * fileOffset, decode bytestreams as raw Float32 / Float64 columns.
15
- *
16
- * What we don't yet support:
17
- * - ScaledIntegerNode encoding (bit-packed integers with scale/offset)
18
- * — this is the more compact format; we throw a clear error so the
19
- * caller can guide the user to a Float-encoded export.
20
- * - Spherical coordinates (most files use cartesian).
21
- * - Per-scan pose transforms — points come back in scan-local space.
4
+ * Pulls the file-header / page-CRC handling from `e57-page.ts`, the
5
+ * XML model from `e57-xml.ts`, and the per-scan binary decoder from
6
+ * `e57-decode.ts`. Re-exports the public surface so existing callers
7
+ * (`@ifc-lite/pointcloud` index, the streaming source, tests) keep
8
+ * working.
22
9
  *
23
- * That subset still covers a large fraction of real-world E57 files
24
- * (Faro, Leica, Trimble, generic exports) and gives a clean error
25
- * message on the rest.
10
+ * Scope:
11
+ * - Single-scan + multi-scan files. Per-Data3D pose (quaternion +
12
+ * translation) is applied before merging so registered scans
13
+ * line up in the file's global frame.
14
+ * - Float (single/double) AND ScaledInteger (bit-packed integer
15
+ * with scale/offset per E57 §6.3.4) for cartesian fields.
16
+ * - Integer / Float / ScaledInteger colour + intensity channels.
17
+ *
18
+ * Out of scope (deferred — see issue #611):
19
+ * - Spherical coordinate prototypes.
26
20
  */
27
21
  import type { DecodedPointChunk } from '../types.js';
28
- export interface E57FileHeader {
29
- majorVersion: number;
30
- minorVersion: number;
31
- fileLogicalSize: number;
32
- xmlLogicalOffset: number;
33
- xmlLogicalLength: number;
34
- pageSize: number;
35
- }
36
- /** Read the 48-byte FileHeader. Throws on bad magic. */
37
- export declare function parseE57FileHeader(bytes: Uint8Array): E57FileHeader;
38
- /**
39
- * Strip the 4-byte CRC tail from each `pageSize`-byte physical page.
40
- *
41
- * Returns a freshly-allocated buffer of "logical" bytes — the form that
42
- * XML offsets and CompressedVector data offsets reference.
43
- *
44
- * `pageSize` is read from the header and is conventionally 1024.
45
- */
46
- export declare function stripPageCrc(bytes: Uint8Array, pageSize: number): Uint8Array;
47
- /**
48
- * Read a CompressedVector binary-section header (E57 spec §6.4.2) and
49
- * return the LOGICAL byte offset where its DataPackets actually start.
50
- *
51
- * Layout (32 bytes):
52
- * [ 0] u8 sectionId (must == 1 for CompressedVector)
53
- * [ 1] u8[7] reserved
54
- * [ 8] u64 LE sectionLogicalLength
55
- * [16] u64 LE dataPhysicalOffset
56
- * [24] u64 LE indexPhysicalOffset
57
- *
58
- * The XML's `points@fileOffset` points at this section header — NOT at
59
- * the first DataPacket. Reading packets straight at `fileOffset` puts
60
- * the parser ~32 bytes off and the first u16 it reads is the low half
61
- * of `sectionLogicalLength`, which usually decodes as a bytestreamCount
62
- * of 0 (matched the user-reported `bytestreamCount (0) ≠ prototype
63
- * length (7)` error exactly).
64
- */
65
- export declare function resolveCompressedVectorDataOffset(logical: Uint8Array, physicalSectionOffset: number, pageSize: number): number;
66
- interface PrototypeField {
67
- name: string;
68
- kind: 'Float' | 'ScaledInteger' | 'Integer';
69
- precision?: 'single' | 'double';
70
- scale?: number;
71
- offset?: number;
72
- minimum?: number;
73
- maximum?: number;
74
- }
75
- export interface Data3DEntry {
76
- guid: string;
77
- name?: string;
78
- recordCount: number;
79
- /** Logical offset into the file where the binary section begins. */
80
- binaryFileOffset: number;
81
- /** Field declarations in record order. */
82
- prototype: PrototypeField[];
83
- /**
84
- * Whether this Data3D defines a `pose` element (translation +
85
- * rotation that places the scan in the file's global frame). We
86
- * don't apply the transform yet — single-scan files don't need it,
87
- * and multi-scan files with poses are rejected upfront so we never
88
- * silently merge in scan-local space.
89
- */
90
- hasPose?: boolean;
91
- }
92
- /**
93
- * Parse the E57 XML section.
94
- *
95
- * Uses our own minimal SAX-style parser (`xml-mini.ts`) instead of
96
- * `DOMParser` because dedicated Web Workers — where the decode runs —
97
- * don't expose DOMParser. The shape we need (e57Root → data3D →
98
- * vectorChild → prototype) is shallow and attribute-heavy, well within
99
- * the mini parser's scope.
100
- */
101
- export declare function parseE57Xml(xmlText: string): Data3DEntry[];
102
- /**
103
- * Decode the binary section starting at `entry.binaryFileOffset` in the
104
- * logical-bytes view. NOTE: `binaryFileOffset` here must already point
105
- * at the first DataPacket (i.e. AFTER the 32-byte CompressedVector
106
- * section header) — `decodeE57` does this conversion via
107
- * `resolveCompressedVectorDataOffset`. Callers passing the raw XML
108
- * offset directly will see a "bytestreamCount ≠ prototype length"
109
- * mismatch.
110
- *
111
- * Returns one DecodedPointChunk per scan; caller can concatenate or
112
- * emit them as separate streaming chunks.
113
- *
114
- * Limitations (Phase-1 E57):
115
- * - Only Float (single/double) prototype fields are decoded. Files
116
- * using ScaledInteger throw a clear error so the host can fall back
117
- * gracefully.
118
- * - Reads only cartesianX/Y/Z + colorRed/Green/Blue + intensity when
119
- * present. Other fields are honoured for stride math but discarded.
120
- */
121
- export declare function decodeE57Scan(logical: Uint8Array, entry: Data3DEntry): DecodedPointChunk;
22
+ import { type E57Pose } from './e57-xml.js';
122
23
  /**
123
24
  * Decode all Data3D scans in an E57 file. Combines them into a single
124
25
  * DecodedPointChunk (positions concatenated). Returns null when the
125
26
  * file has no scans.
126
27
  */
127
28
  export declare function decodeE57(bytes: Uint8Array): DecodedPointChunk | null;
128
- export {};
29
+ /**
30
+ * Apply a per-scan pose (rotation quaternion + translation) to a
31
+ * positions buffer in place: `out = R · in + T`.
32
+ *
33
+ * Quaternion is in Hamilton convention (w + xi + yj + zk); we derive
34
+ * the 3×3 rotation matrix once and reuse across every point in the
35
+ * chunk. Translation is added after rotation.
36
+ */
37
+ export declare function applyPoseInPlace(positions: Float32Array, pointCount: number, pose: E57Pose): void;
38
+ export { parseE57FileHeader, stripPageCrc, resolveCompressedVectorDataOffset, type E57FileHeader, } from './e57-page.js';
39
+ export { parseE57Xml, type Data3DEntry, type E57Pose, } from './e57-xml.js';
40
+ export { decodeE57Scan } from './e57-decode.js';
129
41
  //# sourceMappingURL=e57.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"e57.d.ts","sourceRoot":"","sources":["../../src/formats/e57.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAkB,MAAM,aAAa,CAAC;AAUrE,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAiBnE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAoB5E;AAUD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,UAAU,EACnB,qBAAqB,EAAE,MAAM,EAC7B,QAAQ,EAAE,MAAM,GACf,MAAM,CAgBR;AAID,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAAC;IAC5C,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CA0D1D;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,iBAAiB,CAqLxF;AAmFD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CA+DrE"}
1
+ {"version":3,"file":"e57.d.ts","sourceRoot":"","sources":["../../src/formats/e57.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAKzD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAsDrE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,YAAY,EACvB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,GACZ,IAAI,CAsBN;AAGD,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,iCAAiC,EACjC,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}