def make_convex_hull(self, positions=None, collinear_tolerance=DEFAULT_COLLINEAR_TOLERANCE,
                         coplanar_tolerance=DEFAULT_COPLANAR_TOLERANCE):
        positions = positions or self.positions
        # Use a 3D generalisation of a Graham Scan to facilitate a triangulation of the hull in generation.
        # Time complexity: O(nh) for n positions, and h out-positions.

        # Find first vertex on hull as minimal lexicographically ordered position
        i0 = 0
        minp = positions[0]
        for i in range(1, len(positions)):
            if positions[i] < minp:
                i0 = i
                minp = positions[i]

        # Find second vertex by performing a 2D graham scan step on the xy-plane projections of positions.
        i1 = -1
        (cos1, lsq1) = (-2, 0) # will always be overriden as cos(theta) > -2
        (p0x, p0y, _) = minp
        for i in range(len(positions)):
            if i == i0:
                continue

            (px, py, _) = positions[i]
            dx = px - p0x
            dy = py - p0y
            lsq = (dx * dx) + (dy * dy)
            if lsq == 0:
                if i1 == -1:
                    i1 = i
                continue

            cos = dy / math.sqrt(lsq)
            if cos > cos1 or (cos == cos1 and lsq > lsq1):
                cos1 = cos
                lsq1 = lsq
                i1 = i

        # Dictionary of visited edges to avoid duplicates
        # List of open edges to be visited by graham scan
        closedset = set()
        openset = [ (i0, i1), (i1, i0) ]

        # Mapping from old vertex index to new index for those vertices used by hull.
        outv = { i0: 0, i1: 1 }
        new_index = 2

        # Output triangles for hull
        outtriangles = [ ]

        while len(openset) > 0:
            (i0, i1) = openset.pop()
            if (i0, i1) in closedset:
                continue

            # Find next vertex on hull to form triangle with.
            i2 = -1

            p0 = positions[i0]
            edge = vmath.v3sub(positions[i1], p0)
            isq = 1.0 / vmath.v3lengthsq(edge)

            for i in range(len(positions)):
                if i == i0 or i == i1:
                    continue

                p = positions[i]
                # Find closest point on line containing the edge to determine vector to p
                # Perpendicular to edge, this is not necessary for computing the turn
                # since the value of 'turn' computed is actually the same whether we do this
                # or not, however it is needed to be able to sort equal turn vertices
                # by distance.
                t = vmath.v3dot(vmath.v3sub(p, p0), edge) * isq
                pedge = vmath.v3sub(p, vmath.v3add(p0, vmath.v3muls(edge, t)))

                # Ignore vertex if |pedge| = 0, thus ignoring vertices on the edge itself
                # And so avoid generating degenerate triangles.
                #
                # epsilon chosen by experiment
                plsq = vmath.v3lengthsq(pedge)
                if plsq <= collinear_tolerance:
                    continue

                if i2 == -1:
                    i2 = i
                    maxpedge = pedge
                    maxplsq = plsq
                    maxt = t
                    continue

                # If this is not the first vertex tested, determine if new vertex makes
                # A right turn looking in direction of edge, or is further in same direction.
                #
                # We require a special case when pedge, and maxpedge are coplanar with edge
                # As the computed turn will be 0 and we must check if the cross product
                # Is facing into the hull or outside to determine left/right instead.
                axis = vmath.v3cross(pedge, maxpedge)
                coplanar = vmath.v3dot(pedge, vmath.v3cross(edge, maxpedge))
                # epsilon chosen by experiment
                if (coplanar * coplanar) <= coplanar_tolerance:
                    # Special case for coplanar pedge, maxpedge, edge
                    #
                    # if edges are in same direction, base on distance.
                    if vmath.v3dot(pedge, maxpedge) >= 0:
                        if plsq > maxplsq or (plsq == maxplsq and t > maxt):
                            i2 = i
                            maxpedge = pedge
                            maxplsq = plsq
                            maxt = t
                    else:
                        axis = vmath.v3cross(vmath.v3sub(p, p0), edge)
                        # Check if axis points into the hull.
                        internal = True
                        for p in positions:
                            if vmath.v3dot(axis, vmath.v3sub(p, p0)) < 0:
                                internal = False
                                break

                        if internal:
                            i2 = i
                            maxpedge = pedge
                            maxplsq = plsq
                            maxt = t
                else:
                    turn = vmath.v3dot(axis, edge)
                    # epsilon chosen by experiment
                    if turn < 0 or (turn <= collinear_tolerance and plsq > maxplsq):
                        i2 = i
                        maxpedge = pedge
                        maxplsq = plsq
                        maxt = t

            # Append i2 vertex to hull
            if i2 not in outv:
                outv[i2] = new_index
                new_index += 1

            # Form triangle iff no edge is closed.
            if ( (i0, i1) not in closedset and
                 (i1, i2) not in closedset and
                 (i2, i0) not in closedset ):

                outtriangles.append((i0, i1, i2))
                # Mark visited edges. Open new edges.
                closedset.add((i0, i1))
                closedset.add((i1, i2))
                closedset.add((i2, i0))

                openset.append((i2, i1))
                openset.append((i0, i2))

        # cnt does not 'need' to be len(positions) for convex hull to have succeeded
        #   but numerical issues with not using say fixed point means that we cannot
        #   be sure of success if it is not equal.
        # Obvious side effect is input mesh must already be a convex hull with no
        #   unnecessary vertices.
        cnt = len(outv.items())
        if cnt != len(positions):
            return None

        # Compute output mesh.
        mesh = Mesh()
        mesh.positions = [0] * cnt
        for (i, j) in outv.items():
            mesh.positions[j] = positions[i]
        mesh.primitives = [(outv[i1], outv[i2], outv[i3]) for (i1, i2, i3) in outtriangles]

        # Ensure algorithm has not failed!
        if not mesh.is_convex() or not mesh.simply_closed():
            return None

        return mesh
    # pylint: enable=R0914
Implementation of 'vg delete' command.
dataset/ETHPy150Open turbulenz/turbulenz_tools/turbulenz_tools/tools/mesh.py/Mesh.make_convex_hull--crossover with--dataset/ETHPy150Open openlmi/openlmi-scripts/commands/storage/lmi/scripts/storage/cmd/vg.py/VGDelete.execute