otsdaq_utilities  v2_05_02_indev
ThreeCSG.js
1 'use strict';
2 window.ThreeBSP = (function() {
3 
4  var ThreeBSP,
5  EPSILON = 1e-5,
6  COPLANAR = 0,
7  FRONT = 1,
8  BACK = 2,
9  SPANNING = 3;
10 
11  ThreeBSP = function( geometry ) {
12  // Convert THREE.Geometry to ThreeBSP
13  var i, _length_i,
14  face, vertex, faceVertexUvs, uvs,
15  polygon,
16  polygons = [],
17  tree;
18 
19  if ( geometry instanceof THREE.Geometry ) {
20  this.matrix = new THREE.Matrix4;
21  } else if ( geometry instanceof THREE.Mesh ) {
22  // #todo: add hierarchy support
23  geometry.updateMatrix();
24  this.matrix = geometry.matrix.clone();
25  geometry = geometry.geometry;
26  } else if ( geometry instanceof ThreeBSP.Node ) {
27  this.tree = geometry;
28  this.matrix = new THREE.Matrix4;
29  return this;
30  } else {
31  throw 'ThreeBSP: Given geometry is unsupported';
32  }
33 
34  for ( i = 0, _length_i = geometry.faces.length; i < _length_i; i++ ) {
35  face = geometry.faces[i];
36  faceVertexUvs = null; //geometry.faceVertexUvs[0][i];
37  polygon = new ThreeBSP.Polygon;
38 
39  if ( face instanceof THREE.Face3 ) {
40  vertex = geometry.vertices[ face.a ];
41  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
42  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, /*face.normal */face.vertexNormals[0], uvs );
43  vertex.applyMatrix4(this.matrix);
44  polygon.vertices.push( vertex );
45 
46  vertex = geometry.vertices[ face.b ];
47  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
48  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, /*face.normal*/ face.vertexNormals[1], uvs );
49  vertex.applyMatrix4(this.matrix);
50  polygon.vertices.push( vertex );
51 
52  vertex = geometry.vertices[ face.c ];
53  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
54  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, /*face.normal */ face.vertexNormals[2], uvs );
55  vertex.applyMatrix4(this.matrix);
56  polygon.vertices.push( vertex );
57  } else if ( typeof THREE.Face4 ) {
58  vertex = geometry.vertices[ face.a ];
59  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
60  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );
61  vertex.applyMatrix4(this.matrix);
62  polygon.vertices.push( vertex );
63 
64  vertex = geometry.vertices[ face.b ];
65  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
66  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs );
67  vertex.applyMatrix4(this.matrix);
68  polygon.vertices.push( vertex );
69 
70  vertex = geometry.vertices[ face.c ];
71  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
72  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );
73  vertex.applyMatrix4(this.matrix);
74  polygon.vertices.push( vertex );
75 
76  vertex = geometry.vertices[ face.d ];
77  uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[3].x, faceVertexUvs[3].y ) : null;
78  vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[3], uvs );
79  vertex.applyMatrix4(this.matrix);
80  polygon.vertices.push( vertex );
81  } else {
82  throw 'Invalid face type at index ' + i;
83  }
84 
85  polygon.calculateProperties();
86  polygons.push( polygon );
87  };
88 
89  this.tree = new ThreeBSP.Node( polygons );
90 
91  };
92  ThreeBSP.prototype.subtract = function( other_tree ) {
93  var a = this.tree.clone(),
94  b = other_tree.tree.clone();
95 
96  a.invert();
97  a.clipTo( b );
98  b.clipTo( a );
99  b.invert();
100  b.clipTo( a );
101  b.invert();
102  a.build( b.allPolygons() );
103  a.invert();
104  a = new ThreeBSP( a );
105  a.matrix = this.matrix;
106  return a;
107  };
108  ThreeBSP.prototype.union = function( other_tree ) {
109  var a = this.tree.clone(),
110  b = other_tree.tree.clone();
111 
112  a.clipTo( b );
113  b.clipTo( a );
114  b.invert();
115  b.clipTo( a );
116  b.invert();
117  a.build( b.allPolygons() );
118  a = new ThreeBSP( a );
119  a.matrix = this.matrix;
120  return a;
121  };
122  ThreeBSP.prototype.intersect = function( other_tree ) {
123  var a = this.tree.clone(),
124  b = other_tree.tree.clone();
125 
126  a.invert();
127  b.clipTo( a );
128  b.invert();
129  a.clipTo( b );
130  b.clipTo( a );
131  a.build( b.allPolygons() );
132  a.invert();
133  a = new ThreeBSP( a );
134  a.matrix = this.matrix;
135  return a;
136  };
137  ThreeBSP.prototype.toGeometry = function() {
138  var i, j,
139  matrix = new THREE.Matrix4().getInverse( this.matrix ),
140  geometry = new THREE.Geometry(),
141  polygons = this.tree.allPolygons(),
142  polygon_count = polygons.length,
143  polygon, polygon_vertice_count,
144  vertice_dict = {},
145  vertex_idx_a, vertex_idx_b, vertex_idx_c,
146  vertex, face,
147  verticeUvs;
148 
149  for ( i = 0; i < polygon_count; i++ ) {
150  polygon = polygons[i];
151  polygon_vertice_count = polygon.vertices.length;
152 
153  for ( j = 2; j < polygon_vertice_count; j++ ) {
154  verticeUvs = [];
155 
156  vertex = polygon.vertices[0];
157  verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
158  vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
159  vertex.applyMatrix4(matrix);
160 
161  if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
162  vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
163  } else {
164  geometry.vertices.push( vertex );
165  vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
166  }
167 
168  vertex = polygon.vertices[j-1];
169  verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
170  vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
171  vertex.applyMatrix4(matrix);
172  if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
173  vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
174  } else {
175  geometry.vertices.push( vertex );
176  vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
177  }
178 
179  vertex = polygon.vertices[j];
180  verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
181  vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
182  vertex.applyMatrix4(matrix);
183  if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
184  vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
185  } else {
186  geometry.vertices.push( vertex );
187  vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
188  }
189 
190  face = new THREE.Face3(
191  vertex_idx_a,
192  vertex_idx_b,
193  vertex_idx_c,
194  new THREE.Vector3( polygon.normal.x, polygon.normal.y, polygon.normal.z )
195  );
196 
197  geometry.faces.push( face );
198  geometry.faceVertexUvs[0].push( verticeUvs );
199  }
200 
201  }
202  return geometry;
203  };
204  ThreeBSP.prototype.toMesh = function( material ) {
205  var geometry = this.toGeometry(),
206  mesh = new THREE.Mesh( geometry, material );
207 
208  mesh.position.setFromMatrixPosition( this.matrix );
209  mesh.rotation.setFromRotationMatrix( this.matrix );
210 
211  return mesh;
212  };
213 
214 
215  ThreeBSP.Polygon = function( vertices, normal, w ) {
216  if ( !( vertices instanceof Array ) ) {
217  vertices = [];
218  }
219 
220  this.vertices = vertices;
221  if ( vertices.length > 0 ) {
222  this.calculateProperties();
223  } else {
224  this.normal = this.w = undefined;
225  }
226  };
227  ThreeBSP.Polygon.prototype.calculateProperties = function() {
228  var a = this.vertices[0],
229  b = this.vertices[1],
230  c = this.vertices[2];
231 
232  this.normal = b.clone().subtract( a ).cross(
233  c.clone().subtract( a )
234  ).normalize();
235 
236  this.w = this.normal.clone().dot( a );
237 
238  return this;
239  };
240  ThreeBSP.Polygon.prototype.clone = function() {
241  var i, vertice_count,
242  polygon = new ThreeBSP.Polygon;
243 
244  for ( i = 0, vertice_count = this.vertices.length; i < vertice_count; i++ ) {
245  polygon.vertices.push( this.vertices[i].clone() );
246  };
247  polygon.calculateProperties();
248 
249  return polygon;
250  };
251 
252  ThreeBSP.Polygon.prototype.flip = function() {
253  var i, vertices = [];
254 
255  this.normal.multiplyScalar( -1 );
256  this.w *= -1;
257 
258  for ( i = this.vertices.length - 1; i >= 0; i-- ) {
259  vertices.push( this.vertices[i] );
260  };
261  this.vertices = vertices;
262 
263  return this;
264  };
265  ThreeBSP.Polygon.prototype.classifyVertex = function( vertex ) {
266  var side_value = this.normal.dot( vertex ) - this.w;
267 
268  if ( side_value < -EPSILON ) {
269  return BACK;
270  } else if ( side_value > EPSILON ) {
271  return FRONT;
272  } else {
273  return COPLANAR;
274  }
275  };
276  ThreeBSP.Polygon.prototype.classifySide = function( polygon ) {
277  var i, vertex, classification,
278  num_positive = 0,
279  num_negative = 0,
280  vertice_count = polygon.vertices.length;
281 
282  for ( i = 0; i < vertice_count; i++ ) {
283  vertex = polygon.vertices[i];
284  classification = this.classifyVertex( vertex );
285  if ( classification === FRONT ) {
286  num_positive++;
287  } else if ( classification === BACK ) {
288  num_negative++;
289  }
290  }
291 
292  if ( num_positive > 0 && num_negative === 0 ) {
293  return FRONT;
294  } else if ( num_positive === 0 && num_negative > 0 ) {
295  return BACK;
296  } else if ( num_positive === 0 && num_negative === 0 ) {
297  return COPLANAR;
298  } else {
299  return SPANNING;
300  }
301  };
302  ThreeBSP.Polygon.prototype.splitPolygon = function( polygon, coplanar_front, coplanar_back, front, back ) {
303  var classification = this.classifySide( polygon );
304 
305  if ( classification === COPLANAR ) {
306 
307  ( this.normal.dot( polygon.normal ) > 0 ? coplanar_front : coplanar_back ).push( polygon );
308 
309  } else if ( classification === FRONT ) {
310 
311  front.push( polygon );
312 
313  } else if ( classification === BACK ) {
314 
315  back.push( polygon );
316 
317  } else {
318 
319  var vertice_count,
320  i, j, ti, tj, vi, vj,
321  t, v,
322  f = [],
323  b = [];
324 
325  for ( i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++ ) {
326 
327  j = (i + 1) % vertice_count;
328  vi = polygon.vertices[i];
329  vj = polygon.vertices[j];
330  ti = this.classifyVertex( vi );
331  tj = this.classifyVertex( vj );
332 
333  if ( ti != BACK ) f.push( vi );
334  if ( ti != FRONT ) b.push( vi );
335  if ( (ti | tj) === SPANNING ) {
336  t = ( this.w - this.normal.dot( vi ) ) / this.normal.dot( vj.clone().subtract( vi ) );
337  v = vi.interpolate( vj, t );
338  f.push( v );
339  b.push( v );
340  }
341  }
342 
343 
344  if ( f.length >= 3 ) front.push( new ThreeBSP.Polygon( f ).calculateProperties() );
345  if ( b.length >= 3 ) back.push( new ThreeBSP.Polygon( b ).calculateProperties() );
346  }
347  };
348 
349  ThreeBSP.Vertex = function( x, y, z, normal, uv ) {
350  this.x = x;
351  this.y = y;
352  this.z = z;
353  this.normal = normal || new THREE.Vector3;
354  this.uv = uv || new THREE.Vector2;
355  };
356  ThreeBSP.Vertex.prototype.clone = function() {
357  return new ThreeBSP.Vertex( this.x, this.y, this.z, this.normal.clone(), this.uv.clone() );
358  };
359  ThreeBSP.Vertex.prototype.add = function( vertex ) {
360  this.x += vertex.x;
361  this.y += vertex.y;
362  this.z += vertex.z;
363  return this;
364  };
365  ThreeBSP.Vertex.prototype.subtract = function( vertex ) {
366  this.x -= vertex.x;
367  this.y -= vertex.y;
368  this.z -= vertex.z;
369  return this;
370  };
371  ThreeBSP.Vertex.prototype.multiplyScalar = function( scalar ) {
372  this.x *= scalar;
373  this.y *= scalar;
374  this.z *= scalar;
375  return this;
376  };
377  ThreeBSP.Vertex.prototype.cross = function( vertex ) {
378  var x = this.x,
379  y = this.y,
380  z = this.z;
381 
382  this.x = y * vertex.z - z * vertex.y;
383  this.y = z * vertex.x - x * vertex.z;
384  this.z = x * vertex.y - y * vertex.x;
385 
386  return this;
387  };
388  ThreeBSP.Vertex.prototype.normalize = function() {
389  var length = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
390 
391  this.x /= length;
392  this.y /= length;
393  this.z /= length;
394 
395  return this;
396  };
397  ThreeBSP.Vertex.prototype.dot = function( vertex ) {
398  return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;
399  };
400  ThreeBSP.Vertex.prototype.lerp = function( a, t ) {
401  this.add(
402  a.clone().subtract( this ).multiplyScalar( t )
403  );
404 
405  this.normal.add(
406  a.normal.clone().sub( this.normal ).multiplyScalar( t )
407  );
408 
409  this.uv.add(
410  a.uv.clone().sub( this.uv ).multiplyScalar( t )
411  );
412 
413  return this;
414  };
415  ThreeBSP.Vertex.prototype.interpolate = function( other, t ) {
416  return this.clone().lerp( other, t );
417  };
418  ThreeBSP.Vertex.prototype.applyMatrix4 = function ( m ) {
419 
420  // input: THREE.Matrix4 affine matrix
421 
422  var x = this.x, y = this.y, z = this.z;
423 
424  var e = m.elements;
425 
426  this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
427  this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
428  this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
429 
430  return this;
431 
432  }
433 
434 
435  ThreeBSP.Node = function( polygons ) {
436  var i, polygon_count,
437  front = [],
438  back = [];
439 
440  this.polygons = [];
441  this.front = this.back = undefined;
442 
443  if ( !(polygons instanceof Array) || polygons.length === 0 ) return;
444 
445  this.divider = polygons[0].clone();
446 
447 // console.log('divider length ' + polygons.length);
448 
449  for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
450  this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );
451  }
452 
453 // console.log('divider done ' + front.length + ' ' + back.length);
454 
455  if ( front.length > 0 ) {
456  this.front = new ThreeBSP.Node( front );
457  }
458 
459  if ( back.length > 0 ) {
460  this.back = new ThreeBSP.Node( back );
461  }
462  };
463  ThreeBSP.Node.isConvex = function( polygons ) {
464  var i, j;
465  for ( i = 0; i < polygons.length; i++ ) {
466  for ( j = 0; j < polygons.length; j++ ) {
467  if ( i !== j && polygons[i].classifySide( polygons[j] ) !== BACK ) {
468  return false;
469  }
470  }
471  }
472  return true;
473  };
474  ThreeBSP.Node.prototype.build = function( polygons ) {
475  var i, polygon_count,
476  front = [],
477  back = [];
478 
479  if ( !this.divider ) {
480  this.divider = polygons[0].clone();
481  }
482 
483  for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
484  this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );
485  }
486 
487  if ( front.length > 0 ) {
488  if ( !this.front ) this.front = new ThreeBSP.Node();
489  this.front.build( front );
490  }
491 
492  if ( back.length > 0 ) {
493  if ( !this.back ) this.back = new ThreeBSP.Node();
494  this.back.build( back );
495  }
496  };
497  ThreeBSP.Node.prototype.allPolygons = function() {
498  var polygons = this.polygons.slice();
499  if ( this.front ) polygons = polygons.concat( this.front.allPolygons() );
500  if ( this.back ) polygons = polygons.concat( this.back.allPolygons() );
501  return polygons;
502  };
503  ThreeBSP.Node.prototype.clone = function() {
504  var node = new ThreeBSP.Node();
505 
506  node.divider = this.divider.clone();
507  node.polygons = this.polygons.map( function( polygon ) { return polygon.clone(); } );
508  node.front = this.front && this.front.clone();
509  node.back = this.back && this.back.clone();
510 
511  return node;
512  };
513  ThreeBSP.Node.prototype.invert = function() {
514  var i, polygon_count, temp;
515 
516  for ( i = 0, polygon_count = this.polygons.length; i < polygon_count; i++ ) {
517  this.polygons[i].flip();
518  }
519 
520  this.divider.flip();
521  if ( this.front ) this.front.invert();
522  if ( this.back ) this.back.invert();
523 
524  temp = this.front;
525  this.front = this.back;
526  this.back = temp;
527 
528  return this;
529  };
530  ThreeBSP.Node.prototype.clipPolygons = function( polygons ) {
531  var i, polygon_count,
532  front, back;
533 
534  if ( !this.divider ) return polygons.slice();
535 
536  front = [], back = [];
537 
538  for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
539  this.divider.splitPolygon( polygons[i], front, back, front, back );
540  }
541 
542  if ( this.front ) front = this.front.clipPolygons( front );
543  if ( this.back ) back = this.back.clipPolygons( back );
544  else back = [];
545 
546  return front.concat( back );
547  };
548 
549  ThreeBSP.Node.prototype.clipTo = function( node ) {
550  this.polygons = node.clipPolygons( this.polygons );
551  if ( this.front ) this.front.clipTo( node );
552  if ( this.back ) this.back.clipTo( node );
553  };
554 
555 
556  return ThreeBSP;
557 })();