UNPKG

primitive-geometry

Version:

Geometries for 3D rendering, including normals, UVs and cell indices (faces). Perfect if you want to supercharge your dependency folder... with 30KB of geometries.

188 lines (151 loc) 5.03 kB
/** @module cylinder */ import { checkArguments, getCellsTypedArray, normalize, TAU, TMP, } from "./utils.js"; /** * @typedef {object} CylinderOptions * @property {number} [height=1] * @property {number} [radius=0.25] * @property {number} [nx=16] * @property {number} [ny=1] * @property {number} [radiusApex=radius] * @property {number} [capSegments=1] * @property {boolean} [capApex=true] * @property {boolean} [capBase=true] * @property {number} [phi=TAU] */ /** * @alias module:cylinder * @param {CylinderOptions} [options={}] * @returns {import("../types.js").SimplicialComplex} */ function cylinder({ height = 1, radius = 0.25, nx = 16, ny = 1, radiusApex = radius, capSegments = 1, capApex = true, capBase = true, capBaseSegments = capSegments, phi = TAU, } = {}) { checkArguments(arguments); let capCount = 0; if (capApex) capCount += capSegments; if (capBase) capCount += capBaseSegments; const segments = nx + 1; const slices = ny + 1; const size = segments * slices + segments * 2 * capCount; const positions = new Float32Array(size * 3); const normals = new Float32Array(size * 3); const uvs = new Float32Array(size * 2); const cells = new (getCellsTypedArray(size))((nx * ny + nx * capCount) * 6); let vertexIndex = 0; let cellIndex = 0; const halfHeight = height / 2; const segmentIncrement = 1 / (segments - 1); const ringIncrement = 1 / (slices - 1); for (let i = 0; i < segments; i++) { const u = i * segmentIncrement; for (let j = 0; j < slices; j++) { const v = j * ringIncrement; const p = u * phi; const cosPhi = -Math.cos(p); const sinPhi = Math.sin(p); const r = radius * (1 - v) + radiusApex * v; positions[vertexIndex * 3] = r * cosPhi; positions[vertexIndex * 3 + 1] = height * v - halfHeight; positions[vertexIndex * 3 + 2] = r * sinPhi; TMP[0] = height * cosPhi; TMP[1] = radius - radiusApex; TMP[2] = height * sinPhi; normalize(TMP); normals[vertexIndex * 3] = TMP[0]; normals[vertexIndex * 3 + 1] = TMP[1]; normals[vertexIndex * 3 + 2] = TMP[2]; uvs[vertexIndex * 2] = u; uvs[vertexIndex * 2 + 1] = v; vertexIndex++; } } for (let j = 0; j < slices - 1; j++) { for (let i = 0; i < segments - 1; i++) { cells[cellIndex + 0] = (i + 0) * slices + (j + 0); cells[cellIndex + 1] = (i + 1) * slices + (j + 0); cells[cellIndex + 2] = (i + 1) * slices + (j + 1); cells[cellIndex + 3] = (i + 0) * slices + (j + 0); cells[cellIndex + 4] = (i + 1) * slices + (j + 1); cells[cellIndex + 5] = (i + 0) * slices + (j + 1); cellIndex += 6; } } function computeCap(flip, height, radius, capSegments) { const index = vertexIndex; const segmentIncrement = 1 / (segments - 1); for (let r = 0; r < capSegments; r++) { for (let i = 0; i < segments; i++) { const p = i * segmentIncrement * phi; const cosPhi = -Math.cos(p); const sinPhi = Math.sin(p); // inner point positions[vertexIndex * 3] = (radius * cosPhi * r) / capSegments; positions[vertexIndex * 3 + 1] = height; positions[vertexIndex * 3 + 2] = (radius * sinPhi * r) / capSegments; normals[vertexIndex * 3 + 1] = -flip; uvs[vertexIndex * 2] = (0.5 * cosPhi * r) / capSegments + 0.5; uvs[vertexIndex * 2 + 1] = (0.5 * sinPhi * r) / capSegments + 0.5; vertexIndex++; // outer point positions[vertexIndex * 3] = (radius * cosPhi * (r + 1)) / capSegments; positions[vertexIndex * 3 + 1] = height; positions[vertexIndex * 3 + 2] = (radius * sinPhi * (r + 1)) / capSegments; normals[vertexIndex * 3 + 1] = -flip; uvs[vertexIndex * 2] = (0.5 * (cosPhi * (r + 1))) / capSegments + 0.5; uvs[vertexIndex * 2 + 1] = (0.5 * (sinPhi * (r + 1))) / capSegments + 0.5; vertexIndex++; } } for (let r = 0; r < capSegments; r++) { for (let i = 0; i < segments - 1; i++) { const n = index + r * segments * 2 + i * 2; const a = n + 0; const b = n + 1; const c = n + 2; const d = n + 3; if (flip === 1) { cells[cellIndex] = a; cells[cellIndex + 1] = c; cells[cellIndex + 2] = d; cells[cellIndex + 3] = a; cells[cellIndex + 4] = d; cells[cellIndex + 5] = b; } else { cells[cellIndex + 0] = a; cells[cellIndex + 1] = d; cells[cellIndex + 2] = c; cells[cellIndex + 3] = a; cells[cellIndex + 4] = b; cells[cellIndex + 5] = d; } cellIndex += 6; } } } if (capBase) computeCap(1, -halfHeight, radius, capBaseSegments); if (capApex) computeCap(-1, halfHeight, radiusApex, capSegments); return { positions, normals, uvs, cells, }; } export default cylinder;