@openhps/core
Version:
Open Hybrid Positioning System - Core component
278 lines (215 loc) • 8.15 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CylinderGeometry = void 0;
var _BufferGeometry = require("../core/BufferGeometry.js");
var _BufferAttribute = require("../core/BufferAttribute.js");
var _Vector = require("../math/Vector3.js");
var _Vector2 = require("../math/Vector2.js");
/**
* A geometry class for representing a cylinder.
*
* ```js
* const geometry = new THREE.CylinderGeometry( 5, 5, 20, 32 );
* const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
* const cylinder = new THREE.Mesh( geometry, material );
* scene.add( cylinder );
* ```
*
* @augments BufferGeometry
*/
class CylinderGeometry extends _BufferGeometry.BufferGeometry {
/**
* Constructs a new cylinder geometry.
*
* @param {number} [radiusTop=1] - Radius of the cylinder at the top.
* @param {number} [radiusBottom=1] - Radius of the cylinder at the bottom.
* @param {number} [height=1] - Height of the cylinder.
* @param {number} [radialSegments=32] - Number of segmented faces around the circumference of the cylinder.
* @param {number} [heightSegments=1] - Number of rows of faces along the height of the cylinder.
* @param {boolean} [openEnded=false] - Whether the base of the cylinder is open or capped.
* @param {number} [thetaStart=0] - Start angle for first segment, in radians.
* @param {number} [thetaLength=Math.PI*2] - The central angle, often called theta, of the circular sector, in radians.
* The default value results in a complete cylinder.
*/
constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
super();
this.type = 'CylinderGeometry';
/**
* Holds the constructor parameters that have been
* used to generate the geometry. Any modification
* after instantiation does not change the geometry.
*
* @type {Object}
*/
this.parameters = {
radiusTop: radiusTop,
radiusBottom: radiusBottom,
height: height,
radialSegments: radialSegments,
heightSegments: heightSegments,
openEnded: openEnded,
thetaStart: thetaStart,
thetaLength: thetaLength
};
const scope = this;
radialSegments = Math.floor(radialSegments);
heightSegments = Math.floor(heightSegments);
// buffers
const indices = [];
const vertices = [];
const normals = [];
const uvs = [];
// helper variables
let index = 0;
const indexArray = [];
const halfHeight = height / 2;
let groupStart = 0;
// generate geometry
generateTorso();
if (openEnded === false) {
if (radiusTop > 0) generateCap(true);
if (radiusBottom > 0) generateCap(false);
}
// build geometry
this.setIndex(indices);
this.setAttribute('position', new _BufferAttribute.Float32BufferAttribute(vertices, 3));
this.setAttribute('normal', new _BufferAttribute.Float32BufferAttribute(normals, 3));
this.setAttribute('uv', new _BufferAttribute.Float32BufferAttribute(uvs, 2));
function generateTorso() {
const normal = new _Vector.Vector3();
const vertex = new _Vector.Vector3();
let groupCount = 0;
// this will be used to calculate the normal
const slope = (radiusBottom - radiusTop) / height;
// generate vertices, normals and uvs
for (let y = 0; y <= heightSegments; y++) {
const indexRow = [];
const v = y / heightSegments;
// calculate the radius of the current row
const radius = v * (radiusBottom - radiusTop) + radiusTop;
for (let x = 0; x <= radialSegments; x++) {
const u = x / radialSegments;
const theta = u * thetaLength + thetaStart;
const sinTheta = Math.sin(theta);
const cosTheta = Math.cos(theta);
// vertex
vertex.x = radius * sinTheta;
vertex.y = -v * height + halfHeight;
vertex.z = radius * cosTheta;
vertices.push(vertex.x, vertex.y, vertex.z);
// normal
normal.set(sinTheta, slope, cosTheta).normalize();
normals.push(normal.x, normal.y, normal.z);
// uv
uvs.push(u, 1 - v);
// save index of vertex in respective row
indexRow.push(index++);
}
// now save vertices of the row in our index array
indexArray.push(indexRow);
}
// generate indices
for (let x = 0; x < radialSegments; x++) {
for (let y = 0; y < heightSegments; y++) {
// we use the index array to access the correct indices
const a = indexArray[y][x];
const b = indexArray[y + 1][x];
const c = indexArray[y + 1][x + 1];
const d = indexArray[y][x + 1];
// faces
if (radiusTop > 0 || y !== 0) {
indices.push(a, b, d);
groupCount += 3;
}
if (radiusBottom > 0 || y !== heightSegments - 1) {
indices.push(b, c, d);
groupCount += 3;
}
}
}
// add a group to the geometry. this will ensure multi material support
scope.addGroup(groupStart, groupCount, 0);
// calculate new start value for groups
groupStart += groupCount;
}
function generateCap(top) {
// save the index of the first center vertex
const centerIndexStart = index;
const uv = new _Vector2.Vector2();
const vertex = new _Vector.Vector3();
let groupCount = 0;
const radius = top === true ? radiusTop : radiusBottom;
const sign = top === true ? 1 : -1;
// first we generate the center vertex data of the cap.
// because the geometry needs one set of uvs per face,
// we must generate a center vertex per face/segment
for (let x = 1; x <= radialSegments; x++) {
// vertex
vertices.push(0, halfHeight * sign, 0);
// normal
normals.push(0, sign, 0);
// uv
uvs.push(0.5, 0.5);
// increase index
index++;
}
// save the index of the last center vertex
const centerIndexEnd = index;
// now we generate the surrounding vertices, normals and uvs
for (let x = 0; x <= radialSegments; x++) {
const u = x / radialSegments;
const theta = u * thetaLength + thetaStart;
const cosTheta = Math.cos(theta);
const sinTheta = Math.sin(theta);
// vertex
vertex.x = radius * sinTheta;
vertex.y = halfHeight * sign;
vertex.z = radius * cosTheta;
vertices.push(vertex.x, vertex.y, vertex.z);
// normal
normals.push(0, sign, 0);
// uv
uv.x = cosTheta * 0.5 + 0.5;
uv.y = sinTheta * 0.5 * sign + 0.5;
uvs.push(uv.x, uv.y);
// increase index
index++;
}
// generate indices
for (let x = 0; x < radialSegments; x++) {
const c = centerIndexStart + x;
const i = centerIndexEnd + x;
if (top === true) {
// face top
indices.push(i, i + 1, c);
} else {
// face bottom
indices.push(i + 1, i, c);
}
groupCount += 3;
}
// add a group to the geometry. this will ensure multi material support
scope.addGroup(groupStart, groupCount, top === true ? 1 : 2);
// calculate new start value for groups
groupStart += groupCount;
}
}
copy(source) {
super.copy(source);
this.parameters = Object.assign({}, source.parameters);
return this;
}
/**
* Factory method for creating an instance of this class from the given
* JSON object.
*
* @param {Object} data - A JSON object representing the serialized geometry.
* @return {CylinderGeometry} A new instance.
*/
static fromJSON(data) {
return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength);
}
}
exports.CylinderGeometry = CylinderGeometry;