Source: builders/GeometryBuilderUtils.js

/**
 * Geometry Builder Utils
 */
const GeometryBuilderUtils = {

	convertShapeDataToEarcut: function(shape, vertices, holeIndices) {
		let contour = shape.contour;
		const holes = shape.holes;

		// check directions of shape contour and holes

		if (!isClockWise(contour)) {
			contour = contour.reverse();
		}

		if (holes) {
			for (let i = 0, l = holes.length; i < l; i++) {
				const hole = holes[i];
				if (isClockWise(hole)) {
					holes[i] = hole.reverse();
				}
			}
		}

		// triangulate shape by Earcut

		vertices = vertices || []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]
		holeIndices = holeIndices || []; // array of hole indices

		removeDupEndPts(contour);
		addContour(vertices, contour);

		if (holes) {
			let holeIndex = contour.length;

			holes.forEach(removeDupEndPts);

			for (let i = 0; i < holes.length; i++) {
				holeIndices.push(holeIndex);
				holeIndex += holes[i].length;
				addContour(vertices, holes[i]);
			}
		}
	}

};

function area(contour) {
	const n = contour.length;
	let a = 0.0;

	for (let p = n - 1, q = 0; q < n; p = q++) {
		a += contour[p][0] * contour[q][1] - contour[q][0] * contour[p][1];
	}

	return a * 0.5;
}

function isClockWise(contour) {
	return area(contour) < 0;
}

function removeDupEndPts(contour) {
	const l = contour.length;

	if (l > 2 && isArrayEquals(contour[l - 1], contour[0])) {
		contour.pop();
	}
}

function isArrayEquals(a1, a2) {
	if (a1.length !== a2.length) {
		return false;
	}

	for (let i = 0, l = a1.length; i < l; i++) {
		if (a1[i] !== a2[i]) {
			return false;
		}
	}

	return true;
}

function addContour(vertices, contour) {
	for (let i = 0; i < contour.length; i++) {
		vertices.push(contour[i][0]);
		vertices.push(contour[i][1]);
	}
}

export { GeometryBuilderUtils };