function cutHole() {
        var points = self.points;
        var nPoints = self._nPoints;

        var holeMaxX = -Infinity;
        var holeMaxXIndex = 0;
        var holeIndex = 0;
        // Find hole which xMax is rightest
        for (var i = 0; i < xMaxOfHoles.length; i++) {
            if (xMaxOfHoles[i] > holeMaxX) {
                holeMaxX = xMaxOfHoles[i];
                holeMaxXIndex = xMaxIndicesOfHoles[i];
                holeIndex = i;
            }
        }

        var holePoints = holes[holeIndex];

        xMaxOfHoles.splice(holeIndex, 1);
        xMaxIndicesOfHoles.splice(holeIndex, 1);
        holes.splice(holeIndex, 1);

        var holePointX = holePoints[holeMaxXIndex * 2];
        var holePointY = holePoints[holeMaxXIndex * 2 + 1];
        var minRayX = Infinity;
        var edgeStartPointIndex = -1;
        // Find nearest intersected line
        for (var i = 0, j = points.length - 2; i < points.length; i += 2) {
            var x0 = points[j], y0 = points[j + 1];
            var x1 = points[i], y1 = points[i + 1];

            var rayX = intersectEdge(x0, y0, x1, y1, holePointX, holePointY);
            if (rayX >= holePointX) {
                // Intersected.
                if (rayX < minRayX) {
                    minRayX = rayX;
                    edgeStartPointIndex = j / 2;
                }
            }

            j = i;
        }
        // Didn't find
        if (edgeStartPointIndex < 0) {
            if (process.env.NODE_ENV !== 'production') {
                console.warn('Hole must be inside exterior.');
            }
            return;
        }
        var edgeEndPointIndex = (edgeStartPointIndex + 1) % (points.length / 2);
        // Point of seam edge/
        var seamPointIndex = (points[edgeStartPointIndex * 2] > points[edgeEndPointIndex * 2]) ? edgeStartPointIndex : edgeEndPointIndex;
        // Use maximum x of edge
        var seamX = points[seamPointIndex * 2];
        var seamY = points[seamPointIndex * 2 + 1];

        var minimumAngleCos = Infinity;
        // And figure out if any of reflex points is in the triangle,
        // if has, use the reflex point with minimum angle with (1, 0)
        for (var i = 0; i < nPoints; i++) {
            if (self._pointsTypes[i] === VERTEX_TYPE_REFLEX) {
                var xi = points[i * 2];
                var yi = points[i * 2 + 1];
                if (isPointInTriangle(holePointX, holePointY, minRayX, holePointY, seamX, seamY, xi, yi)) {
                    // Use dot product with (1, 0) as angle
                    var dx = xi - holePointX;
                    var dy = yi - holePointY;
                    var len = Math.sqrt(dx * dx + dy * dy);
                    dx /= len; dy /= len;
                    var angleCos = dx * dx;
                    if (angleCos < minimumAngleCos) {
                        minimumAngleCos = angleCos;
                        // Replaced seam.
                        seamPointIndex = i;
                    }
                }
            }
        }

        // TODO Use splice to add maybe slow
        var newPointsCount = nPoints + holePoints.length / 2 + 2;
        var newPoints = new Float32Array(newPointsCount * 2);
        var newPointsTypes = new Uint8Array(newPointsCount);
        seamX = points[seamPointIndex * 2];
        seamY = points[seamPointIndex * 2 + 1];

        var offPt = 0;
        var offType = 0;

        // x, y, prevX, prevY, nextX, nextY is used for point type.
        var x, y;
        var prevX, prevY, nextX, nextY;
        function copyPoints(idx, source) {
            prevX = x;
            prevY = y;
            x = newPoints[offPt++] = source[idx * 2];
            y = newPoints[offPt++] = source[idx * 2 + 1];
        }
        function guessAndAddPointType() {
            var type = triangleArea(prevX, prevY, x, y, nextX, nextY) < 0 ? VERTEX_TYPE_CONVEX : VERTEX_TYPE_REFLEX;
            newPointsTypes[offType++] = type;
        }

        for (var i = 0; i < seamPointIndex; i++) {
            copyPoints(i, points);
            newPointsTypes[offType++] = self._pointsTypes[i];
        }
        copyPoints(seamPointIndex, points);
        if (0 === seamPointIndex) { // In case first point is seam.
            prevX = points[nPoints * 2 - 2];
            prevY = points[nPoints * 2 - 1];
        }
        nextX = holePoints[holeMaxXIndex * 2];
        nextY = holePoints[holeMaxXIndex * 2 + 1];

        guessAndAddPointType();

        // Add hole
        for (var i = 0, holePointsCount = holePoints.length / 2; i < holePointsCount; i++) {
            var idx = (i + holeMaxXIndex) % holePointsCount;
            copyPoints(idx, holePoints);

            var nextIdx = (idx + 1) % holePointsCount;
            nextX = holePoints[nextIdx * 2]; nextY = holePoints[nextIdx * 2 + 1];
            guessAndAddPointType();
        }
        // Add another seam.
        copyPoints(holeMaxXIndex, holePoints);
        nextX = seamX; nextY = seamY;
        guessAndAddPointType();
        copyPoints(seamPointIndex, points);
        var nextIdx = (seamPointIndex + 1) % nPoints;
        nextX = points[nextIdx * 2]; nextY = points[nextIdx * 2 + 1];
        guessAndAddPointType();

        // Add rest
        for (var i = seamPointIndex + 1; i < nPoints; i++) {
            copyPoints(i, points);
            newPointsTypes[offType++] = self._pointsTypes[i];
        }

        // Update points and pointsTypes
        self.points = newPoints;
        self._pointsTypes = newPointsTypes;
        self._nPoints = newPointsCount;
    }
@param {Array.<number>} exterior. Exterior points
     exterior should be clockwise order. (When y is from bottom to top)
@param {Array.<Array>} holes. holes should be counter clockwise order.