import {uid} from '@luma.gl/core'; import {Geometry} from '../geometry/geometry'; const INDEX_OFFSETS = { x: [2, 0, 1], y: [0, 1, 2], z: [1, 2, 0] }; export type TruncatedConeGeometryProps = { topRadius?: number; bottomRadius?: number; topCap?: boolean; bottomCap?: boolean; height?: number; nradial?: number; nvertical?: number; verticalAxis?: 'x' | 'y' | 'z'; }; /** * Primitives inspired by TDL http://code.google.com/p/webglsamples/, * copyright 2011 Google Inc. new BSD License * (http://www.opensource.org/licenses/bsd-license.php). */ export class TruncatedConeGeometry extends Geometry { constructor(props: TruncatedConeGeometryProps & {id?: string; attributes?: any} = {}) { const {id = uid('truncated-code-geometry')} = props; const {indices, attributes} = tesselateTruncatedCone(props); super({ ...props, id, topology: 'triangle-list', indices, attributes: { POSITION: {size: 3, value: attributes.POSITION}, NORMAL: {size: 3, value: attributes.NORMAL}, TEXCOORD_0: {size: 2, value: attributes.TEXCOORD_0}, ...props.attributes } }); } } /* eslint-disable max-statements, complexity */ function tesselateTruncatedCone(props: TruncatedConeGeometryProps = {}) { const { bottomRadius = 0, topRadius = 0, height = 1, nradial = 10, nvertical = 10, verticalAxis = 'y', topCap = false, bottomCap = false } = props; const extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0); const numVertices = (nradial + 1) * (nvertical + 1 + extra); const slant = Math.atan2(bottomRadius - topRadius, height); const msin = Math.sin; const mcos = Math.cos; const mpi = Math.PI; const cosSlant = mcos(slant); const sinSlant = msin(slant); const start = topCap ? -2 : 0; const end = nvertical + (bottomCap ? 2 : 0); const vertsAroundEdge = nradial + 1; const indices = new Uint16Array(nradial * (nvertical + extra) * 6); const indexOffset = INDEX_OFFSETS[verticalAxis]; const positions = new Float32Array(numVertices * 3); const normals = new Float32Array(numVertices * 3); const texCoords = new Float32Array(numVertices * 2); let i3 = 0; let i2 = 0; for (let i = start; i <= end; i++) { let v = i / nvertical; let y = height * v; let ringRadius; if (i < 0) { y = 0; v = 1; ringRadius = bottomRadius; } else if (i > nvertical) { y = height; v = 1; ringRadius = topRadius; } else { ringRadius = bottomRadius + (topRadius - bottomRadius) * (i / nvertical); } if (i === -2 || i === nvertical + 2) { ringRadius = 0; v = 0; } y -= height / 2; for (let j = 0; j < vertsAroundEdge; j++) { const sin = msin((j * mpi * 2) / nradial); const cos = mcos((j * mpi * 2) / nradial); positions[i3 + indexOffset[0]] = sin * ringRadius; positions[i3 + indexOffset[1]] = y; positions[i3 + indexOffset[2]] = cos * ringRadius; normals[i3 + indexOffset[0]] = i < 0 || i > nvertical ? 0 : sin * cosSlant; normals[i3 + indexOffset[1]] = i < 0 ? -1 : i > nvertical ? 1 : sinSlant; normals[i3 + indexOffset[2]] = i < 0 || i > nvertical ? 0 : cos * cosSlant; texCoords[i2 + 0] = j / nradial; texCoords[i2 + 1] = v; i2 += 2; i3 += 3; } } for (let i = 0; i < nvertical + extra; i++) { for (let j = 0; j < nradial; j++) { const index = (i * nradial + j) * 6; indices[index + 0] = vertsAroundEdge * (i + 0) + 0 + j; indices[index + 1] = vertsAroundEdge * (i + 0) + 1 + j; indices[index + 2] = vertsAroundEdge * (i + 1) + 1 + j; indices[index + 3] = vertsAroundEdge * (i + 0) + 0 + j; indices[index + 4] = vertsAroundEdge * (i + 1) + 1 + j; indices[index + 5] = vertsAroundEdge * (i + 1) + 0 + j; } } return { indices, attributes: { POSITION: positions, NORMAL: normals, TEXCOORD_0: texCoords } }; }