Test Failed
Push — master ( def0c6...dd5863 )
by
unknown
06:55
created

docs/js/plythreejs/PLYLoader.js   F

Complexity

Total Complexity 87
Complexity/F 4.83

Size

Lines of Code 455
Function Count 18

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
c 1
b 0
f 0
nc 12
dl 0
loc 455
rs 2.6017
wmc 87
mnd 4
bc 50
fnc 18
bpm 2.7777
cpm 4.8333
noi 12

15 Functions

Rating   Name   Duplication   Size   Complexity  
C THREE.PLYLoader.handleElement 0 40 8
C THREE.PLYLoader.parseHeader 0 118 11
B THREE.PLYLoader.parseBinary 0 26 3
C THREE.PLYLoader.binaryRead 0 24 17
A THREE.PLYLoader.load 0 14 1
A THREE.PLYLoader.setPropertyNameMapping 0 5 1
A THREE.PLYLoader.postProcess 0 23 3
B THREE.PLYLoader.parseASCIINumber 0 16 17
A THREE.PLYLoader.parse 0 15 3
A THREE.PLYLoader.setCrossOrigin 0 5 1
B THREE.PLYLoader.parseASCIIElement 0 32 4
A THREE.PLYLoader.isASCII 0 7 1
B THREE.PLYLoader.binaryReadElement 0 38 4
A THREE.PLYLoader.bin2str 0 13 2
B THREE.PLYLoader.parseASCII 0 51 5

How to fix   Complexity   

Complexity

Complex classes like docs/js/plythreejs/PLYLoader.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * @author Wei Meng / http://about.me/menway
3
 *
4
 * Description: A THREE loader for PLY ASCII files (known as the Polygon File Format or the Stanford Triangle Format).
5
 *
6
 *
7
 * Limitations: ASCII decoding assumes file is UTF-8.
8
 *
9
 * Usage:
10
 *	var loader = new THREE.PLYLoader();
11
 *	loader.load('./models/ply/ascii/dolphins.ply', function (geometry) {
12
 *
13
 *		scene.add( new THREE.Mesh( geometry ) );
14
 *
15
 *	} );
16
 *
17
 * If the PLY file uses non standard property names, they can be mapped while
18
 * loading. For example, the following maps the properties
19
 * “diffuse_(red|green|blue)” in the file to standard color names.
20
 *
21
 * loader.setPropertyNameMapping( {
22
 *	diffuse_red: 'red',
23
 *	diffuse_green: 'green',
24
 *	diffuse_blue: 'blue'
25
 * } );
26
 *
27
 */
28
29
30
THREE.PLYLoader = function ( manager ) {
0 ignored issues
show
Bug introduced by
The variable THREE seems to be never declared. If this is a global, consider adding a /** global: THREE */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
31
32
	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
0 ignored issues
show
Bug introduced by
The variable THREE seems to be never declared. If this is a global, consider adding a /** global: THREE */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
33
34
	this.propertyNameMapping = {};
35
36
};
37
38
THREE.PLYLoader.prototype = {
39
40
	constructor: THREE.PLYLoader,
41
42
	load: function ( url, onLoad, onProgress, onError ) {
43
44
		var scope = this;
45
46
		var loader = new THREE.XHRLoader( this.manager );
0 ignored issues
show
Bug introduced by
The variable THREE seems to be never declared. If this is a global, consider adding a /** global: THREE */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
47
		loader.setCrossOrigin( this.crossOrigin );
48
		loader.setResponseType( 'arraybuffer' );
49
		loader.load( url, function ( text ) {
50
51
			onLoad( scope.parse( text ) );
52
53
		}, onProgress, onError );
54
55
	},
56
57
	setCrossOrigin: function ( value ) {
58
59
		this.crossOrigin = value;
60
61
	},
62
63
	setPropertyNameMapping: function ( mapping ) {
64
65
		this.propertyNameMapping = mapping;
66
67
	},
68
69
	bin2str: function ( buf ) {
70
71
		var array_buffer = new Uint8Array( buf );
72
		var str = '';
73
		for ( var i = 0; i < buf.byteLength; i ++ ) {
74
75
			str += String.fromCharCode( array_buffer[ i ] ); // implicitly assumes little-endian
76
77
		}
78
79
		return str;
80
81
	},
82
83
	isASCII: function( data ) {
84
85
		var header = this.parseHeader( this.bin2str( data ) );
86
87
		return header.format === "ascii";
88
89
	},
90
91
	parse: function ( data ) {
92
93
		if ( data instanceof ArrayBuffer ) {
94
95
			return this.isASCII( data )
96
				? this.parseASCII( this.bin2str( data ) )
97
				: this.parseBinary( data );
98
99
		} else {
100
101
			return this.parseASCII( data );
102
103
		}
104
105
	},
106
107
	parseHeader: function ( data ) {
108
109
		var patternHeader = /ply([\s\S]*)end_header\s/;
110
		var headerText = "";
111
		var headerLength = 0;
112
		var result = patternHeader.exec( data );
113
		if ( result !== null ) {
114
115
			headerText = result [ 1 ];
116
			headerLength = result[ 0 ].length;
117
118
		}
119
120
		var header = {
121
			comments: [],
122
			elements: [],
123
			headerLength: headerLength
124
		};
125
126
		var lines = headerText.split( '\n' );
127
		var currentElement = undefined;
0 ignored issues
show
Unused Code Comprehensibility introduced by
The assignment of undefined is not necessary as currentElement is implicitly marked as undefined by the declaration.
Loading history...
128
		var lineType, lineValues;
129
130
		function make_ply_element_property( propertValues, propertyNameMapping ) {
131
132
			var property = {
133
				type: propertValues[ 0 ]
134
			};
135
136
			if ( property.type === 'list' ) {
137
138
				property.name = propertValues[ 3 ];
139
				property.countType = propertValues[ 1 ];
140
				property.itemType = propertValues[ 2 ];
141
142
			} else {
143
144
				property.name = propertValues[ 1 ];
145
146
			}
147
148
			if ( property.name in propertyNameMapping ) {
149
150
				property.name = propertyNameMapping[ property.name ];
151
152
			}
153
154
			return property;
155
156
		}
157
158
		for ( var i = 0; i < lines.length; i ++ ) {
159
160
			var line = lines[ i ];
161
			line = line.trim();
162
			if ( line === "" ) {
163
164
				continue;
165
166
			}
167
			lineValues = line.split( /\s+/ );
168
			lineType = lineValues.shift();
169
			line = lineValues.join( " " );
170
171
			switch ( lineType ) {
172
173
			case "format":
174
175
				header.format = lineValues[ 0 ];
176
				header.version = lineValues[ 1 ];
177
178
				break;
179
180
			case "comment":
181
182
				header.comments.push( line );
183
184
				break;
185
186
			case "element":
187
188
				if ( ! ( currentElement === undefined ) ) {
189
190
					header.elements.push( currentElement );
191
192
				}
193
194
				currentElement = Object();
195
				currentElement.name = lineValues[ 0 ];
196
				currentElement.count = parseInt( lineValues[ 1 ] );
197
				currentElement.properties = [];
198
199
				break;
200
201
			case "property":
202
203
				currentElement.properties.push( make_ply_element_property( lineValues, this.propertyNameMapping ) );
204
205
				break;
206
207
208
			default:
209
210
				console.log( "unhandled", lineType, lineValues );
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
211
212
			}
213
214
		}
215
216
		if ( ! ( currentElement === undefined ) ) {
217
218
			header.elements.push( currentElement );
219
220
		}
221
222
		return header;
223
224
	},
225
226
	parseASCIINumber: function ( n, type ) {
227
228
		switch ( type ) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
229
230
		case 'char': case 'uchar': case 'short': case 'ushort': case 'int': case 'uint':
231
		case 'int8': case 'uint8': case 'int16': case 'uint16': case 'int32': case 'uint32':
232
233
			return parseInt( n );
234
235
		case 'float': case 'double': case 'float32': case 'float64':
236
237
			return parseFloat( n );
238
239
		}
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
240
241
	},
242
243
	parseASCIIElement: function ( properties, line ) {
244
245
		var values = line.split( /\s+/ );
246
247
		var element = Object();
248
249
		for ( var i = 0; i < properties.length; i ++ ) {
250
251
			if ( properties[ i ].type === "list" ) {
252
253
				var list = [];
254
				var n = this.parseASCIINumber( values.shift(), properties[ i ].countType );
255
256
				for ( var j = 0; j < n; j ++ ) {
257
258
					list.push( this.parseASCIINumber( values.shift(), properties[ i ].itemType ) );
259
260
				}
261
262
				element[ properties[ i ].name ] = list;
263
264
			} else {
265
266
				element[ properties[ i ].name ] = this.parseASCIINumber( values.shift(), properties[ i ].type );
267
268
			}
269
270
		}
271
272
		return element;
273
274
	},
275
276
	parseASCII: function ( data ) {
277
278
		// PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
279
280
		var geometry = new THREE.Geometry();
0 ignored issues
show
Bug introduced by
The variable THREE seems to be never declared. If this is a global, consider adding a /** global: THREE */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
281
282
		var result;
283
284
		var header = this.parseHeader( data );
285
286
		var patternBody = /end_header\s([\s\S]*)$/;
287
		var body = "";
288
		if ( ( result = patternBody.exec( data ) ) !== null ) {
289
290
			body = result [ 1 ];
291
292
		}
293
294
		var lines = body.split( '\n' );
295
		var currentElement = 0;
296
		var currentElementCount = 0;
297
		geometry.useColor = false;
298
299
		for ( var i = 0; i < lines.length; i ++ ) {
300
301
			var line = lines[ i ];
302
			line = line.trim();
303
			if ( line === "" ) {
304
305
				continue;
306
307
			}
308
309
			if ( currentElementCount >= header.elements[ currentElement ].count ) {
310
311
				currentElement ++;
312
				currentElementCount = 0;
313
314
			}
315
316
			var element = this.parseASCIIElement( header.elements[ currentElement ].properties, line );
317
318
			this.handleElement( geometry, header.elements[ currentElement ].name, element );
319
320
			currentElementCount ++;
321
322
		}
323
324
		return this.postProcess( geometry );
325
326
	},
327
328
	postProcess: function ( geometry ) {
329
330
		if ( geometry.useColor ) {
331
332
			for ( var i = 0; i < geometry.faces.length; i ++ ) {
333
334
				geometry.faces[ i ].vertexColors = [
335
					geometry.colors[ geometry.faces[ i ].a ],
336
					geometry.colors[ geometry.faces[ i ].b ],
337
					geometry.colors[ geometry.faces[ i ].c ]
338
				];
339
340
			}
341
342
			geometry.elementsNeedUpdate = true;
343
344
		}
345
346
		geometry.computeBoundingSphere();
347
348
		return geometry;
349
350
	},
351
352
	handleElement: function ( geometry, elementName, element ) {
353
354
		if ( elementName === "vertex" ) {
355
356
			geometry.vertices.push(
357
				new THREE.Vector3( element.x, element.y, element.z )
0 ignored issues
show
Bug introduced by
The variable THREE seems to be never declared. If this is a global, consider adding a /** global: THREE */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
358
			);
359
360
			if ( 'red' in element && 'green' in element && 'blue' in element ) {
361
362
				geometry.useColor = true;
363
364
				var color = new THREE.Color();
365
				color.setRGB( element.red / 255.0, element.green / 255.0, element.blue / 255.0 );
366
				geometry.colors.push( color );
367
368
			}
369
370
		} else if ( elementName === "face" ) {
371
372
			var vertex_indices = element.vertex_indices;
373
374
			if ( vertex_indices.length === 3 ) {
375
376
				geometry.faces.push(
377
					new THREE.Face3( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 2 ] )
378
				);
379
380
			} else if ( vertex_indices.length === 4 ) {
381
382
				geometry.faces.push(
383
					new THREE.Face3( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 3 ] ),
384
					new THREE.Face3( vertex_indices[ 1 ], vertex_indices[ 2 ], vertex_indices[ 3 ] )
385
				);
386
387
			}
388
389
		}
390
391
	},
392
393
	binaryRead: function ( dataview, at, type, little_endian ) {
394
395
		switch ( type ) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
396
397
			// corespondences for non-specific length types here match rply:
398
		case 'int8':		case 'char':	 return [ dataview.getInt8( at ), 1 ];
399
400
		case 'uint8':		case 'uchar':	 return [ dataview.getUint8( at ), 1 ];
401
402
		case 'int16':		case 'short':	 return [ dataview.getInt16( at, little_endian ), 2 ];
403
404
		case 'uint16':	case 'ushort': return [ dataview.getUint16( at, little_endian ), 2 ];
405
406
		case 'int32':		case 'int':		 return [ dataview.getInt32( at, little_endian ), 4 ];
407
408
		case 'uint32':	case 'uint':	 return [ dataview.getUint32( at, little_endian ), 4 ];
409
410
		case 'float32': case 'float':	 return [ dataview.getFloat32( at, little_endian ), 4 ];
411
412
		case 'float64': case 'double': return [ dataview.getFloat64( at, little_endian ), 8 ];
413
414
		}
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
415
416
	},
417
418
	binaryReadElement: function ( dataview, at, properties, little_endian ) {
419
420
		var element = Object();
421
		var result, read = 0;
422
423
		for ( var i = 0; i < properties.length; i ++ ) {
424
425
			if ( properties[ i ].type === "list" ) {
426
427
				var list = [];
428
429
				result = this.binaryRead( dataview, at + read, properties[ i ].countType, little_endian );
430
				var n = result[ 0 ];
431
				read += result[ 1 ];
432
433
				for ( var j = 0; j < n; j ++ ) {
434
435
					result = this.binaryRead( dataview, at + read, properties[ i ].itemType, little_endian );
436
					list.push( result[ 0 ] );
437
					read += result[ 1 ];
438
439
				}
440
441
				element[ properties[ i ].name ] = list;
442
443
			} else {
444
445
				result = this.binaryRead( dataview, at + read, properties[ i ].type, little_endian );
446
				element[ properties[ i ].name ] = result[ 0 ];
447
				read += result[ 1 ];
448
449
			}
450
451
		}
452
453
		return [ element, read ];
454
455
	},
456
457
	parseBinary: function ( data ) {
458
459
		var geometry = new THREE.Geometry();
0 ignored issues
show
Bug introduced by
The variable THREE seems to be never declared. If this is a global, consider adding a /** global: THREE */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
460
461
		var header = this.parseHeader( this.bin2str( data ) );
462
		var little_endian = ( header.format === "binary_little_endian" );
463
		var body = new DataView( data, header.headerLength );
464
		var result, loc = 0;
465
466
		for ( var currentElement = 0; currentElement < header.elements.length; currentElement ++ ) {
467
468
			for ( var currentElementCount = 0; currentElementCount < header.elements[ currentElement ].count; currentElementCount ++ ) {
469
470
				result = this.binaryReadElement( body, loc, header.elements[ currentElement ].properties, little_endian );
471
				loc += result[ 1 ];
472
				var element = result[ 0 ];
473
474
				this.handleElement( geometry, header.elements[ currentElement ].name, element );
475
476
			}
477
478
		}
479
480
		return this.postProcess( geometry );
481
482
	}
483
484
};