ThreeBSP.js

699 阅读1分钟

ThreeBSP.js源码编译后js文件

官方仓库CoffeeScript代码编写 也可以自行拉去,根据官方文档进行编译 编译脚本 首先全局下载coffeescript


npm install --global coffeescript

编译脚本(切换到src目录下)


coffee -c .\ThreeBSP.coffee     

// Generated by CoffeeScript 2.7.0
(function () {
  var BACK, COPLANAR, EPSILON, FRONT, SPANNING, Timelimit, ref, returning,
    boundMethodCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } },
    hasProp = {}.hasOwnProperty;

  EPSILON = 1e-5;

  COPLANAR = 0;

  FRONT = 1;

  BACK = 2;

  SPANNING = 3;

  // Call the callback with no arguments
  // then return the first value.
  // Used to construct chainable
  // callbacks
  returning = function (value, fn) {
    fn();
    return value;
  };

  Timelimit = class Timelimit {
    constructor(timeout, progress) {
      this.check = this.check.bind(this);
      this.start = this.start.bind(this);
      this.finish = this.finish.bind(this);
      this.doTask = this.doTask.bind(this);
      this.timeout = timeout;
      this.progress = progress;
      "NOTHING";
    }

    check() {
      var elapsed;
      if (this.started == null) {
        return;
      }
      return returning((elapsed = Date.now() - this.started), () => {
        var 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.`);
        }
      });
    }

    start() {
      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();
    }

    finish() {
      var 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`;
        return this.started = this.done = this.total = void 0;
      }
    }

    doTask(block) {
      var result;
      this.start();
      result = block();
      this.finish();
      return result;
    }

  };

  //#
  //# ThreBSP Driver

  // Can be instantiated with THREE.Geometry,
  // THREE.Mesh or a ThreeBSP.Node
  window.ThreeBSP = class ThreeBSP {
    constructor(treeIsh, matrix1, options = {}) {
      var base, ref, ref1, ref2, ref3;
      // Evaluate block after replacing @timer with new_timer
      // then put @timer back after block returns
      this.withTimer = this.withTimer.bind(this);
      this.toTree = this.toTree.bind(this);
      // Converters/Exporters
      this.toMesh = this.toMesh.bind(this);
      this.toGeometry = this.toGeometry.bind(this);
      // CSG Operations
      this.subtract = this.subtract.bind(this);
      this.union = this.union.bind(this);
      this.intersect = this.intersect.bind(this);
      this.matrix = matrix1;
      this.options = options;
      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();
      }
      // Start a timer if one wasn't passed
      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);
      }
      this.tree = this.toTree(treeIsh);
    }

    withTimer(new_timer, block) {
      var old_timer;
      old_timer = this.options.timer;
      try {
        this.options.timer = new_timer;
        return block();
      } finally {
        this.options.timer = old_timer;
      }
    }

    toTree(treeIsh) {
      var face, 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;
      for (i = k = 0, len = ref.length; k < len; i = ++k) {
        face = ref[i];
        ((face, i) => {
          var 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());
        })(face, i);
      }
      return new ThreeBSP.Node(polygons, this.options);
    }

    toMesh(material = new THREE.MeshNormalMaterial()) {
      return this.options.timer.doTask(() => {
        var geometry, mesh;
        geometry = this.toGeometry();
        return returning((mesh = new THREE.Mesh(geometry, material)), () => {
          mesh.position.getPositionFromMatrix(this.matrix);
          return mesh.rotation.setEulerFromRotationMatrix(this.matrix);
        });
      });
    }

    toGeometry() {
      return this.options.timer.doTask(() => {
        var geometry, matrix;
        matrix = new THREE.Matrix4().getInverse(this.matrix);
        return returning((geometry = new THREE.Geometry()), () => {
          var 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(() => {
              var face, idx, l, polyVerts, ref1, results1, v, vertUvs, verts;
              polyVerts = (function () {
                var 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 () {
                  var 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 = new THREE.Face3(...((function () {
                  var 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;
                })()), polygon.normal.clone());
                geometry.faces.push(face);
                results1.push(geometry.faceVertexUvs[0].push(vertUvs));
              }
              return results1;
            }));
          }
          return results;
        });
      });
    }

    subtract(other) {
      return this.options.timer.doTask(() => {
        return other.withTimer(this.options.timer, () => {
          var them, us;
          [us, them] = [this.tree.clone(), other.tree.clone()];
          us.invert().clipTo(them);
          them.clipTo(us).invert().clipTo(us).invert();
          return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix, this.options);
        });
      });
    }

    union(other) {
      return this.options.timer.doTask(() => {
        return other.withTimer(this.options.timer, () => {
          var them, us;
          [us, them] = [this.tree.clone(), other.tree.clone()];
          us.clipTo(them);
          them.clipTo(us).invert().clipTo(us).invert();
          return new ThreeBSP(us.build(them.allPolygons()), this.matrix, this.options);
        });
      });
    }

    intersect(other) {
      return this.options.timer.doTask(() => {
        return other.withTimer(this.options.timer, () => {
          var them, us;
          [us, them] = [this.tree.clone(), other.tree.clone()];
          them.clipTo(us.invert()).invert().clipTo(us.clipTo(them));
          return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix, this.options);
        });
      });
    }

  };

  //#
  //# ThreeBSP.Vertex
  ref = ThreeBSP.Vertex = class Vertex extends THREE.Vector3 {
    constructor(x, y, z, normal = new THREE.Vector3(), uv = new THREE.Vector2()) {
      super(x, y, z);
      this.lerp = this.lerp.bind(this);
      this.interpolate = this.interpolate.bind(this);
      this.normal = normal;
      this.uv = uv;
    }

    clone() {
      return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
    }

    lerp(v, alpha) {
      boundMethodCheck(this, ref);
      return returning(super.lerp(() => {
        this.uv.add(v.uv.clone().sub(this.uv).multiplyScalar(alpha));
        return this.normal.lerp(v, alpha);
      }));
    }

    interpolate(...args) {
      boundMethodCheck(this, ref);
      return this.clone().lerp(...args);
    }

  };

  //#
  //# ThreeBSP.Polygon
  ThreeBSP.Polygon = class Polygon {
    constructor(vertices = [], normal, w) {
      this.calculateProperties = this.calculateProperties.bind(this);
      this.clone = this.clone.bind(this);
      this.invert = this.invert.bind(this);
      this.classifyVertex = this.classifyVertex.bind(this);
      this.classifySide = this.classifySide.bind(this);
      // Return a list of polygons from `poly` such
      // that no polygons span the plane defined by
      // `this`. Should be a list of one or two Polygons
      this.tessellate = this.tessellate.bind(this);
      this.subdivide = this.subdivide.bind(this);
      this.vertices = vertices;
      this.normal = normal;
      this.w = w;
      if (this.vertices.length) {
        this.calculateProperties();
      }
    }

    calculateProperties() {
      return returning(this, () => {
        var a, b, c;
        [a, b, c] = this.vertices;
        this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
        return this.w = this.normal.clone().dot(a);
      });
    }

    clone() {
      var v;
      return new ThreeBSP.Polygon((function () {
        var k, len, ref1, results;
        ref1 = this.vertices;
        results = [];
        for (k = 0, len = ref1.length; k < len; k++) {
          v = ref1[k];
          results.push(v.clone());
        }
        return results;
      }).call(this), this.normal.clone(), this.w);
    }

    invert() {
      return returning(this, () => {
        this.normal.multiplyScalar(-1);
        this.w *= -1;
        return this.vertices.reverse();
      });
    }

    classifyVertex(vertex) {
      var side;
      side = this.normal.dot(vertex) - this.w;
      switch (false) {
        case !(side < -EPSILON):
          return BACK;
        case !(side > EPSILON):
          return FRONT;
        default:
          return COPLANAR;
      }
    }

    classifySide(polygon) {
      var back, front, tally, v;
      [front, back] = [0, 0];
      tally = (v) => {
        switch (this.classifyVertex(v)) {
          case FRONT:
            return front += 1;
          case BACK:
            return back += 1;
        }
      };
      (function () {
        var k, len, ref1, results;
        ref1 = polygon.vertices;
        results = [];
        for (k = 0, len = ref1.length; k < len; k++) {
          v = ref1[k];
          results.push(tally(v));
        }
        return results;
      })();
      if (front > 0 && back === 0) {
        return FRONT;
      }
      if (front === 0 && back > 0) {
        return BACK;
      }
      if ((front === back && back === 0)) {
        return COPLANAR;
      }
      return SPANNING;
    }

    tessellate(poly) {
      var b, count, f, i, j, k, len, polys, ref1, t, ti, tj, v, vi, vj;
      ({ f, b, count } = {
        f: [],
        b: [],
        count: poly.vertices.length
      });
      if (this.classifySide(poly) !== SPANNING) {
        return [poly];
      }
      ref1 = poly.vertices;
      // vi and vj are the current and next Vertex
      // i  and j  are the indexes of vi and vj
      // ti and tj are the classifications of vi and vj
      for (i = k = 0, len = ref1.length; k < len; i = ++k) {
        vi = ref1[i];
        vj = poly.vertices[(j = (i + 1) % count)];
        [ti, tj] = (function () {
          var l, len1, ref2, 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);
        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 = []), () => {
        if (f.length >= 3) {
          polys.push(new ThreeBSP.Polygon(f));
        }
        if (b.length >= 3) {
          return polys.push(new ThreeBSP.Polygon(b));
        }
      });
    }

    subdivide(polygon, coplanar_front, coplanar_back, front, back) {
      var k, len, poly, ref1, results, side;
      ref1 = this.tessellate(polygon);
      results = [];
      for (k = 0, len = ref1.length; k < len; k++) {
        poly = ref1[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(coplanar_front.push(poly));
            } else {
              results.push(coplanar_back.push(poly));
            }
            break;
          default:
            throw new Error(`BUG: Polygon of classification ${side} in subdivision`);
        }
      }
      return results;
    }

  };

  //#
  //# ThreeBSP.Node
  ThreeBSP.Node = class Node {
    clone() {
      var node;
      return returning((node = new ThreeBSP.Node(this.options)), () => {
        var ref1;
        node.divider = (ref1 = this.divider) != null ? ref1.clone() : void 0;
        node.polygons = this.options.timer.doTask(() => {
          var k, len, p, ref2, results;
          ref2 = this.polygons;
          results = [];
          for (k = 0, len = ref2.length; k < len; k++) {
            p = ref2[k];
            results.push(p.clone());
          }
          return results;
        });
        node.front = this.options.timer.doTask(() => {
          var ref2;
          return (ref2 = this.front) != null ? ref2.clone() : void 0;
        });
        return node.back = this.options.timer.doTask(() => {
          var ref2;
          return (ref2 = this.back) != null ? ref2.clone() : void 0;
        });
      });
    }

    constructor(polygons, options = {}) {
      this.clone = this.clone.bind(this);
      this.build = this.build.bind(this);
      this.isConvex = this.isConvex.bind(this);
      this.allPolygons = this.allPolygons.bind(this);
      this.invert = this.invert.bind(this);
      this.clipPolygons = this.clipPolygons.bind(this);
      this.clipTo = this.clipTo.bind(this);
      this.options = options;
      if ((polygons != null) && !(polygons instanceof Array)) {
        this.options = polygons;
        polygons = void 0;
      }
      this.polygons = [];
      this.options.timer.doTask(() => {
        if ((polygons != null) && polygons.length) {
          return this.build(polygons);
        }
      });
    }

    build(polygons) {
      return returning(this, () => {
        var polys, results, side, sides;
        sides = {
          front: [],
          back: []
        };
        if (this.divider == null) {
          this.divider = polygons[0].clone();
        }
        this.options.timer.doTask(() => {
          var k, len, poly, results;
          results = [];
          for (k = 0, len = polygons.length; k < len; k++) {
            poly = polygons[k];
            results.push(this.options.timer.doTask(() => {
              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;
      });
    }

    isConvex(polys) {
      var 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;
    }

    allPolygons() {
      return this.options.timer.doTask(() => {
        var ref1, ref2;
        return this.polygons.slice().concat(((ref2 = this.front) != null ? ref2.allPolygons() : void 0) || []).concat(((ref1 = this.back) != null ? ref1.allPolygons() : void 0) || []);
      });
    }

    invert() {
      return returning(this, () => {
        return this.options.timer.doTask(() => {
          var flipper, k, l, len, len1, poly, ref1, ref2;
          ref1 = this.polygons;
          for (k = 0, len = ref1.length; k < len; k++) {
            poly = ref1[k];
            this.options.timer.doTask(() => {
              return poly.invert();
            });
          }
          ref2 = [this.divider, this.front, this.back];
          for (l = 0, len1 = ref2.length; l < len1; l++) {
            flipper = ref2[l];
            this.options.timer.doTask(() => {
              return flipper != null ? flipper.invert() : void 0;
            });
          }
          return [this.front, this.back] = [this.back, this.front];
        });
      });
    }

    clipPolygons(polygons) {
      return this.options.timer.doTask(() => {
        var 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(() => {
            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;
        }
      });
    }

    clipTo(node) {
      return returning(this, () => {
        return this.options.timer.doTask(() => {
          var ref1, ref2;
          this.polygons = node.clipPolygons(this.polygons);
          if ((ref1 = this.front) != null) {
            ref1.clipTo(node);
          }
          return (ref2 = this.back) != null ? ref2.clipTo(node) : void 0;
        });
      });
    }

  };

}).call(this);