@loaders.gl/ply 4.2.0-alpha.4 → 4.2.0-alpha.5

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.
@@ -1,319 +1,399 @@
1
1
  import normalizePLY from "./normalize-ply.js";
2
- export function parsePLY(data) {
3
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
4
- let header;
5
- let attributes;
6
- if (data instanceof ArrayBuffer) {
7
- const text = new TextDecoder().decode(data);
8
- header = parseHeader(text, options);
9
- attributes = header.format === 'ascii' ? parseASCII(text, header) : parseBinary(data, header);
10
- } else {
11
- header = parseHeader(data, options);
12
- attributes = parseASCII(data, header);
13
- }
14
- return normalizePLY(header, attributes);
2
+ /**
3
+ * @param data
4
+ * @param options
5
+ * @returns
6
+ */
7
+ export function parsePLY(data, options = {}) {
8
+ let header;
9
+ let attributes;
10
+ if (data instanceof ArrayBuffer) {
11
+ const text = new TextDecoder().decode(data);
12
+ header = parseHeader(text, options);
13
+ attributes = header.format === 'ascii' ? parseASCII(text, header) : parseBinary(data, header);
14
+ }
15
+ else {
16
+ header = parseHeader(data, options);
17
+ attributes = parseASCII(data, header);
18
+ }
19
+ return normalizePLY(header, attributes);
15
20
  }
21
+ /**
22
+ * @param data
23
+ * @param options
24
+ * @returns header
25
+ */
16
26
  function parseHeader(data, options) {
17
- const PLY_HEADER_PATTERN = /ply([\s\S]*)end_header\s/;
18
- let headerText = '';
19
- let headerLength = 0;
20
- const result = PLY_HEADER_PATTERN.exec(data);
21
- if (result !== null) {
22
- headerText = result[1];
23
- headerLength = result[0].length;
24
- }
25
- const lines = headerText.split('\n');
26
- const header = parseHeaderLines(lines, headerLength, options);
27
- return header;
27
+ const PLY_HEADER_PATTERN = /ply([\s\S]*)end_header\s/;
28
+ let headerText = '';
29
+ let headerLength = 0;
30
+ const result = PLY_HEADER_PATTERN.exec(data);
31
+ if (result !== null) {
32
+ headerText = result[1];
33
+ headerLength = result[0].length;
34
+ }
35
+ const lines = headerText.split('\n');
36
+ const header = parseHeaderLines(lines, headerLength, options);
37
+ return header;
28
38
  }
39
+ /**
40
+ * @param lines
41
+ * @param headerLength
42
+ * @param options
43
+ * @returns header
44
+ */
45
+ // eslint-disable-next-line complexity
29
46
  function parseHeaderLines(lines, headerLength, options) {
30
- const header = {
31
- comments: [],
32
- elements: [],
33
- headerLength
34
- };
35
- let lineType;
36
- let lineValues;
37
- let currentElement = null;
38
- for (let i = 0; i < lines.length; i++) {
39
- let line = lines[i];
40
- line = line.trim();
41
- if (line === '') {
42
- continue;
43
- }
44
- lineValues = line.split(/\s+/);
45
- lineType = lineValues.shift();
46
- line = lineValues.join(' ');
47
- switch (lineType) {
48
- case 'format':
49
- header.format = lineValues[0];
50
- header.version = lineValues[1];
51
- break;
52
- case 'comment':
53
- header.comments.push(line);
54
- break;
55
- case 'element':
56
- if (currentElement) {
57
- header.elements.push(currentElement);
47
+ const header = {
48
+ comments: [],
49
+ elements: [],
50
+ headerLength
51
+ };
52
+ let lineType;
53
+ let lineValues;
54
+ let currentElement = null;
55
+ for (let i = 0; i < lines.length; i++) {
56
+ let line = lines[i];
57
+ line = line.trim();
58
+ if (line === '') {
59
+ // eslint-disable-next-line
60
+ continue;
58
61
  }
59
- currentElement = {
60
- name: lineValues[0],
61
- count: parseInt(lineValues[1], 10),
62
- properties: []
63
- };
64
- break;
65
- case 'property':
66
- if (currentElement) {
67
- const property = makePLYElementProperty(lineValues);
68
- if (options !== null && options !== void 0 && options.propertyNameMapping && property.name in (options === null || options === void 0 ? void 0 : options.propertyNameMapping)) {
69
- property.name = options === null || options === void 0 ? void 0 : options.propertyNameMapping[property.name];
70
- }
71
- currentElement.properties.push(property);
62
+ lineValues = line.split(/\s+/);
63
+ lineType = lineValues.shift();
64
+ line = lineValues.join(' ');
65
+ switch (lineType) {
66
+ case 'format':
67
+ header.format = lineValues[0];
68
+ header.version = lineValues[1];
69
+ break;
70
+ case 'comment':
71
+ header.comments.push(line);
72
+ break;
73
+ case 'element':
74
+ // Start new element, store previous element
75
+ if (currentElement) {
76
+ header.elements.push(currentElement);
77
+ }
78
+ currentElement = {
79
+ name: lineValues[0],
80
+ count: parseInt(lineValues[1], 10),
81
+ properties: []
82
+ };
83
+ break;
84
+ case 'property':
85
+ if (currentElement) {
86
+ const property = makePLYElementProperty(lineValues);
87
+ if (options?.propertyNameMapping && property.name in options?.propertyNameMapping) {
88
+ property.name = options?.propertyNameMapping[property.name];
89
+ }
90
+ currentElement.properties.push(property);
91
+ }
92
+ break;
93
+ default:
94
+ // eslint-disable-next-line
95
+ console.log('unhandled', lineType, lineValues);
72
96
  }
73
- break;
74
- default:
75
- console.log('unhandled', lineType, lineValues);
76
97
  }
77
- }
78
- if (currentElement) {
79
- header.elements.push(currentElement);
80
- }
81
- return header;
98
+ // Store in-progress element
99
+ if (currentElement) {
100
+ header.elements.push(currentElement);
101
+ }
102
+ return header;
82
103
  }
104
+ /** Generate attributes arrays from the header */
105
+ // eslint-disable-next-line complexity
83
106
  function getPLYAttributes(header) {
84
- const attributes = {
85
- indices: [],
86
- vertices: [],
87
- normals: [],
88
- uvs: [],
89
- colors: []
90
- };
91
- for (const element of header.elements) {
92
- if (element.name === 'vertex') {
93
- for (const property of element.properties) {
94
- switch (property.name) {
95
- case 'x':
96
- case 'y':
97
- case 'z':
98
- case 'nx':
99
- case 'ny':
100
- case 'nz':
101
- case 's':
102
- case 't':
103
- case 'red':
104
- case 'green':
105
- case 'blue':
106
- break;
107
- default:
108
- attributes[property.name] = [];
109
- break;
107
+ // TODO Generate only the attribute arrays actually in the header
108
+ const attributes = {
109
+ indices: [],
110
+ vertices: [],
111
+ normals: [],
112
+ uvs: [],
113
+ colors: []
114
+ };
115
+ for (const element of header.elements) {
116
+ if (element.name === 'vertex') {
117
+ for (const property of element.properties) {
118
+ switch (property.name) {
119
+ case 'x':
120
+ case 'y':
121
+ case 'z':
122
+ case 'nx':
123
+ case 'ny':
124
+ case 'nz':
125
+ case 's':
126
+ case 't':
127
+ case 'red':
128
+ case 'green':
129
+ case 'blue':
130
+ break;
131
+ default:
132
+ // Add any non-geometry attributes
133
+ attributes[property.name] = [];
134
+ break;
135
+ }
136
+ }
110
137
  }
111
- }
112
138
  }
113
- }
114
- return attributes;
139
+ return attributes;
115
140
  }
141
+ /**
142
+ * @param propertyValues
143
+ * @returns property of ply element
144
+ */
116
145
  function makePLYElementProperty(propertyValues) {
117
- const type = propertyValues[0];
118
- switch (type) {
119
- case 'list':
120
- return {
121
- type,
122
- name: propertyValues[3],
123
- countType: propertyValues[1],
124
- itemType: propertyValues[2]
125
- };
126
- default:
127
- return {
128
- type,
129
- name: propertyValues[1]
130
- };
131
- }
146
+ const type = propertyValues[0];
147
+ switch (type) {
148
+ case 'list':
149
+ return {
150
+ type,
151
+ name: propertyValues[3],
152
+ countType: propertyValues[1],
153
+ itemType: propertyValues[2]
154
+ };
155
+ default:
156
+ return {
157
+ type,
158
+ name: propertyValues[1]
159
+ };
160
+ }
132
161
  }
162
+ /**
163
+ * Parses ASCII number
164
+ * @param n
165
+ * @param type
166
+ * @returns
167
+ */
168
+ // eslint-disable-next-line complexity
133
169
  function parseASCIINumber(n, type) {
134
- switch (type) {
135
- case 'char':
136
- case 'uchar':
137
- case 'short':
138
- case 'ushort':
139
- case 'int':
140
- case 'uint':
141
- case 'int8':
142
- case 'uint8':
143
- case 'int16':
144
- case 'uint16':
145
- case 'int32':
146
- case 'uint32':
147
- return parseInt(n, 10);
148
- case 'float':
149
- case 'double':
150
- case 'float32':
151
- case 'float64':
152
- return parseFloat(n);
153
- default:
154
- throw new Error(type);
155
- }
170
+ switch (type) {
171
+ case 'char':
172
+ case 'uchar':
173
+ case 'short':
174
+ case 'ushort':
175
+ case 'int':
176
+ case 'uint':
177
+ case 'int8':
178
+ case 'uint8':
179
+ case 'int16':
180
+ case 'uint16':
181
+ case 'int32':
182
+ case 'uint32':
183
+ return parseInt(n, 10);
184
+ case 'float':
185
+ case 'double':
186
+ case 'float32':
187
+ case 'float64':
188
+ return parseFloat(n);
189
+ default:
190
+ throw new Error(type);
191
+ }
156
192
  }
193
+ /**
194
+ * @param properties
195
+ * @param line
196
+ * @returns ASCII element
197
+ */
157
198
  function parsePLYElement(properties, line) {
158
- const values = line.split(/\s+/);
159
- const element = {};
160
- for (let i = 0; i < properties.length; i++) {
161
- if (properties[i].type === 'list') {
162
- const list = [];
163
- const n = parseASCIINumber(values.shift(), properties[i].countType);
164
- for (let j = 0; j < n; j++) {
165
- list.push(parseASCIINumber(values.shift(), properties[i].itemType));
166
- }
167
- element[properties[i].name] = list;
168
- } else {
169
- element[properties[i].name] = parseASCIINumber(values.shift(), properties[i].type);
199
+ const values = line.split(/\s+/);
200
+ const element = {};
201
+ for (let i = 0; i < properties.length; i++) {
202
+ if (properties[i].type === 'list') {
203
+ const list = [];
204
+ const n = parseASCIINumber(values.shift(), properties[i].countType);
205
+ for (let j = 0; j < n; j++) {
206
+ list.push(parseASCIINumber(values.shift(), properties[i].itemType));
207
+ }
208
+ element[properties[i].name] = list;
209
+ }
210
+ else {
211
+ element[properties[i].name] = parseASCIINumber(values.shift(), properties[i].type);
212
+ }
170
213
  }
171
- }
172
- return element;
214
+ return element;
173
215
  }
216
+ /**
217
+ * @param data
218
+ * @param header
219
+ * @returns [attributes]
220
+ */
174
221
  function parseASCII(data, header) {
175
- const attributes = getPLYAttributes(header);
176
- let result;
177
- const patternBody = /end_header\s([\s\S]*)$/;
178
- let body = '';
179
- if ((result = patternBody.exec(data)) !== null) {
180
- body = result[1];
181
- }
182
- const lines = body.split('\n');
183
- let currentElement = 0;
184
- let currentElementCount = 0;
185
- for (let i = 0; i < lines.length; i++) {
186
- let line = lines[i];
187
- line = line.trim();
188
- if (line !== '') {
189
- if (currentElementCount >= header.elements[currentElement].count) {
190
- currentElement++;
191
- currentElementCount = 0;
192
- }
193
- const element = parsePLYElement(header.elements[currentElement].properties, line);
194
- handleElement(attributes, header.elements[currentElement].name, element);
195
- currentElementCount++;
222
+ // PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
223
+ const attributes = getPLYAttributes(header);
224
+ let result;
225
+ const patternBody = /end_header\s([\s\S]*)$/;
226
+ let body = '';
227
+ if ((result = patternBody.exec(data)) !== null) {
228
+ body = result[1];
196
229
  }
197
- }
198
- return attributes;
230
+ const lines = body.split('\n');
231
+ let currentElement = 0;
232
+ let currentElementCount = 0;
233
+ for (let i = 0; i < lines.length; i++) {
234
+ let line = lines[i];
235
+ line = line.trim();
236
+ if (line !== '') {
237
+ if (currentElementCount >= header.elements[currentElement].count) {
238
+ currentElement++;
239
+ currentElementCount = 0;
240
+ }
241
+ const element = parsePLYElement(header.elements[currentElement].properties, line);
242
+ handleElement(attributes, header.elements[currentElement].name, element);
243
+ currentElementCount++;
244
+ }
245
+ }
246
+ return attributes;
199
247
  }
200
- function handleElement(buffer, elementName) {
201
- let element = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
202
- if (elementName === 'vertex') {
203
- for (const propertyName of Object.keys(element)) {
204
- switch (propertyName) {
205
- case 'x':
206
- buffer.vertices.push(element.x, element.y, element.z);
207
- break;
208
- case 'y':
209
- case 'z':
210
- break;
211
- case 'nx':
212
- if ('nx' in element && 'ny' in element && 'nz' in element) {
213
- buffer.normals.push(element.nx, element.ny, element.nz);
214
- }
215
- break;
216
- case 'ny':
217
- case 'nz':
218
- break;
219
- case 's':
220
- if ('s' in element && 't' in element) {
221
- buffer.uvs.push(element.s, element.t);
222
- }
223
- break;
224
- case 't':
225
- break;
226
- case 'red':
227
- if ('red' in element && 'green' in element && 'blue' in element) {
228
- buffer.colors.push(element.red, element.green, element.blue);
229
- }
230
- break;
231
- case 'green':
232
- case 'blue':
233
- break;
234
- default:
235
- buffer[propertyName].push(element[propertyName]);
236
- }
248
+ /**
249
+ * @param buffer
250
+ * @param elementName
251
+ * @param element
252
+ */
253
+ // eslint-disable-next-line complexity
254
+ function handleElement(buffer, elementName, element = {}) {
255
+ if (elementName === 'vertex') {
256
+ for (const propertyName of Object.keys(element)) {
257
+ switch (propertyName) {
258
+ case 'x':
259
+ buffer.vertices.push(element.x, element.y, element.z);
260
+ break;
261
+ case 'y':
262
+ case 'z':
263
+ break;
264
+ case 'nx':
265
+ if ('nx' in element && 'ny' in element && 'nz' in element) {
266
+ buffer.normals.push(element.nx, element.ny, element.nz);
267
+ }
268
+ break;
269
+ case 'ny':
270
+ case 'nz':
271
+ break;
272
+ case 's':
273
+ if ('s' in element && 't' in element) {
274
+ buffer.uvs.push(element.s, element.t);
275
+ }
276
+ break;
277
+ case 't':
278
+ break;
279
+ case 'red':
280
+ if ('red' in element && 'green' in element && 'blue' in element) {
281
+ buffer.colors.push(element.red, element.green, element.blue);
282
+ }
283
+ break;
284
+ case 'green':
285
+ case 'blue':
286
+ break;
287
+ default:
288
+ buffer[propertyName].push(element[propertyName]);
289
+ }
290
+ }
237
291
  }
238
- } else if (elementName === 'face') {
239
- const vertexIndices = element.vertex_indices || element.vertex_index;
240
- if (vertexIndices.length === 3) {
241
- buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[2]);
242
- } else if (vertexIndices.length === 4) {
243
- buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[3]);
244
- buffer.indices.push(vertexIndices[1], vertexIndices[2], vertexIndices[3]);
292
+ else if (elementName === 'face') {
293
+ const vertexIndices = element.vertex_indices || element.vertex_index; // issue #9338
294
+ if (vertexIndices.length === 3) {
295
+ buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[2]);
296
+ }
297
+ else if (vertexIndices.length === 4) {
298
+ buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[3]);
299
+ buffer.indices.push(vertexIndices[1], vertexIndices[2], vertexIndices[3]);
300
+ }
245
301
  }
246
- }
247
302
  }
303
+ /**
304
+ * Reads binary data
305
+ * @param dataview
306
+ * @param at
307
+ * @param type
308
+ * @param littleEndian
309
+ * @returns [number, number]
310
+ */
311
+ // eslint-disable-next-line complexity
248
312
  function binaryRead(dataview, at, type, littleEndian) {
249
- switch (type) {
250
- case 'int8':
251
- case 'char':
252
- return [dataview.getInt8(at), 1];
253
- case 'uint8':
254
- case 'uchar':
255
- return [dataview.getUint8(at), 1];
256
- case 'int16':
257
- case 'short':
258
- return [dataview.getInt16(at, littleEndian), 2];
259
- case 'uint16':
260
- case 'ushort':
261
- return [dataview.getUint16(at, littleEndian), 2];
262
- case 'int32':
263
- case 'int':
264
- return [dataview.getInt32(at, littleEndian), 4];
265
- case 'uint32':
266
- case 'uint':
267
- return [dataview.getUint32(at, littleEndian), 4];
268
- case 'float32':
269
- case 'float':
270
- return [dataview.getFloat32(at, littleEndian), 4];
271
- case 'float64':
272
- case 'double':
273
- return [dataview.getFloat64(at, littleEndian), 8];
274
- default:
275
- throw new Error(type);
276
- }
313
+ switch (type) {
314
+ // corespondences for non-specific length types here match rply:
315
+ case 'int8':
316
+ case 'char':
317
+ return [dataview.getInt8(at), 1];
318
+ case 'uint8':
319
+ case 'uchar':
320
+ return [dataview.getUint8(at), 1];
321
+ case 'int16':
322
+ case 'short':
323
+ return [dataview.getInt16(at, littleEndian), 2];
324
+ case 'uint16':
325
+ case 'ushort':
326
+ return [dataview.getUint16(at, littleEndian), 2];
327
+ case 'int32':
328
+ case 'int':
329
+ return [dataview.getInt32(at, littleEndian), 4];
330
+ case 'uint32':
331
+ case 'uint':
332
+ return [dataview.getUint32(at, littleEndian), 4];
333
+ case 'float32':
334
+ case 'float':
335
+ return [dataview.getFloat32(at, littleEndian), 4];
336
+ case 'float64':
337
+ case 'double':
338
+ return [dataview.getFloat64(at, littleEndian), 8];
339
+ default:
340
+ throw new Error(type);
341
+ }
277
342
  }
343
+ /**
344
+ * Reads binary data
345
+ * @param dataview
346
+ * @param at
347
+ * @param properties
348
+ * @param littleEndian
349
+ * @returns [object, number]
350
+ */
278
351
  function binaryReadElement(dataview, at, properties, littleEndian) {
279
- const element = {};
280
- let result;
281
- let read = 0;
282
- for (let i = 0; i < properties.length; i++) {
283
- if (properties[i].type === 'list') {
284
- const list = [];
285
- result = binaryRead(dataview, at + read, properties[i].countType, littleEndian);
286
- const n = result[0];
287
- read += result[1];
288
- for (let j = 0; j < n; j++) {
289
- result = binaryRead(dataview, at + read, properties[i].itemType, littleEndian);
290
- list.push(result[0]);
291
- read += result[1];
292
- }
293
- element[properties[i].name] = list;
294
- } else {
295
- result = binaryRead(dataview, at + read, properties[i].type, littleEndian);
296
- element[properties[i].name] = result[0];
297
- read += result[1];
352
+ const element = {};
353
+ let result;
354
+ let read = 0;
355
+ for (let i = 0; i < properties.length; i++) {
356
+ if (properties[i].type === 'list') {
357
+ const list = [];
358
+ result = binaryRead(dataview, at + read, properties[i].countType, littleEndian);
359
+ const n = result[0];
360
+ read += result[1];
361
+ for (let j = 0; j < n; j++) {
362
+ result = binaryRead(dataview, at + read, properties[i].itemType, littleEndian);
363
+ // @ts-ignore
364
+ list.push(result[0]);
365
+ read += result[1];
366
+ }
367
+ element[properties[i].name] = list;
368
+ }
369
+ else {
370
+ result = binaryRead(dataview, at + read, properties[i].type, littleEndian);
371
+ element[properties[i].name] = result[0];
372
+ read += result[1];
373
+ }
298
374
  }
299
- }
300
- return [element, read];
375
+ return [element, read];
301
376
  }
377
+ /**
378
+ * Parses binary data
379
+ * @param data
380
+ * @param header
381
+ * @returns [attributes] of data
382
+ */
302
383
  function parseBinary(data, header) {
303
- const attributes = getPLYAttributes(header);
304
- const littleEndian = header.format === 'binary_little_endian';
305
- const body = new DataView(data, header.headerLength);
306
- let result;
307
- let loc = 0;
308
- for (let currentElement = 0; currentElement < header.elements.length; currentElement++) {
309
- const count = header.elements[currentElement].count;
310
- for (let currentElementCount = 0; currentElementCount < count; currentElementCount++) {
311
- result = binaryReadElement(body, loc, header.elements[currentElement].properties, littleEndian);
312
- loc += result[1];
313
- const element = result[0];
314
- handleElement(attributes, header.elements[currentElement].name, element);
384
+ const attributes = getPLYAttributes(header);
385
+ const littleEndian = header.format === 'binary_little_endian';
386
+ const body = new DataView(data, header.headerLength);
387
+ let result;
388
+ let loc = 0;
389
+ for (let currentElement = 0; currentElement < header.elements.length; currentElement++) {
390
+ const count = header.elements[currentElement].count;
391
+ for (let currentElementCount = 0; currentElementCount < count; currentElementCount++) {
392
+ result = binaryReadElement(body, loc, header.elements[currentElement].properties, littleEndian);
393
+ loc += result[1];
394
+ const element = result[0];
395
+ handleElement(attributes, header.elements[currentElement].name, element);
396
+ }
315
397
  }
316
- }
317
- return attributes;
398
+ return attributes;
318
399
  }
319
- //# sourceMappingURL=parse-ply.js.map