computeHorizon (eyePoint, crossEdge, face, horizon) {
    // moves face's vertices to the `unclaimed` vertex list
    this.deleteFaceVertices(face)

    face.mark = DELETED

    let edge
    if (!crossEdge) {
      edge = crossEdge = face.getEdge(0)
    } else {
      // start from the next edge since `crossEdge` was already analyzed
      // (actually `crossEdge.opposite` was the face who called this method
      // recursively)
      edge = crossEdge.next
    }

    // All the faces that are able to see `eyeVertex` are defined as follows
    //
    //       v    /
    //           / <== visible face
    //          /
    //         |
    //         | <== not visible face
    //
    //  dot(v, visible face normal) - visible face offset > this.tolerance
    //
    do {
      const oppositeEdge = edge.opposite
      const oppositeFace = oppositeEdge.face
      if (oppositeFace.mark === VISIBLE) {
        if (oppositeFace.distanceToPlane(eyePoint) > this.tolerance) {
          this.computeHorizon(eyePoint, oppositeEdge, oppositeFace, horizon)
        } else {
          horizon.push(edge)
        }
      }
      edge = edge.next
    } while (edge !== crossEdge)
  }
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`

@param {number[]} eyePoint - The coordinates of a point
@param {HalfEdge} crossEdge - The edge used to jump to the current `face`
@param {Face} face - The current face being tested
@param {HalfEdge[]} horizon - The edges that form part of the horizon in
ccw order