1. 程式人生 > >threejs 使用ThreeBSP庫進行Three.js網格組合 (原始碼)

threejs 使用ThreeBSP庫進行Three.js網格組合 (原始碼)

import * as THREE from 'three';
let BACK;
let COPLANAR;
let EPSILON;
let FRONT;
let SPANNING;
let Timelimit;
let returning;
let bind = function(fn, me) {
  return function() {
    return fn.apply(me, arguments);
  };
};
let slice = [].slice;
let extend = function(child, parent) {
  for (let key in parent) {
    if (hasProp.call(parent, key)) {
      child[key] = parent[key];
    }
  }
  function Ctor() {
    this.constructor = child;
  }
  Ctor.prototype = parent.prototype;
  child.prototype = new Ctor();
  child.__super__ = parent.prototype;
  return child;
};
let hasProp = {}.hasOwnProperty;
EPSILON = 1e-5;
COPLANAR = 0;
FRONT = 1;
BACK = 2;
SPANNING = 3;
returning = function(value, fn) {
  fn();
  return value;
};
Timelimit = (function() {
  function Timelimit(timeout, progress) {
    this.timeout = timeout;
    this.progress = progress;
    this.doTask = bind(this.doTask, this);
    this.finish = bind(this.finish, this);
    this.start = bind(this.start, this);
    this.check = bind(this.check, this);
  }
  Timelimit.prototype.check = function() {
    let elapsed;
    if (this.started == null) {
      return;
    }
    return returning((elapsed = Date.now() - this.started), (function(_this) {
      return function() {
        let ref, ref1, ref2;
        if ((ref = elapsed >= _this.timeout) != null ? ref : 2e308) {
          throw new Error('Timeout reached: ' + elapsed + '/' + _this.timeout + ', ' + ((ref1 = _this.tasks) != null ? ref1 : 0) + ' tasks unfinished ' + ((ref2 = _this.done) != null ? ref2 : 0) + ' finished.');
        }
      };
    })(this));
  };
  Timelimit.prototype.start = function() {
    if (this.started == null) {
      this.started = Date.now();
    }
    if (this.tasks == null) {
      this.tasks = 0;
    }
    if (this.total == null) {
      this.total = 0;
    }
    this.total += 1;
    this.tasks += 1;
    return this.check();
  };
  Timelimit.prototype.finish = function() {
    let elapsed;
    if ((this.tasks != null) && this.tasks < 1) {
      throw new Error('Finished more tasks than started');
    }
    this.tasks -= 1;
    elapsed = this.check();
    if (this.done == null) {
      this.done = 0;
    }
    this.done += 1;
    if (this.progress != null) {
      this.progress(this.done, this.total);
    }
    if (this.tasks === 0) {
      // 'Finished ' + this.done + ' tasks in ' + elapsed + '/' + this.timeout + ' ms';
      let result = this.started = this.done = this.total = void 0;
      return result;
    }
  };
  Timelimit.prototype.doTask = function(block) {
    let result;
    this.start();
    result = block();
    this.finish();
    return result;
  };
  return Timelimit;
})();
function ThreeBSP(treeIsh, matrix1, options) {
  let base;
  let ref;
  let ref1;
  let ref2;
  let ref3;
  this.matrix = matrix1;
  this.options = options != null ? options : {};
  this.intersect = bind(this.intersect, this);
  this.union = bind(this.union, this);
  this.subtract = bind(this.subtract, this);
  this.toGeometry = bind(this.toGeometry, this);
  this.toMesh = bind(this.toMesh, this);
  this.toTree = bind(this.toTree, this);
  this.withTimer = bind(this.withTimer, this);
  if ((this.matrix != null) && !(this.matrix instanceof THREE.Matrix4)) {
    this.options = this.matrix;
    this.matrix = void 0;
  }
  if (this.options == null) {
    this.options = {};
  }
  if (this.matrix == null) {
    this.matrix = new THREE.Matrix4();
  }
  if ((base = this.options).timer == null) {
    base.timer = new Timelimit((ref = (ref1 = this.options.timer) != null ? ref1.timeout : void 0) != null ? ref : this.options.timeout, (ref2 = (ref3 = this.options.timer) != null ? ref3.progress : void 0) != null ? ref2 : this.options.progress);
  }
  if (treeIsh !== undefined) {
    this.tree = this.toTree(treeIsh);
  }
}
ThreeBSP.prototype.withTimer = function(newTimer, block) {
  let oldTimer;
  oldTimer = this.options.timer;
  try {
    this.options.timer = newTimer;
    return block();
  } finally {
    this.options.timer = oldTimer;
  }
};
ThreeBSP.prototype.toTree = function(treeIsh) {
  let face, fn1, geometry, i, k, len, polygons, ref;
  if (treeIsh instanceof ThreeBSP.Node) {
    return treeIsh;
  }
  polygons = [];
  geometry = treeIsh instanceof THREE.Geometry ? treeIsh : treeIsh instanceof THREE.Mesh ? (treeIsh.updateMatrix(), this.matrix = treeIsh.matrix.clone(), treeIsh.geometry) : void 0;
  ref = geometry.faces;
  fn1 = (function(_this) {
    return function(face, i) {
      let faceVertexUvs, idx, l, len1, polygon, ref1, ref2, vIndex, vName, vertex;
      faceVertexUvs = (ref1 = geometry.faceVertexUvs) != null ? ref1[0][i] : void 0;
      if (faceVertexUvs == null) {
        faceVertexUvs = [new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2()];
      }
      polygon = new ThreeBSP.Polygon();
      ref2 = ['a', 'b', 'c', 'd'];
      for (vIndex = l = 0, len1 = ref2.length; l < len1; vIndex = ++l) {
        vName = ref2[vIndex];
        if ((idx = face[vName]) != null) {
          vertex = geometry.vertices[idx];
          vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[vIndex].x, faceVertexUvs[vIndex].y));
          vertex.applyMatrix4(_this.matrix);
          polygon.vertices.push(vertex);
        }
      }
      return polygons.push(polygon.calculateProperties());
    };
  })(this);
  for (i = k = 0, len = ref.length; k < len; i = ++k) {
    face = ref[i];
    fn1(face, i);
  }
  return new ThreeBSP.Node(polygons, this.options);
};
ThreeBSP.prototype.toMesh = function(material) {
  if (material == null) {
    material = new THREE.MeshNormalMaterial();
  }
  return this.options.timer.doTask((function(_this) {
    return function() {
      let geometry;
      let mesh;
      geometry = _this.toGeometry();
      return returning((mesh = new THREE.Mesh(geometry, material)), function() {
        // mesh.position.getPositionFromMatrix(_this.matrix);
        mesh.position.setFromMatrixPosition(_this.matrix);
        return mesh.rotation.setFromRotationMatrix(_this.matrix);
      });
    };
  })(this));
};
ThreeBSP.prototype.toGeometry = function() {
  return this.options.timer.doTask((function(_this) {
    return function() {
      let geometry, matrix;
      matrix = new THREE.Matrix4().getInverse(_this.matrix);
      return returning((geometry = new THREE.Geometry()), function() {
        let k, len, polygon, ref, results;
        ref = _this.tree.allPolygons();
        results = [];
        for (k = 0, len = ref.length; k < len; k++) {
          polygon = ref[k];
          results.push(_this.options.timer.doTask(function() {
            let face, idx, l, polyVerts, ref1, results1, v, vertUvs, verts;
            polyVerts = (function() {
              let l, len1, ref1, results1;
              ref1 = polygon.vertices;
              results1 = [];
              for (l = 0, len1 = ref1.length; l < len1; l++) {
                v = ref1[l];
                results1.push(v.clone().applyMatrix4(matrix));
              }
              return results1;
            })();
            results1 = [];
            for (idx = l = 2, ref1 = polyVerts.length; 2 <= ref1 ? l < ref1 : l > ref1; idx = 2 <= ref1 ? ++l : --l) {
              verts = [polyVerts[0], polyVerts[idx - 1], polyVerts[idx]];
              vertUvs = (function() {
                let len1, m, ref2, ref3, results2;
                results2 = [];
                for (m = 0, len1 = verts.length; m < len1; m++) {
                  v = verts[m];
                  results2.push(new THREE.Vector2((ref2 = v.uv) != null ? ref2.x : void 0, (ref3 = v.uv) != null ? ref3.y : void 0));
                }
                return results2;
              })();
              face = (function(func, args, Ctor) {
                Ctor.prototype = func.prototype;
                let child = new Ctor;
                let result = func.apply(child, args);
                return Object(result) === result ? result : child;
              })(THREE.Face3, slice.call((function() {
                let len1, m, results2;
                results2 = [];
                for (m = 0, len1 = verts.length; m < len1; m++) {
                  v = verts[m];
                  results2.push(geometry.vertices.push(v) - 1);
                }
                return results2;
              })()).concat([polygon.normal.clone()]), function(){});
              geometry.faces.push(face);
              results1.push(geometry.faceVertexUvs[0].push(vertUvs));
            }
            return results1;
          }));
        }
        return results;
      });
    };
  })(this));
};
ThreeBSP.prototype.subtract = function(other) {
  return this.options.timer.doTask((function(_this) {
    return function() {
      return other.withTimer(_this.options.timer, function() {
        let ref;
        let them;
        let us;
        ref = [_this.tree.clone(), other.tree.clone()];
        us = ref[0];
        them = ref[1];
        us.invert().clipTo(them);
        them.clipTo(us).invert().clipTo(us).invert();
        return new ThreeBSP(us.build(them.allPolygons()).invert(), _this.matrix, _this.options);
      });
    };
  })(this));
};
ThreeBSP.prototype.union = function(other) {
  return this.options.timer.doTask((function(_this) {
    return function() {
      return other.withTimer(_this.options.timer, function() {
        let ref;
        let them;
        let us;
        ref = [_this.tree.clone(), other.tree.clone()];
        us = ref[0];
        them = ref[1];
        us.clipTo(them);
        them.clipTo(us).invert().clipTo(us).invert();
        return new ThreeBSP(us.build(them.allPolygons()), _this.matrix, _this.options);
      });
    };
  })(this));
};
ThreeBSP.prototype.intersect = function(other) {
  return this.options.timer.doTask((function(_this) {
    return function() {
      return other.withTimer(_this.options.timer, function() {
        let ref;
        let them;
        let us;
        ref = [_this.tree.clone(), other.tree.clone()];
        us = ref[0];
        them = ref[1];
        them.clipTo(us.invert()).invert().clipTo(us.clipTo(them));
        return new ThreeBSP(us.build(them.allPolygons()).invert(), _this.matrix, _this.options);
      });
    };
  })(this));
};
ThreeBSP.Vertex = (function(superClass) {
  extend(Vertex, superClass);
  function Vertex(x, y, z, normal, uv) {
    this.normal = normal != null ? normal : new THREE.Vector3();
    this.uv = uv != null ? uv : new THREE.Vector2();
    this.interpolate = bind(this.interpolate, this);
    this.lerp = bind(this.lerp, this);
    Vertex.__super__.constructor.call(this, x, y, z);
  }
  Vertex.prototype.clone = function() {
    return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
  };
  Vertex.prototype.lerp = function(v, alpha) {
    return returning(Vertex.__super__.lerp.apply(this, arguments), (function(_this) {
      return function() {
        _this.uv.add(v.uv.clone().sub(_this.uv).multiplyScalar(alpha));
        return _this.normal.lerp(v, alpha);
      };
    })(this));
  };
  Vertex.prototype.interpolate = function() {
    let args;
    let ref;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    return (ref = this.clone()).lerp.apply(ref, args);
  };
  return Vertex;
})(THREE.Vector3);
ThreeBSP.Polygon = (function() {
  function Polygon(vertices, normal, w) {
    this.vertices = vertices != null ? vertices : [];
    this.normal = normal;
    this.w = w;
    this.subdivide = bind(this.subdivide, this);
    this.tessellate = bind(this.tessellate, this);
    this.classifySide = bind(this.classifySide, this);
    this.classifyVertex = bind(this.classifyVertex, this);
    this.invert = bind(this.invert, this);
    this.clone = bind(this.clone, this);
    this.calculateProperties = bind(this.calculateProperties, this);
    if (this.vertices.length) {
      this.calculateProperties();
    }
  }
  Polygon.prototype.calculateProperties = function() {
    return returning(this, (function(_this) {
      return function() {
        let a;
        let b;
        let c;
        let ref;
        ref = _this.vertices;
        a = ref[0];
        b = ref[1];
        c = ref[2];
        _this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
        _this.w = _this.normal.clone().dot(a);
        return _this.w;
      };
    })(this));
  };
  Polygon.prototype.clone = function() {
    let v;
    return new ThreeBSP.Polygon((function() {
      let k;
      let len;
      let ref;
      let results;
      ref = this.vertices;
      results = [];
      for (k = 0, len = ref.length; k < len; k++) {
        v = ref[k];
        results.push(v.clone());
      }
      return results;
    }).call(this), this.normal.clone(), this.w);
  };
  Polygon.prototype.invert = function() {
    return returning(this, (function(_this) {
      return function() {
        _this.normal.multiplyScalar(-1);
        _this.w *= -1;
        return _this.vertices.reverse();
      };
    })(this));
  };
  Polygon.prototype.classifyVertex = function(vertex) {
    let side;
    side = this.normal.dot(vertex) - this.w;
    switch (false) {
      case !(side < -EPSILON):
        return BACK;
      case !(side > EPSILON):
        return FRONT;
      default:
        return COPLANAR;
    }
  };
  Polygon.prototype.classifySide = function(polygon) {
    let back;
    let front;
    let k;
    let len;
    let ref;
    let ref1;
    let tally;
    let v;
    ref = [0, 0];
    front = ref[0];
    back = ref[1];
    tally = (function(_this) {
      return function(v) {
        switch (_this.classifyVertex(v)) {
          case FRONT:
            let dataFront = front += 1;
            return dataFront;
          case BACK:
            let dataBack = back += 1;
            return dataBack;
        }
      };
    })(this);
    ref1 = polygon.vertices;
    for (k = 0, len = ref1.length; k < len; k++) {
      v = ref1[k];
      tally(v);
    }
    if (front > 0 && back === 0) {
      return FRONT;
    }
    if (front === 0 && back > 0) {
      return BACK;
    }
    if ((front === back && back === 0)) {
      return COPLANAR;
    }
    return SPANNING;
  };
  Polygon.prototype.tessellate = function(poly) {
    let b;
    let count;
    let f;
    let i;
    let j;
    let k;
    let len;
    let polys;
    let ref;
    let ref1;
    let ref2;
    let t;
    let ti;
    let tj;
    let v;
    let vi;
    let vj;
    ref = {
      f: [],
      b: [],
      count: poly.vertices.length
    };
    f = ref.f;
    b = ref.b;
    count = ref.count;
    if (this.classifySide(poly) !== SPANNING) {
      return [poly];
    }
    ref1 = poly.vertices;
    for (i = k = 0, len = ref1.length; k < len; i = ++k) {
      vi = ref1[i];
      vj = poly.vertices[(j = (i + 1) % count)];
      ref2 = (function() {
        let l;
        let len1;
        let ref2;
        let results;
        ref2 = [vi, vj];
        results = [];
        for (l = 0, len1 = ref2.length; l < len1; l++) {
          v = ref2[l];
          results.push(this.classifyVertex(v));
        }
        return results;
      }).call(this);
      ti = ref2[0];
      tj = ref2[1];
      if (ti !== BACK) {
        f.push(vi);
      }
      if (ti !== FRONT) {
        b.push(vi);
      }
      if ((ti | tj) === SPANNING) {
        t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().sub(vi));
        v = vi.interpolate(vj, t);
        f.push(v);
        b.push(v);
      }
    }
    return returning((polys = []), (function(_this) {
      return function() {
        if (f.length >= 3) {
          polys.push(new ThreeBSP.Polygon(f));
        }
        if (b.length >= 3) {
          return polys.push(new ThreeBSP.Polygon(b));
        }
      };
    })(this));
  };
  Polygon.prototype.subdivide = function(polygon, coplanarFront, coplanarBack, front, back) {
    let k;
    let len;
    let poly;
    let ref;
    let results;
    let side;
    ref = this.tessellate(polygon);
    results = [];
    for (k = 0, len = ref.length; k < len; k++) {
      poly = ref[k];
      side = this.classifySide(poly);
      switch (side) {
        case FRONT:
          results.push(front.push(poly));
          break;
        case BACK:
          results.push(back.push(poly));
          break;
        case COPLANAR:
          if (this.normal.dot(poly.normal) > 0) {
            results.push(coplanarFront.push(poly));
          } else {
            results.push(coplanarBack.push(poly));
          }
          break;
        default:
          throw new Error('BUG: Polygon of classification ' + side + ' in subdivision"');
      }
    }
    return results;
  };
  return Polygon;
})();
ThreeBSP.Node = (function() {
  Node.prototype.clone = function() {
    let node;
    return returning((node = new ThreeBSP.Node(this.options)), (function(_this) {
      return function() {
        let ref;
        node.divider = (ref = _this.divider) != null ? ref.clone() : void 0;
        node.polygons = _this.options.timer.doTask(function() {
          let k, len, p, ref1, results;
          ref1 = _this.polygons;
          results = [];
          for (k = 0, len = ref1.length; k < len; k++) {
            p = ref1[k];
            results.push(p.clone());
          }
          return results;
        });
        node.front = _this.options.timer.doTask(function() {
          let ref1;
          return (ref1 = _this.front) != null ? ref1.clone() : void 0;
        });
        node.back = _this.options.timer.doTask(function() {
          let ref1;
          return (ref1 = _this.back) != null ? ref1.clone() : void 0;
        });
        return node.back;
      };
    })(this));
  };
  function Node(polygons, options) {
    this.options = options != null ? options : {};
    this.clipTo = bind(this.clipTo, this);
    this.clipPolygons = bind(this.clipPolygons, this);
    this.invert = bind(this.invert, this);
    this.allPolygons = bind(this.allPolygons, this);
    this.isConvex = bind(this.isConvex, this);
    this.build = bind(this.build, this);
    this.clone = bind(this.clone, this);
    if ((polygons != null) && !(polygons instanceof Array)) {
      this.options = polygons;
      polygons = void 0;
    }
    this.polygons = [];
    this.options.timer.doTask((function(_this) {
      return function() {
        if ((polygons != null) && polygons.length) {
          return _this.build(polygons);
        }
      };
    })(this));
  }
  Node.prototype.build = function(polygons) {
    return returning(this, (function(_this) {
      return function() {
        let polys, results, side, sides;
        sides = {
          front: [],
          back: []
        };
        if (_this.divider == null) {
          _this.divider = polygons[0].clone();
        }
        _this.options.timer.doTask(function() {
          let k, len, poly, results;
          results = [];
          for (k = 0, len = polygons.length; k < len; k++) {
            poly = polygons[k];
            results.push(_this.options.timer.doTask(function() {
              return _this.divider.subdivide(poly, _this.polygons, _this.polygons, sides.front, sides.back);
            }));
          }
          return results;
        });
        results = [];
        for (side in sides) {
          if (!hasProp.call(sides, side)) continue;
          polys = sides[side];
          if (polys.length) {
            if (_this[side] == null) {
              _this[side] = new ThreeBSP.Node(_this.options);
            }
            results.push(_this[side].build(polys));
          } else {
            results.push(void 0);
          }
        }
        return results;
      };
    })(this));
  };
  Node.prototype.isConvex = function(polys) {
    let inner, k, l, len, len1, outer;
    for (k = 0, len = polys.length; k < len; k++) {
      inner = polys[k];
      for (l = 0, len1 = polys.length; l < len1; l++) {
        outer = polys[l];
        if (inner !== outer && outer.classifySide(inner) !== BACK) {
          return false;
        }
      }
    }
    return true;
  };
  Node.prototype.allPolygons = function() {
    return this.options.timer.doTask((function(_this) {
      return function() {
        var ref, ref1;
        return _this.polygons.slice().concat(((ref1 = _this.front) != null ? ref1.allPolygons() : void 0) || []).concat(((ref = _this.back) != null ? ref.allPolygons() : void 0) || []);
      };
    })(this));
  };
  Node.prototype.invert = function() {
    return returning(this, (function(_this) {
      return function() {
        return _this.options.timer.doTask(function() {
          let flipper, k, l, len, len1, poly, ref, ref1, ref2;
          ref = _this.polygons;
          for (k = 0, len = ref.length; k < len; k++) {
            poly = ref[k];
            _this.options.timer.doTask(function() {
              return poly.invert();
            });
          }
          ref1 = [_this.divider, _this.front, _this.back];
          for (l = 0, len1 = ref1.length; l < len1; l++) {
            flipper = ref1[l];
            _this.options.timer.doTask(function() {
              return flipper != null ? flipper.invert() : void 0;
            });
          }
          return ref2 = [_this.back, _this.front], _this.front = ref2[0], _this.back = ref2[1], ref2;
        });
      };
    })(this));
  };
  Node.prototype.clipPolygons = function(polygons) {
    return this.options.timer.doTask((function(_this) {
      return function() {
        let back, front, k, len, poly;
        if (!_this.divider) {
          return polygons.slice();
        }
        front = [];
        back = [];
        for (k = 0, len = polygons.length; k < len; k++) {
          poly = polygons[k];
          _this.options.timer.doTask(function() {
            return _this.divider.subdivide(poly, front, back, front, back);
          });
        }
        if (_this.front) {
          front = _this.front.clipPolygons(front);
        }
        if (_this.back) {
          back = _this.back.clipPolygons(back);
        }
        if (_this.back) {
          return front.concat(back);
        } else {
          return front;
        }
      };
    })(this));
  };
  Node.prototype.clipTo = function(node) {
    return returning(this, (function(_this) {
      return function() {
        return _this.options.timer.doTask(function() {
          let ref, ref1;
          _this.polygons = node.clipPolygons(_this.polygons);
          if ((ref = _this.front) != null) {
            ref.clipTo(node);
          }
          return (ref1 = _this.back) != null ? ref1.clipTo(node) : void 0;
        });
      };
    })(this));
  };
  return Node;
})();

export default ThreeBSP;