function addTubesOnBorderEdges(triangleMesh, vertices, indices, newVertices, newIndices, numSegments, radius) {
	// Intialize arc data with half circles, as the tubes are inside the mesh on one side.
	// Skip the first and last points, as these are already part of the mesh.
	initArcVertices(numSegments-2, radius, -Math.PI/2.0+Math.PI/(numSegments-1), Math.PI - 2*Math.PI/(numSegments-1));

    // Create mesh graph with unordered indices in insertion order
	let meshGraph = new MeshGraph(triangleMesh, false);

    let indexOffset = newVertices.length;
	let borderEdgeLoops = getBorderEdgeLoops(meshGraph);
	for (let i = 0; i < borderEdgeLoops.length; i++) {
		for (let j = 0; j < borderEdgeLoops[i].length; j++) {
			// Indices of the edge loop points.
			let iLast = borderEdgeLoops[i][(j-1+borderEdgeLoops[i].length) % borderEdgeLoops[i].length];
			let iCurrent = borderEdgeLoops[i][j];
			let iNext = borderEdgeLoops[i][(j+1) % borderEdgeLoops[i].length];

			// The edge loop points themselved.
			let pLast = vertices[iLast];
			let pCurrent = vertices[iCurrent];
			let pNext = vertices[iNext];

			// Compute the tangent direction, i.e. the direction of the tube/the direction of the line segment.
            let tangent = vec3add(vec3sub(pCurrent, pLast), vec3sub(pNext, pCurrent));
			let tangentLength = vec3length(tangent);
			if (tangentLength < 0.0001) {
				// In case the two vertices are almost identical, just skip this path line segment.
				continue;
			}
			tangent = vec3mul(1/tangentLength, tangent);

			// Compute the normal of the current edge segment after extrusion.
			let k0 = iLast;
			let k1 = iCurrent;
			let k2 = iNext;
			let k3 = iLast + triangleMesh.vertices.length;
			let k4 = iCurrent + triangleMesh.vertices.length;
			let k5 = iNext + triangleMesh.vertices.length;
			let normal0 = vec3cross(vec3sub(newVertices[k4], newVertices[k0]), vec3sub(newVertices[k4], newVertices[k1]));
            let normal1 = vec3cross(vec3sub(newVertices[k5], newVertices[k1]), vec3sub(newVertices[k5], newVertices[k2]));
            let normal = vec3normalize(vec3add(normal0, normal1));

			// Add the circle arc points to the vertex data
            let circleVertices = getTransformedCircleVertices(pCurrent, normal, tangent);
			for (let k = 0; k < circleVertices.length; k++) {
                newVertices.push(circleVertices[k]);
            }

			// Gather the indices of the circle points. This is necessary, as we skipped the first and last point of the
			// arc when calling initArcVertices. Thus, we need to use different indices fot these points (i.e., k1,k4/k2,k5).
			let indicesCircle0 = [];
			let indicesCircle1 = [];
			indicesCircle0.push(k1);
			indicesCircle1.push(k2);
			for (let k = 0; k < circleVertices.length; k++) {
                indicesCircle0.push(indexOffset+k);
                if (j < borderEdgeLoops[i].length-1) {
                    indicesCircle1.push(indexOffset+circleVertices.length+k);
                } else {
                    indicesCircle1.push(indexOffset-circleVertices.length*(borderEdgeLoops[i].length-1)+k);
                }
			}
			indicesCircle0.push(k4);
			indicesCircle1.push(k5);
			indexOffset += circleVertices.length;

			// Now add indices for creating triangles approximating the tube along the mesh edges.
			for (let k = 0; k < indicesCircle0.length-1; k++) {
				// Build two CCW triangles (one quad) for each side
				// Triangle 1
				newIndices.push(indicesCircle1[k+1]);
				newIndices.push(indicesCircle0[k+1]);
				newIndices.push(indicesCircle0[k]);

				// Triangle 2
				newIndices.push(indicesCircle1[k]);
				newIndices.push(indicesCircle1[k+1]);
				newIndices.push(indicesCircle0[k]);
			}
		}
	}
}
/**
 * This function is used by @see createOpenSurfaceShellMeshWithNormalsAndRoundEdges in order to smooth border edges
 * by creating tubes along these edges. The tube data is appended to newVertices and newIndices.
 * @param {TriangleMesh} triangleMesh The triangle mesh from which the edges are used as paths to create tubes along.
 * @param {vec3[]} vertices The vertices of the mesh.
 * @param {number[]} indices The indices of the mesh.
 * @param {vec3[]} newVertices The vertices of the already extruded mesh (without closed border edges).
 * @param {number[]} newIndices The indices of the already extruded mesh (without closed border edges).
 * @param {number} numSegments The number of segments to use for the triangular approximation of the tubes.
 * @param {number} radius The radius of the tubes.
 */
This function is used by @see createOpenSurfaceShellMeshWithNormalsAndRoundEdges in order to smooth border edges
by creating tubes along these edges. The tube data is appended to newVertices and newIndices.
@param {TriangleMesh} triangleMesh The triangle mesh from which the edges are used as paths to create tubes along.
@param {vec3[]} vertices The vertices of the mesh.
@param {number[]} indices The indices of the mesh.
@param {vec3[]} newVertices The vertices of the already extruded mesh (without closed border edges).
@param {number[]} newIndices The indices of the already extruded mesh (without closed border edges).
@param {number} numSegments The number of segments to use for the triangular approximation of the tubes.
@param {number} radius The radius of the tubes.
This function is used by @see createOpenSurfaceShellMeshWithNormalsAndRoundEdges in order to smooth border edges by creating tubes along these edges . The tube data is appended to newVertices and newIndices . @param { TriangleMesh } triangleMesh The triangle mesh from which the edges are used as paths to create tubes along . @param { vec3 [] } vertices The vertices of the mesh . @param { number [] } indices The indices of the mesh . @param { vec3 [] } newVertices The vertices of the already extruded mesh ( without closed border edges ) . @param { number [] } newIndices The indices of the already extruded mesh ( without closed border edges ) . @param { number } numSegments The number of segments to use for the triangular approximation of the tubes . @param { number } radius The radius of the tubes .
function addTubesOnBorderEdges(triangleMesh, vertices, indices, newVertices, newIndices, numSegments, radius) {
	initArcVertices(numSegments-2, radius, -Math.PI/2.0+Math.PI/(numSegments-1), Math.PI - 2*Math.PI/(numSegments-1));
	let meshGraph = new MeshGraph(triangleMesh, false);
    let indexOffset = newVertices.length;
	let borderEdgeLoops = getBorderEdgeLoops(meshGraph);
	for (let i = 0; i < borderEdgeLoops.length; i++) {
		for (let j = 0; j < borderEdgeLoops[i].length; j++) {
			let iLast = borderEdgeLoops[i][(j-1+borderEdgeLoops[i].length) % borderEdgeLoops[i].length];
			let iCurrent = borderEdgeLoops[i][j];
			let iNext = borderEdgeLoops[i][(j+1) % borderEdgeLoops[i].length];
			let pLast = vertices[iLast];
			let pCurrent = vertices[iCurrent];
			let pNext = vertices[iNext];
            let tangent = vec3add(vec3sub(pCurrent, pLast), vec3sub(pNext, pCurrent));
			let tangentLength = vec3length(tangent);
			if (tangentLength < 0.0001) {
				continue;
			}
			tangent = vec3mul(1/tangentLength, tangent);
			let k0 = iLast;
			let k1 = iCurrent;
			let k2 = iNext;
			let k3 = iLast + triangleMesh.vertices.length;
			let k4 = iCurrent + triangleMesh.vertices.length;
			let k5 = iNext + triangleMesh.vertices.length;
			let normal0 = vec3cross(vec3sub(newVertices[k4], newVertices[k0]), vec3sub(newVertices[k4], newVertices[k1]));
            let normal1 = vec3cross(vec3sub(newVertices[k5], newVertices[k1]), vec3sub(newVertices[k5], newVertices[k2]));
            let normal = vec3normalize(vec3add(normal0, normal1));
            let circleVertices = getTransformedCircleVertices(pCurrent, normal, tangent);
			for (let k = 0; k < circleVertices.length; k++) {
                newVertices.push(circleVertices[k]);
            }
			let indicesCircle0 = [];
			let indicesCircle1 = [];
			indicesCircle0.push(k1);
			indicesCircle1.push(k2);
			for (let k = 0; k < circleVertices.length; k++) {
                indicesCircle0.push(indexOffset+k);
                if (j < borderEdgeLoops[i].length-1) {
                    indicesCircle1.push(indexOffset+circleVertices.length+k);
                } else {
                    indicesCircle1.push(indexOffset-circleVertices.length*(borderEdgeLoops[i].length-1)+k);
                }
			}
			indicesCircle0.push(k4);
			indicesCircle1.push(k5);
			indexOffset += circleVertices.length;
			for (let k = 0; k < indicesCircle0.length-1; k++) {
				newIndices.push(indicesCircle1[k+1]);
				newIndices.push(indicesCircle0[k+1]);
				newIndices.push(indicesCircle0[k]);
				newIndices.push(indicesCircle1[k]);
				newIndices.push(indicesCircle1[k+1]);
				newIndices.push(indicesCircle0[k]);
			}
		}
	}
}
function addTubesOnBorderEdges ( triangleMesh , vertices , indices , newVertices , newIndices , numSegments , radius ) { initArcVertices ( numSegments - 2 , radius , - Math . PI / 2.0 + Math . PI / ( numSegments - 1 ) , Math . PI - 2 * Math . PI / ( numSegments - 1 ) ) ; let meshGraph = new MeshGraph ( triangleMesh , false ) ; let indexOffset = newVertices . length ; let borderEdgeLoops = getBorderEdgeLoops ( meshGraph ) ; for ( let i = 0 ; i < borderEdgeLoops . length ; i ++ ) { for ( let j = 0 ; j < borderEdgeLoops [ i ] . length ; j ++ ) { let iLast = borderEdgeLoops [ i ] [ ( j - 1 + borderEdgeLoops [ i ] . length ) % borderEdgeLoops [ i ] . length ] ; let iCurrent = borderEdgeLoops [ i ] [ j ] ; let iNext = borderEdgeLoops [ i ] [ ( j + 1 ) % borderEdgeLoops [ i ] . length ] ; let pLast = vertices [ iLast ] ; let pCurrent = vertices [ iCurrent ] ; let pNext = vertices [ iNext ] ; let tangent = vec3add ( vec3sub ( pCurrent , pLast ) , vec3sub ( pNext , pCurrent ) ) ; let tangentLength = vec3length ( tangent ) ; if ( tangentLength < 0.0001 ) { continue ; } tangent = vec3mul ( 1 / tangentLength , tangent ) ; let k0 = iLast ; let k1 = iCurrent ; let k2 = iNext ; let k3 = iLast + triangleMesh . vertices . length ; let k4 = iCurrent + triangleMesh . vertices . length ; let k5 = iNext + triangleMesh . vertices . length ; let normal0 = vec3cross ( vec3sub ( newVertices [ k4 ] , newVertices [ k0 ] ) , vec3sub ( newVertices [ k4 ] , newVertices [ k1 ] ) ) ; let normal1 = vec3cross ( vec3sub ( newVertices [ k5 ] , newVertices [ k1 ] ) , vec3sub ( newVertices [ k5 ] , newVertices [ k2 ] ) ) ; let normal = vec3normalize ( vec3add ( normal0 , normal1 ) ) ; let circleVertices = getTransformedCircleVertices ( pCurrent , normal , tangent ) ; for ( let k = 0 ; k < circleVertices . length ; k ++ ) { newVertices . push ( circleVertices [ k ] ) ; } let indicesCircle0 = [ ] ; let indicesCircle1 = [ ] ; indicesCircle0 . push ( k1 ) ; indicesCircle1 . push ( k2 ) ; for ( let k = 0 ; k < circleVertices . length ; k ++ ) { indicesCircle0 . push ( indexOffset + k ) ; if ( j < borderEdgeLoops [ i ] . length - 1 ) { indicesCircle1 . push ( indexOffset + circleVertices . length + k ) ; } else { indicesCircle1 . push ( indexOffset - circleVertices . length * ( borderEdgeLoops [ i ] . length - 1 ) + k ) ; } } indicesCircle0 . push ( k4 ) ; indicesCircle1 . push ( k5 ) ; indexOffset += circleVertices . length ; for ( let k = 0 ; k < indicesCircle0 . length - 1 ; k ++ ) { newIndices . push ( indicesCircle1 [ k + 1 ] ) ; newIndices . push ( indicesCircle0 [ k + 1 ] ) ; newIndices . push ( indicesCircle0 [ k ] ) ; newIndices . push ( indicesCircle1 [ k ] ) ; newIndices . push ( indicesCircle1 [ k + 1 ] ) ; newIndices . push ( indicesCircle0 [ k ] ) ; } } } }
This function is used by @see createOpenSurfaceShellMeshWithNormalsAndRoundEdges in order to smooth border edges
 by creating tubes along these edges.
This function is used by @see createOpenSurfaceShellMeshWithNormalsAndRoundEdges in order to smooth border edges by creating tubes along these edges .
// Intialize arc data with half circles, as the tubes are inside the mesh on one side. // Skip the first and last points, as these are already part of the mesh. // Create mesh graph with unordered indices in insertion order // Indices of the edge loop points. // The edge loop points themselved. // Compute the tangent direction, i.e. the direction of the tube/the direction of the line segment. // In case the two vertices are almost identical, just skip this path line segment. // Compute the normal of the current edge segment after extrusion. // Add the circle arc points to the vertex data // Gather the indices of the circle points. This is necessary, as we skipped the first and last point of the // arc when calling initArcVertices. Thus, we need to use different indices fot these points (i.e., k1,k4/k2,k5). // Now add indices for creating triangles approximating the tube along the mesh edges. // Build two CCW triangles (one quad) for each side // Triangle 1 // Triangle 2