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.
114 lines (92 loc) • 2.76 kB
JavaScript
/** @module capsule */
import { checkArguments, getCellsTypedArray, TAU } from "./utils.js";
/**
* @typedef {object} CapsuleOptions
* @property {number} [height=0.5]
* @property {number} [radius=0.25]
* @property {number} [nx=16]
* @property {number} [ny=1]
* @property {number} [roundSegments=32]
* @property {number} [phi=TAU]
*/
/**
* @alias module:capsule
* @param {CapsuleOptions} [options={}]
* @returns {import("../types.js").SimplicialComplex}
*/
function capsule({
height = 0.5,
radius = 0.25,
nx = 16,
ny = 1,
roundSegments = 16,
phi = TAU,
} = {}) {
checkArguments(arguments);
const ringsBody = ny + 1;
const ringsCap = roundSegments * 2;
const ringsTotal = ringsCap + ringsBody;
const size = ringsTotal * nx;
const positions = new Float32Array(size * 3);
const normals = new Float32Array(size * 3);
const uvs = new Float32Array(size * 2);
const cells = new (getCellsTypedArray(size))((ringsTotal - 1) * (nx - 1) * 6);
let vertexIndex = 0;
let cellIndex = 0;
const segmentIncrement = 1 / (nx - 1);
const ringIncrement = 1 / (ringsCap - 1);
const bodyIncrement = 1 / (ringsBody - 1);
function computeRing(r, y, dy) {
for (let s = 0; s < nx; s++, vertexIndex++) {
const x = -Math.cos(s * segmentIncrement * phi) * r;
const z = Math.sin(s * segmentIncrement * phi) * r;
const py = radius * y + height * dy;
positions[vertexIndex * 3] = radius * x;
positions[vertexIndex * 3 + 1] = py;
positions[vertexIndex * 3 + 2] = radius * z;
normals[vertexIndex * 3] = x;
normals[vertexIndex * 3 + 1] = y;
normals[vertexIndex * 3 + 2] = z;
uvs[vertexIndex * 2] = s * segmentIncrement;
uvs[vertexIndex * 2 + 1] = 1 - (0.5 - py / (2 * radius + height));
}
}
for (let r = 0; r < roundSegments; r++) {
computeRing(
Math.sin(Math.PI * r * ringIncrement),
Math.sin(Math.PI * (r * ringIncrement - 0.5)),
-0.5,
);
}
for (let r = 0; r < ringsBody; r++) {
computeRing(1, 0, r * bodyIncrement - 0.5);
}
for (let r = roundSegments; r < ringsCap; r++) {
computeRing(
Math.sin(Math.PI * r * ringIncrement),
Math.sin(Math.PI * (r * ringIncrement - 0.5)),
0.5,
);
}
for (let r = 0; r < ringsTotal - 1; r++) {
for (let s = 0; s < nx - 1; s++) {
const a = r * nx;
const b = (r + 1) * nx;
const s1 = s + 1;
cells[cellIndex] = a + s;
cells[cellIndex + 1] = a + s1;
cells[cellIndex + 2] = b + s1;
cells[cellIndex + 3] = a + s;
cells[cellIndex + 4] = b + s1;
cells[cellIndex + 5] = b + s;
cellIndex += 6;
}
}
return {
positions,
normals,
uvs,
cells,
};
}
export default capsule;