@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,86 +1,77 @@
1
1
  import { getMeshBoundingBox } from '@loaders.gl/schema';
2
2
  import { getPLYSchema } from "./get-ply-schema.js";
3
+ /**
4
+ * @param header
5
+ * @param attributes
6
+ * @returns data and header
7
+ */
3
8
  export default function normalizePLY(plyHeader, plyAttributes, options) {
4
- const attributes = getMeshAttributes(plyAttributes);
5
- const boundingBox = getMeshBoundingBox(attributes);
6
- const vertexCount = plyAttributes.indices.length || plyAttributes.vertices.length / 3;
7
- const isTriangles = plyAttributes.indices && plyAttributes.indices.length > 0;
8
- const mode = isTriangles ? 4 : 0;
9
- const topology = isTriangles ? 'triangle-list' : 'point-list';
10
- const schema = getPLYSchema(plyHeader, attributes);
11
- const plyMesh = {
12
- loader: 'ply',
13
- loaderData: plyHeader,
14
- header: {
15
- vertexCount,
16
- boundingBox
17
- },
18
- schema,
19
- attributes,
20
- indices: {
21
- value: new Uint32Array(0),
22
- size: 0
23
- },
24
- mode,
25
- topology
26
- };
27
- if (plyAttributes.indices.length > 0) {
28
- plyMesh.indices = {
29
- value: new Uint32Array(plyAttributes.indices),
30
- size: 1
9
+ const attributes = getMeshAttributes(plyAttributes);
10
+ const boundingBox = getMeshBoundingBox(attributes);
11
+ const vertexCount = plyAttributes.indices.length || plyAttributes.vertices.length / 3;
12
+ // TODO - how to detect POINT CLOUDS vs MESHES?
13
+ // TODO - For Meshes, PLY quadrangles must be split?
14
+ const isTriangles = plyAttributes.indices && plyAttributes.indices.length > 0;
15
+ const mode = isTriangles ? 4 : 0; // TRIANGLES vs POINTS
16
+ const topology = isTriangles ? 'triangle-list' : 'point-list';
17
+ const schema = getPLYSchema(plyHeader, attributes);
18
+ const plyMesh = {
19
+ loader: 'ply',
20
+ loaderData: plyHeader,
21
+ header: {
22
+ vertexCount,
23
+ boundingBox
24
+ },
25
+ schema,
26
+ attributes,
27
+ indices: { value: new Uint32Array(0), size: 0 },
28
+ mode,
29
+ topology
31
30
  };
32
- }
33
- return plyMesh;
31
+ if (plyAttributes.indices.length > 0) {
32
+ plyMesh.indices = { value: new Uint32Array(plyAttributes.indices), size: 1 };
33
+ }
34
+ return plyMesh;
34
35
  }
36
+ /**
37
+ * @param attributes
38
+ * @returns accessors []
39
+ */
40
+ // eslint-disable-next-line complexity
35
41
  function getMeshAttributes(attributes) {
36
- const accessors = {};
37
- for (const attributeName of Object.keys(attributes)) {
38
- switch (attributeName) {
39
- case 'vertices':
40
- if (attributes.vertices.length > 0) {
41
- accessors.POSITION = {
42
- value: new Float32Array(attributes.vertices),
43
- size: 3
44
- };
45
- }
46
- break;
47
- case 'normals':
48
- if (attributes.normals.length > 0) {
49
- accessors.NORMAL = {
50
- value: new Float32Array(attributes.normals),
51
- size: 3
52
- };
53
- }
54
- break;
55
- case 'uvs':
56
- if (attributes.uvs.length > 0) {
57
- accessors.TEXCOORD_0 = {
58
- value: new Float32Array(attributes.uvs),
59
- size: 2
60
- };
61
- }
62
- break;
63
- case 'colors':
64
- if (attributes.colors.length > 0) {
65
- accessors.COLOR_0 = {
66
- value: new Uint8Array(attributes.colors),
67
- size: 3,
68
- normalized: true
69
- };
70
- }
71
- break;
72
- case 'indices':
73
- break;
74
- default:
75
- if (attributes[attributeName].length > 0) {
76
- accessors[attributeName] = {
77
- value: new Float32Array(attributes[attributeName]),
78
- size: 1
79
- };
42
+ const accessors = {};
43
+ for (const attributeName of Object.keys(attributes)) {
44
+ switch (attributeName) {
45
+ case 'vertices':
46
+ if (attributes.vertices.length > 0) {
47
+ accessors.POSITION = { value: new Float32Array(attributes.vertices), size: 3 };
48
+ }
49
+ break;
50
+ // optional attributes data
51
+ case 'normals':
52
+ if (attributes.normals.length > 0) {
53
+ accessors.NORMAL = { value: new Float32Array(attributes.normals), size: 3 };
54
+ }
55
+ break;
56
+ case 'uvs':
57
+ if (attributes.uvs.length > 0) {
58
+ accessors.TEXCOORD_0 = { value: new Float32Array(attributes.uvs), size: 2 };
59
+ }
60
+ break;
61
+ case 'colors':
62
+ if (attributes.colors.length > 0) {
63
+ // TODO - normalized shoud be based on `uchar` flag in source data?
64
+ accessors.COLOR_0 = { value: new Uint8Array(attributes.colors), size: 3, normalized: true };
65
+ }
66
+ break;
67
+ case 'indices':
68
+ break;
69
+ default:
70
+ if (attributes[attributeName].length > 0) {
71
+ accessors[attributeName] = { value: new Float32Array(attributes[attributeName]), size: 1 };
72
+ }
73
+ break;
80
74
  }
81
- break;
82
75
  }
83
- }
84
- return accessors;
76
+ return accessors;
85
77
  }
86
- //# sourceMappingURL=normalize-ply.js.map
@@ -1,4 +1,4 @@
1
- import { PLYMesh } from './ply-types';
1
+ import { PLYMesh } from "./ply-types.js";
2
2
  /**
3
3
  * PARSER
4
4
  * @param iterator
@@ -1 +1 @@
1
- {"version":3,"file":"parse-ply-in-batches.d.ts","sourceRoot":"","sources":["../../src/lib/parse-ply-in-batches.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAC,OAAO,EAAoD,MAAM,aAAa,CAAC;AAIvF;;;;GAIG;AACH,wBAAuB,iBAAiB,CACtC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,EAC5D,OAAO,EAAE,GAAG,GACX,aAAa,CAAC,OAAO,CAAC,CAexB"}
1
+ {"version":3,"file":"parse-ply-in-batches.d.ts","sourceRoot":"","sources":["../../src/lib/parse-ply-in-batches.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAC,OAAO,EAAoD,uBAAoB;AAIvF;;;;GAIG;AACH,wBAAuB,iBAAiB,CACtC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,EAC5D,OAAO,EAAE,GAAG,GACX,aAAa,CAAC,OAAO,CAAC,CAexB"}
@@ -1,177 +1,246 @@
1
+ // PLY Loader, adapted from THREE.js (MIT license)
2
+ //
3
+ // Attributions per original THREE.js source file:
4
+ //
5
+ // @author Wei Meng / http://about.me/menway
6
+ //
7
+ // Description: A loader for PLY ASCII files (known as the Polygon File Format
8
+ // or the Stanford Triangle Format).
9
+ //
10
+ // Limitations: ASCII decoding assumes file is UTF-8.
11
+ //
12
+ // If the PLY file uses non standard property names, they can be mapped while
13
+ // loading. For example, the following maps the properties
14
+ // “diffuse_(red|green|blue)” in the file to standard color names.
15
+ //
16
+ // parsePLY(data, {
17
+ // propertyNameMapping: {
18
+ // diffuse_red: 'red',
19
+ // diffuse_green: 'green',
20
+ // diffuse_blue: 'blue'
21
+ // }
22
+ // });
1
23
  import { makeLineIterator, makeTextDecoderIterator, forEach } from '@loaders.gl/loader-utils';
2
24
  import normalizePLY from "./normalize-ply.js";
3
25
  let currentElement;
26
+ /**
27
+ * PARSER
28
+ * @param iterator
29
+ * @param options
30
+ */
4
31
  export async function* parsePLYInBatches(iterator, options) {
5
- const lineIterator = makeLineIterator(makeTextDecoderIterator(iterator));
6
- const header = await parsePLYHeader(lineIterator, options);
7
- let attributes;
8
- switch (header.format) {
9
- case 'ascii':
10
- attributes = await parseASCII(lineIterator, header);
11
- break;
12
- default:
13
- throw new Error('Binary PLY can not yet be parsed in streaming mode');
14
- }
15
- yield normalizePLY(header, attributes, options);
32
+ const lineIterator = makeLineIterator(makeTextDecoderIterator(iterator));
33
+ const header = await parsePLYHeader(lineIterator, options);
34
+ let attributes;
35
+ switch (header.format) {
36
+ case 'ascii':
37
+ attributes = await parseASCII(lineIterator, header);
38
+ break;
39
+ default:
40
+ throw new Error('Binary PLY can not yet be parsed in streaming mode');
41
+ // attributes = await parseBinary(lineIterator, header);
42
+ }
43
+ yield normalizePLY(header, attributes, options);
16
44
  }
45
+ /**
46
+ * Parses header
47
+ * @param lineIterator
48
+ * @param options
49
+ * @returns
50
+ */
17
51
  async function parsePLYHeader(lineIterator, options) {
18
- const header = {
19
- comments: [],
20
- elements: []
21
- };
22
- await forEach(lineIterator, line => {
23
- line = line.trim();
24
- if (line === 'end_header') {
25
- return true;
26
- }
27
- if (line === '') {
28
- return false;
29
- }
30
- const lineValues = line.split(/\s+/);
31
- const lineType = lineValues.shift();
32
- line = lineValues.join(' ');
33
- switch (lineType) {
34
- case 'ply':
35
- break;
36
- case 'format':
37
- header.format = lineValues[0];
38
- header.version = lineValues[1];
39
- break;
40
- case 'comment':
41
- header.comments.push(line);
42
- break;
43
- case 'element':
44
- if (currentElement) {
45
- header.elements.push(currentElement);
52
+ const header = {
53
+ comments: [],
54
+ elements: []
55
+ // headerLength
56
+ };
57
+ // Note: forEach does not reset iterator if exiting loop prematurely
58
+ // so that iteration can continue in a second loop
59
+ await forEach(lineIterator, (line) => {
60
+ line = line.trim();
61
+ // End of header
62
+ if (line === 'end_header') {
63
+ return true; // Returning true cancels `forEach`
46
64
  }
47
- currentElement = {
48
- name: lineValues[0],
49
- count: parseInt(lineValues[1], 10),
50
- properties: []
51
- };
52
- break;
53
- case 'property':
54
- const property = makePLYElementProperty(lineValues, options.propertyNameMapping);
55
- currentElement.properties.push(property);
56
- break;
57
- default:
58
- console.log('unhandled', lineType, lineValues);
65
+ // Ignore empty lines
66
+ if (line === '') {
67
+ // eslint-disable-next-line
68
+ return false; // Returning false does not cancel `forEach`
69
+ }
70
+ const lineValues = line.split(/\s+/);
71
+ const lineType = lineValues.shift();
72
+ line = lineValues.join(' ');
73
+ switch (lineType) {
74
+ case 'ply':
75
+ // First line magic characters, ignore
76
+ break;
77
+ case 'format':
78
+ header.format = lineValues[0];
79
+ header.version = lineValues[1];
80
+ break;
81
+ case 'comment':
82
+ header.comments.push(line);
83
+ break;
84
+ case 'element':
85
+ if (currentElement) {
86
+ header.elements.push(currentElement);
87
+ }
88
+ currentElement = {
89
+ name: lineValues[0],
90
+ count: parseInt(lineValues[1], 10),
91
+ properties: []
92
+ };
93
+ break;
94
+ case 'property':
95
+ const property = makePLYElementProperty(lineValues, options.propertyNameMapping);
96
+ currentElement.properties.push(property);
97
+ break;
98
+ default:
99
+ // eslint-disable-next-line
100
+ console.log('unhandled', lineType, lineValues);
101
+ }
102
+ return false;
103
+ });
104
+ if (currentElement) {
105
+ header.elements.push(currentElement);
59
106
  }
60
- return false;
61
- });
62
- if (currentElement) {
63
- header.elements.push(currentElement);
64
- }
65
- return header;
107
+ return header;
66
108
  }
67
109
  function makePLYElementProperty(propertyValues, propertyNameMapping) {
68
- const type = propertyValues[0];
69
- switch (type) {
70
- case 'list':
71
- return {
72
- type,
73
- name: propertyValues[3],
74
- countType: propertyValues[1],
75
- itemType: propertyValues[2]
76
- };
77
- default:
78
- return {
79
- type,
80
- name: propertyValues[1]
81
- };
82
- }
110
+ const type = propertyValues[0];
111
+ switch (type) {
112
+ case 'list':
113
+ return {
114
+ type,
115
+ name: propertyValues[3],
116
+ countType: propertyValues[1],
117
+ itemType: propertyValues[2]
118
+ };
119
+ default:
120
+ return {
121
+ type,
122
+ name: propertyValues[1]
123
+ };
124
+ }
83
125
  }
126
+ // ASCII PARSING
127
+ /**
128
+ * @param lineIterator
129
+ * @param header
130
+ * @returns
131
+ */
84
132
  async function parseASCII(lineIterator, header) {
85
- const attributes = {
86
- indices: [],
87
- vertices: [],
88
- normals: [],
89
- uvs: [],
90
- colors: []
91
- };
92
- let currentElement = 0;
93
- let currentElementCount = 0;
94
- for await (let line of lineIterator) {
95
- line = line.trim();
96
- if (line !== '') {
97
- if (currentElementCount >= header.elements[currentElement].count) {
98
- currentElement++;
99
- currentElementCount = 0;
100
- }
101
- const element = parsePLYElement(header.elements[currentElement].properties, line);
102
- handleElement(attributes, header.elements[currentElement].name, element);
103
- currentElementCount++;
133
+ // PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
134
+ const attributes = {
135
+ indices: [],
136
+ vertices: [],
137
+ normals: [],
138
+ uvs: [],
139
+ colors: []
140
+ };
141
+ let currentElement = 0;
142
+ let currentElementCount = 0;
143
+ for await (let line of lineIterator) {
144
+ line = line.trim();
145
+ if (line !== '') {
146
+ if (currentElementCount >= header.elements[currentElement].count) {
147
+ currentElement++;
148
+ currentElementCount = 0;
149
+ }
150
+ const element = parsePLYElement(header.elements[currentElement].properties, line);
151
+ handleElement(attributes, header.elements[currentElement].name, element);
152
+ currentElementCount++;
153
+ }
104
154
  }
105
- }
106
- return attributes;
155
+ return attributes;
107
156
  }
157
+ /**
158
+ * Parses ASCII number
159
+ * @param n
160
+ * @param type
161
+ * @returns ASCII number
162
+ */
163
+ // eslint-disable-next-line complexity
108
164
  function parseASCIINumber(n, type) {
109
- switch (type) {
110
- case 'char':
111
- case 'uchar':
112
- case 'short':
113
- case 'ushort':
114
- case 'int':
115
- case 'uint':
116
- case 'int8':
117
- case 'uint8':
118
- case 'int16':
119
- case 'uint16':
120
- case 'int32':
121
- case 'uint32':
122
- return parseInt(n, 10);
123
- case 'float':
124
- case 'double':
125
- case 'float32':
126
- case 'float64':
127
- return parseFloat(n);
128
- default:
129
- throw new Error(type);
130
- }
165
+ switch (type) {
166
+ case 'char':
167
+ case 'uchar':
168
+ case 'short':
169
+ case 'ushort':
170
+ case 'int':
171
+ case 'uint':
172
+ case 'int8':
173
+ case 'uint8':
174
+ case 'int16':
175
+ case 'uint16':
176
+ case 'int32':
177
+ case 'uint32':
178
+ return parseInt(n, 10);
179
+ case 'float':
180
+ case 'double':
181
+ case 'float32':
182
+ case 'float64':
183
+ return parseFloat(n);
184
+ default:
185
+ throw new Error(type);
186
+ }
131
187
  }
188
+ /**
189
+ * Parses ASCII element
190
+ * @param properties
191
+ * @param line
192
+ * @returns element
193
+ */
132
194
  function parsePLYElement(properties, line) {
133
- const values = line.split(/\s+/);
134
- const element = {};
135
- for (let i = 0; i < properties.length; i++) {
136
- if (properties[i].type === 'list') {
137
- const list = [];
138
- const n = parseASCIINumber(values.shift(), properties[i].countType);
139
- for (let j = 0; j < n; j++) {
140
- list.push(parseASCIINumber(values.shift(), properties[i].itemType));
141
- }
142
- element[properties[i].name] = list;
143
- } else {
144
- element[properties[i].name] = parseASCIINumber(values.shift(), properties[i].type);
195
+ const values = line.split(/\s+/);
196
+ const element = {};
197
+ for (let i = 0; i < properties.length; i++) {
198
+ if (properties[i].type === 'list') {
199
+ const list = [];
200
+ const n = parseASCIINumber(values.shift(), properties[i].countType);
201
+ for (let j = 0; j < n; j++) {
202
+ list.push(parseASCIINumber(values.shift(), properties[i].itemType));
203
+ }
204
+ element[properties[i].name] = list;
205
+ }
206
+ else {
207
+ element[properties[i].name] = parseASCIINumber(values.shift(), properties[i].type);
208
+ }
145
209
  }
146
- }
147
- return element;
210
+ return element;
148
211
  }
149
- function handleElement(buffer, elementName) {
150
- let element = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
151
- switch (elementName) {
152
- case 'vertex':
153
- buffer.vertices.push(element.x, element.y, element.z);
154
- if ('nx' in element && 'ny' in element && 'nz' in element) {
155
- buffer.normals.push(element.nx, element.ny, element.nz);
156
- }
157
- if ('s' in element && 't' in element) {
158
- buffer.uvs.push(element.s, element.t);
159
- }
160
- if ('red' in element && 'green' in element && 'blue' in element) {
161
- buffer.colors.push(element.red / 255.0, element.green / 255.0, element.blue / 255.0);
162
- }
163
- break;
164
- case 'face':
165
- const vertexIndices = element.vertex_indices || element.vertex_index;
166
- if (vertexIndices.length === 3) {
167
- buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[2]);
168
- } else if (vertexIndices.length === 4) {
169
- buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[3]);
170
- buffer.indices.push(vertexIndices[1], vertexIndices[2], vertexIndices[3]);
171
- }
172
- break;
173
- default:
174
- break;
175
- }
212
+ /**
213
+ * @param buffer
214
+ * @param elementName
215
+ * @param element
216
+ */
217
+ // HELPER FUNCTIONS
218
+ // eslint-disable-next-line complexity
219
+ function handleElement(buffer, elementName, element = {}) {
220
+ switch (elementName) {
221
+ case 'vertex':
222
+ buffer.vertices.push(element.x, element.y, element.z);
223
+ if ('nx' in element && 'ny' in element && 'nz' in element) {
224
+ buffer.normals.push(element.nx, element.ny, element.nz);
225
+ }
226
+ if ('s' in element && 't' in element) {
227
+ buffer.uvs.push(element.s, element.t);
228
+ }
229
+ if ('red' in element && 'green' in element && 'blue' in element) {
230
+ buffer.colors.push(element.red / 255.0, element.green / 255.0, element.blue / 255.0);
231
+ }
232
+ break;
233
+ case 'face':
234
+ const vertexIndices = element.vertex_indices || element.vertex_index; // issue #9338
235
+ if (vertexIndices.length === 3) {
236
+ buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[2]);
237
+ }
238
+ else if (vertexIndices.length === 4) {
239
+ buffer.indices.push(vertexIndices[0], vertexIndices[1], vertexIndices[3]);
240
+ buffer.indices.push(vertexIndices[1], vertexIndices[2], vertexIndices[3]);
241
+ }
242
+ break;
243
+ default:
244
+ break;
245
+ }
176
246
  }
177
- //# sourceMappingURL=parse-ply-in-batches.js.map
@@ -1,4 +1,4 @@
1
- import type { PLYMesh } from './ply-types';
1
+ import type { PLYMesh } from "./ply-types.js";
2
2
  export type ParsePLYOptions = {
3
3
  propertyNameMapping?: Record<string, string>;
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"parse-ply.d.ts","sourceRoot":"","sources":["../../src/lib/parse-ply.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,OAAO,EAMR,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,eAAe,GAAG;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAc3F"}
1
+ {"version":3,"file":"parse-ply.d.ts","sourceRoot":"","sources":["../../src/lib/parse-ply.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,OAAO,EAMR,uBAAoB;AAGrB,MAAM,MAAM,eAAe,GAAG;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAc3F"}