"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const Visible = 0;
const Deleted = 1;
const _v1 = /* @__PURE__ */ new THREE.Vector3();
const _line3 = /* @__PURE__ */ new THREE.Line3();
const _plane = /* @__PURE__ */ new THREE.Plane();
const _closestPoint = /* @__PURE__ */ new THREE.Vector3();
const _triangle = /* @__PURE__ */ new THREE.Triangle();
class ConvexHull {
  constructor() {
    this.tolerance = -1;
    this.faces = [];
    this.newFaces = [];
    this.assigned = new VertexList();
    this.unassigned = new VertexList();
    this.vertices = [];
  }
  setFromPoints(points) {
    if (points.length >= 4) {
      this.makeEmpty();
      for (let i = 0, l = points.length; i < l; i++) {
        this.vertices.push(new VertexNode(points[i]));
      }
      this.compute();
    }
    return this;
  }
  setFromObject(object) {
    const points = [];
    object.updateMatrixWorld(true);
    object.traverse(function(node) {
      const geometry = node.geometry;
      if (geometry !== void 0) {
        const attribute = geometry.attributes.position;
        if (attribute !== void 0) {
          for (let i = 0, l = attribute.count; i < l; i++) {
            const point = new THREE.Vector3();
            point.fromBufferAttribute(attribute, i).applyMatrix4(node.matrixWorld);
            points.push(point);
          }
        }
      }
    });
    return this.setFromPoints(points);
  }
  containsPoint(point) {
    const faces = this.faces;
    for (let i = 0, l = faces.length; i < l; i++) {
      const face = faces[i];
      if (face.distanceToPoint(point) > this.tolerance)
        return false;
    }
    return true;
  }
  intersectRay(ray, target) {
    const faces = this.faces;
    let tNear = -Infinity;
    let tFar = Infinity;
    for (let i = 0, l = faces.length; i < l; i++) {
      const face = faces[i];
      const vN = face.distanceToPoint(ray.origin);
      const vD = face.normal.dot(ray.direction);
      if (vN > 0 && vD >= 0)
        return null;
      const t = vD !== 0 ? -vN / vD : 0;
      if (t <= 0)
        continue;
      if (vD > 0) {
        tFar = Math.min(t, tFar);
      } else {
        tNear = Math.max(t, tNear);
      }
      if (tNear > tFar) {
        return null;
      }
    }
    if (tNear !== -Infinity) {
      ray.at(tNear, target);
    } else {
      ray.at(tFar, target);
    }
    return target;
  }
  intersectsRay(ray) {
    return this.intersectRay(ray, _v1) !== null;
  }
  makeEmpty() {
    this.faces = [];
    this.vertices = [];
    return this;
  }
  // Adds a vertex to the 'assigned' list of vertices and assigns it to the given face
  addVertexToFace(vertex, face) {
    vertex.face = face;
    if (face.outside === null) {
      this.assigned.append(vertex);
    } else {
      this.assigned.insertBefore(face.outside, vertex);
    }
    face.outside = vertex;
    return this;
  }
  // Removes a vertex from the 'assigned' list of vertices and from the given face
  removeVertexFromFace(vertex, face) {
    if (vertex === face.outside) {
      if (vertex.next !== null && vertex.next.face === face) {
        face.outside = vertex.next;
      } else {
        face.outside = null;
      }
    }
    this.assigned.remove(vertex);
    return this;
  }
  // Removes all the visible vertices that a given face is able to see which are stored in the 'assigned' vertex list
  removeAllVerticesFromFace(face) {
    if (face.outside !== null) {
      const start = face.outside;
      let end = face.outside;
      while (end.next !== null && end.next.face === face) {
        end = end.next;
      }
      this.assigned.removeSubList(start, end);
      start.prev = end.next = null;
      face.outside = null;
      return start;
    }
  }
  // Removes all the visible vertices that 'face' is able to see
  deleteFaceVertices(face, absorbingFace) {
    const faceVertices = this.removeAllVerticesFromFace(face);
    if (faceVertices !== void 0) {
      if (absorbingFace === void 0) {
        this.unassigned.appendChain(faceVertices);
      } else {
        let vertex = faceVertices;
        do {
          const nextVertex = vertex.next;
          const distance = absorbingFace.distanceToPoint(vertex.point);
          if (distance > this.tolerance) {
            this.addVertexToFace(vertex, absorbingFace);
          } else {
            this.unassigned.append(vertex);
          }
          vertex = nextVertex;
        } while (vertex !== null);
      }
    }
    return this;
  }
  // Reassigns as many vertices as possible from the unassigned list to the new faces
  resolveUnassignedPoints(newFaces) {
    if (this.unassigned.isEmpty() === false) {
      let vertex = this.unassigned.first();
      do {
        const nextVertex = vertex.next;
        let maxDistance = this.tolerance;
        let maxFace = null;
        for (let i = 0; i < newFaces.length; i++) {
          const face = newFaces[i];
          if (face.mark === Visible) {
            const distance = face.distanceToPoint(vertex.point);
            if (distance > maxDistance) {
              maxDistance = distance;
              maxFace = face;
            }
            if (maxDistance > 1e3 * this.tolerance)
              break;
          }
        }
        if (maxFace !== null) {
          this.addVertexToFace(vertex, maxFace);
        }
        vertex = nextVertex;
      } while (vertex !== null);
    }
    return this;
  }
  // Computes the extremes of a simplex which will be the initial hull
  computeExtremes() {
    const min = new THREE.Vector3();
    const max = new THREE.Vector3();
    const minVertices = [];
    const maxVertices = [];
    for (let i = 0; i < 3; i++) {
      minVertices[i] = maxVertices[i] = this.vertices[0];
    }
    min.copy(this.vertices[0].point);
    max.copy(this.vertices[0].point);
    for (let i = 0, l = this.vertices.length; i < l; i++) {
      const vertex = this.vertices[i];
      const point = vertex.point;
      for (let j = 0; j < 3; j++) {
        if (point.getComponent(j) < min.getComponent(j)) {
          min.setComponent(j, point.getComponent(j));
          minVertices[j] = vertex;
        }
      }
      for (let j = 0; j < 3; j++) {
        if (point.getComponent(j) > max.getComponent(j)) {
          max.setComponent(j, point.getComponent(j));
          maxVertices[j] = vertex;
        }
      }
    }
    this.tolerance = 3 * Number.EPSILON * (Math.max(Math.abs(min.x), Math.abs(max.x)) + Math.max(Math.abs(min.y), Math.abs(max.y)) + Math.max(Math.abs(min.z), Math.abs(max.z)));
    return { min: minVertices, max: maxVertices };
  }
  // Computes the initial simplex assigning to its faces all the points
  // that are candidates to form part of the hull
  computeInitialHull() {
    const vertices = this.vertices;
    const extremes = this.computeExtremes();
    const min = extremes.min;
    const max = extremes.max;
    let maxDistance = 0;
    let index = 0;
    for (let i = 0; i < 3; i++) {
      const distance = max[i].point.getComponent(i) - min[i].point.getComponent(i);
      if (distance > maxDistance) {
        maxDistance = distance;
        index = i;
      }
    }
    const v0 = min[index];
    const v1 = max[index];
    let v2;
    let v3;
    maxDistance = 0;
    _line3.set(v0.point, v1.point);
    for (let i = 0, l = this.vertices.length; i < l; i++) {
      const vertex = vertices[i];
      if (vertex !== v0 && vertex !== v1) {
        _line3.closestPointToPoint(vertex.point, true, _closestPoint);
        const distance = _closestPoint.distanceToSquared(vertex.point);
        if (distance > maxDistance) {
          maxDistance = distance;
          v2 = vertex;
        }
      }
    }
    maxDistance = -1;
    _plane.setFromCoplanarPoints(v0.point, v1.point, v2.point);
    for (let i = 0, l = this.vertices.length; i < l; i++) {
      const vertex = vertices[i];
      if (vertex !== v0 && vertex !== v1 && vertex !== v2) {
        const distance = Math.abs(_plane.distanceToPoint(vertex.point));
        if (distance > maxDistance) {
          maxDistance = distance;
          v3 = vertex;
        }
      }
    }
    const faces = [];
    if (_plane.distanceToPoint(v3.point) < 0) {
      faces.push(Face.create(v0, v1, v2), Face.create(v3, v1, v0), Face.create(v3, v2, v1), Face.create(v3, v0, v2));
      for (let i = 0; i < 3; i++) {
        const j = (i + 1) % 3;
        faces[i + 1].getEdge(2).setTwin(faces[0].getEdge(j));
        faces[i + 1].getEdge(1).setTwin(faces[j + 1].getEdge(0));
      }
    } else {
      faces.push(Face.create(v0, v2, v1), Face.create(v3, v0, v1), Face.create(v3, v1, v2), Face.create(v3, v2, v0));
      for (let i = 0; i < 3; i++) {
        const j = (i + 1) % 3;
        faces[i + 1].getEdge(2).setTwin(faces[0].getEdge((3 - i) % 3));
        faces[i + 1].getEdge(0).setTwin(faces[j + 1].getEdge(1));
      }
    }
    for (let i = 0; i < 4; i++) {
      this.faces.push(faces[i]);
    }
    for (let i = 0, l = vertices.length; i < l; i++) {
      const vertex = vertices[i];
      if (vertex !== v0 && vertex !== v1 && vertex !== v2 && vertex !== v3) {
        maxDistance = this.tolerance;
        let maxFace = null;
        for (let j = 0; j < 4; j++) {
          const distance = this.faces[j].distanceToPoint(vertex.point);
          if (distance > maxDistance) {
            maxDistance = distance;
            maxFace = this.faces[j];
          }
        }
        if (maxFace !== null) {
          this.addVertexToFace(vertex, maxFace);
        }
      }
    }
    return this;
  }
  // Removes inactive faces
  reindexFaces() {
    const activeFaces = [];
    for (let i = 0; i < this.faces.length; i++) {
      const face = this.faces[i];
      if (face.mark === Visible) {
        activeFaces.push(face);
      }
    }
    this.faces = activeFaces;
    return this;
  }
  // Finds the next vertex to create faces with the current hull
  nextVertexToAdd() {
    if (this.assigned.isEmpty() === false) {
      let eyeVertex, maxDistance = 0;
      const eyeFace = this.assigned.first().face;
      let vertex = eyeFace.outside;
      do {
        const distance = eyeFace.distanceToPoint(vertex.point);
        if (distance > maxDistance) {
          maxDistance = distance;
          eyeVertex = vertex;
        }
        vertex = vertex.next;
      } while (vertex !== null && vertex.face === eyeFace);
      return eyeVertex;
    }
  }
  // Computes a chain of half edges in CCW order called the 'horizon'.
  // For an edge to be part of the horizon it must join a face that can see
  // 'eyePoint' and a face that cannot see 'eyePoint'.
  computeHorizon(eyePoint, crossEdge, face, horizon) {
    this.deleteFaceVertices(face);
    face.mark = Deleted;
    let edge;
    if (crossEdge === null) {
      edge = crossEdge = face.getEdge(0);
    } else {
      edge = crossEdge.next;
    }
    do {
      const twinEdge = edge.twin;
      const oppositeFace = twinEdge.face;
      if (oppositeFace.mark === Visible) {
        if (oppositeFace.distanceToPoint(eyePoint) > this.tolerance) {
          this.computeHorizon(eyePoint, twinEdge, oppositeFace, horizon);
        } else {
          horizon.push(edge);
        }
      }
      edge = edge.next;
    } while (edge !== crossEdge);
    return this;
  }
  // Creates a face with the vertices 'eyeVertex.point', 'horizonEdge.tail' and 'horizonEdge.head' in CCW order
  addAdjoiningFace(eyeVertex, horizonEdge) {
    const face = Face.create(eyeVertex, horizonEdge.tail(), horizonEdge.head());
    this.faces.push(face);
    face.getEdge(-1).setTwin(horizonEdge.twin);
    return face.getEdge(0);
  }
  //  Adds 'horizon.length' faces to the hull, each face will be linked with the
  //  horizon opposite face and the face on the left/right
  addNewFaces(eyeVertex, horizon) {
    this.newFaces = [];
    let firstSideEdge = null;
    let previousSideEdge = null;
    for (let i = 0; i < horizon.length; i++) {
      const horizonEdge = horizon[i];
      const sideEdge = this.addAdjoiningFace(eyeVertex, horizonEdge);
      if (firstSideEdge === null) {
        firstSideEdge = sideEdge;
      } else {
        sideEdge.next.setTwin(previousSideEdge);
      }
      this.newFaces.push(sideEdge.face);
      previousSideEdge = sideEdge;
    }
    firstSideEdge.next.setTwin(previousSideEdge);
    return this;
  }
  // Adds a vertex to the hull
  addVertexToHull(eyeVertex) {
    const horizon = [];
    this.unassigned.clear();
    this.removeVertexFromFace(eyeVertex, eyeVertex.face);
    this.computeHorizon(eyeVertex.point, null, eyeVertex.face, horizon);
    this.addNewFaces(eyeVertex, horizon);
    this.resolveUnassignedPoints(this.newFaces);
    return this;
  }
  cleanup() {
    this.assigned.clear();
    this.unassigned.clear();
    this.newFaces = [];
    return this;
  }
  compute() {
    let vertex;
    this.computeInitialHull();
    while ((vertex = this.nextVertexToAdd()) !== void 0) {
      this.addVertexToHull(vertex);
    }
    this.reindexFaces();
    this.cleanup();
    return this;
  }
}
const Face = /* @__PURE__ */ (() => {
  class Face2 {
    constructor() {
      this.normal = new THREE.Vector3();
      this.midpoint = new THREE.Vector3();
      this.area = 0;
      this.constant = 0;
      this.outside = null;
      this.mark = Visible;
      this.edge = null;
    }
    static create(a, b, c) {
      const face = new Face2();
      const e0 = new HalfEdge(a, face);
      const e1 = new HalfEdge(b, face);
      const e2 = new HalfEdge(c, face);
      e0.next = e2.prev = e1;
      e1.next = e0.prev = e2;
      e2.next = e1.prev = e0;
      face.edge = e0;
      return face.compute();
    }
    getEdge(i) {
      let edge = this.edge;
      while (i > 0) {
        edge = edge.next;
        i--;
      }
      while (i < 0) {
        edge = edge.prev;
        i++;
      }
      return edge;
    }
    compute() {
      const a = this.edge.tail();
      const b = this.edge.head();
      const c = this.edge.next.head();
      _triangle.set(a.point, b.point, c.point);
      _triangle.getNormal(this.normal);
      _triangle.getMidpoint(this.midpoint);
      this.area = _triangle.getArea();
      this.constant = this.normal.dot(this.midpoint);
      return this;
    }
    distanceToPoint(point) {
      return this.normal.dot(point) - this.constant;
    }
  }
  return Face2;
})();
class HalfEdge {
  constructor(vertex, face) {
    this.vertex = vertex;
    this.prev = null;
    this.next = null;
    this.twin = null;
    this.face = face;
  }
  head() {
    return this.vertex;
  }
  tail() {
    return this.prev ? this.prev.vertex : null;
  }
  length() {
    const head = this.head();
    const tail = this.tail();
    if (tail !== null) {
      return tail.point.distanceTo(head.point);
    }
    return -1;
  }
  lengthSquared() {
    const head = this.head();
    const tail = this.tail();
    if (tail !== null) {
      return tail.point.distanceToSquared(head.point);
    }
    return -1;
  }
  setTwin(edge) {
    this.twin = edge;
    edge.twin = this;
    return this;
  }
}
class VertexNode {
  constructor(point) {
    this.point = point;
    this.prev = null;
    this.next = null;
    this.face = null;
  }
}
class VertexList {
  constructor() {
    this.head = null;
    this.tail = null;
  }
  first() {
    return this.head;
  }
  last() {
    return this.tail;
  }
  clear() {
    this.head = this.tail = null;
    return this;
  }
  // Inserts a vertex before the target vertex
  insertBefore(target, vertex) {
    vertex.prev = target.prev;
    vertex.next = target;
    if (vertex.prev === null) {
      this.head = vertex;
    } else {
      vertex.prev.next = vertex;
    }
    target.prev = vertex;
    return this;
  }
  // Inserts a vertex after the target vertex
  insertAfter(target, vertex) {
    vertex.prev = target;
    vertex.next = target.next;
    if (vertex.next === null) {
      this.tail = vertex;
    } else {
      vertex.next.prev = vertex;
    }
    target.next = vertex;
    return this;
  }
  // Appends a vertex to the end of the linked list
  append(vertex) {
    if (this.head === null) {
      this.head = vertex;
    } else {
      this.tail.next = vertex;
    }
    vertex.prev = this.tail;
    vertex.next = null;
    this.tail = vertex;
    return this;
  }
  // Appends a chain of vertices where 'vertex' is the head.
  appendChain(vertex) {
    if (this.head === null) {
      this.head = vertex;
    } else {
      this.tail.next = vertex;
    }
    vertex.prev = this.tail;
    while (vertex.next !== null) {
      vertex = vertex.next;
    }
    this.tail = vertex;
    return this;
  }
  // Removes a vertex from the linked list
  remove(vertex) {
    if (vertex.prev === null) {
      this.head = vertex.next;
    } else {
      vertex.prev.next = vertex.next;
    }
    if (vertex.next === null) {
      this.tail = vertex.prev;
    } else {
      vertex.next.prev = vertex.prev;
    }
    return this;
  }
  // Removes a list of vertices whose 'head' is 'a' and whose 'tail' is b
  removeSubList(a, b) {
    if (a.prev === null) {
      this.head = b.next;
    } else {
      a.prev.next = b.next;
    }
    if (b.next === null) {
      this.tail = a.prev;
    } else {
      b.next.prev = a.prev;
    }
    return this;
  }
  isEmpty() {
    return this.head === null;
  }
}
exports.ConvexHull = ConvexHull;
exports.Face = Face;
exports.HalfEdge = HalfEdge;
exports.VertexList = VertexList;
exports.VertexNode = VertexNode;
//# sourceMappingURL=ConvexHull.cjs.map
