js/SceneKit/SCNCylinder.js
'use strict'
import SCNGeometry from './SCNGeometry'
import SCNGeometryElement from './SCNGeometryElement'
import SCNGeometryPrimitiveType from './SCNGeometryPrimitiveType'
import SCNGeometrySource from './SCNGeometrySource'
//import SCNGeometryTessellator from './SCNGeometryTessellator'
import SCNMaterial from './SCNMaterial'
import SCNVector3 from './SCNVector3'
/**
* A right circular cylinder geometry.
* @access public
* @extends {SCNGeometry}
* @see https://developer.apple.com/documentation/scenekit/scncylinder
*/
export default class SCNCylinder extends SCNGeometry {
static get _propTypes() {
return {
$constructor: (propNames, propValues) => {
const cylinder = new SCNCylinder(
propValues.cylinderradius,
propValues.cylinderheight
)
cylinder.radialSegmentCount = propValues.cylinderradialSegmentCount
cylinder.heightSegmentCount = propValues.cylinderheightSegmentCount
cylinder.materials = propValues.materials
cylinder.tessellator = propValues.tessellator
cylinder.subdivisionLevel = propValues.subdivisionLevel
return cylinder
},
name: ['string', null],
cylinderradius: ['float', null],
cylinderheight: ['float', null],
cylinderradialSegmentCount: ['integer', null],
cylinderheightSegmentCount: ['integer', null],
cylinderradialSpan: ['float', null],
cylinderprimitiveType: ['integer', null],
materials: ['NSArray', null],
tessellator: ['SCNGeometryTessellator', null],
subdivisionLevel: ['integer', null],
subdivisionSettings: ['bytes', null],
wantsAdaptiveSubdivision: ['boolean', null]
}
}
// Creating a Cylinder
/**
* Creates a cylinder geometry with the specified radius and height.
* @access public
* @constructor
* @param {number} radius - The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space.
* @param {number} height - The height of the cylinder along the y-axis of its local coordinate space.
* @desc The cylinder is centered in its local coordinate system. For example, if you create a cylinder whose radius is 5.0 and height is 10.0, its circular cross section extends from -5.0 to 5.0 along the x- and z-axes, and the y-coordinates of its base and top are -5.0 and 5.0, respectively.
* @see https://developer.apple.com/documentation/scenekit/scncylinder/1523685-init
*/
constructor(radius = 0.5, height = 1.0) {
super([], [])
// Adjusting a Cylinder’s Dimensions
/**
* The radius of the cylinder’s circular cross section. Animatable.
* @type {number}
* @see https://developer.apple.com/documentation/scenekit/scncylinder/1522674-radius
*/
this.radius = radius
/**
* The extent of the cylinder along its y-axis. Animatable.
* @type {number}
* @see https://developer.apple.com/documentation/scenekit/scncylinder/1523678-height
*/
this.height = height
// Adjusting Geometric Detail
/**
* The number of subdivisions around the circumference of the cylinder. Animatable.
* @type {number}
* @see https://developer.apple.com/documentation/scenekit/scncylinder/1524002-radialsegmentcount
*/
this.radialSegmentCount = 48
/**
* The number of subdivisions in the sides of the cylinder along its y-axis. Animatable.
* @type {number}
* @see https://developer.apple.com/documentation/scenekit/scncylinder/1523330-heightsegmentcount
*/
this.heightSegmentCount = 1
this._createGeometry()
this.materials.push(new SCNMaterial())
}
_createGeometry() {
const sourceData = []
const top = this.height * 0.5
const bottom = -this.height * 0.5
const sideData = []
const topData = []
const bottomData = []
const rStep = 2.0 * Math.PI / this.radialSegmentCount
const tStep = 1.0 / this.radialSegmentCount
for(let i=0; i<=this.radialSegmentCount; i++){
const x = -Math.sin(rStep * i)
const z = -Math.cos(rStep * i)
const vx = x * this.radius
const vz = z * this.radius
// vertex
sideData.push(vx, bottom, vz)
topData.push(vx, top, vz)
bottomData.push(-vx, bottom, vz)
// normal
sideData.push(x, 0, z)
topData.push(0, 1, 0)
bottomData.push(0, -1, 0)
// texcoord
const tx = tStep * i
sideData.push(tx, 1.0)
const ttx = (1 + Math.cos(i * rStep)) * 0.5
const tty = (1 + Math.sin(i * rStep)) * 0.5
topData.push(ttx, tty)
bottomData.push(ttx, tty)
// vertex
sideData.push(vx, top, vz)
topData.push(0, top, 0)
bottomData.push(0, bottom, 0)
// normal
sideData.push(x, 0, z)
topData.push(0, 1, 0)
bottomData.push(0, -1, 0)
// texcoord
sideData.push(tx, 0.0)
topData.push(0.5, 0.5)
bottomData.push(0.5, 0.5)
}
sourceData.push(...sideData, ...topData, ...bottomData)
const vectorCount = (this.radialSegmentCount + 1) * 6
const vertexSource = new SCNGeometrySource(
sourceData, // data
SCNGeometrySource.Semantic.vertex, // semantic
vectorCount, // vectorCount
true, // floatComponents
3, // componentsPerVector
4, // bytesPerComponent
0, // offset
32 // sride
)
const normalSource = new SCNGeometrySource(
sourceData, // data
SCNGeometrySource.Semantic.normal, // semantic
vectorCount, // vectorCount
true, // floatComponents
3, // componentsPerVector
4, // bytesPerComponent
12, // offset
32 // stride
)
const texcoordSource = new SCNGeometrySource(
sourceData, // data
SCNGeometrySource.Semantic.texcoord, // semantic
vectorCount, // vectorCount
true, // floatComponents
2, // componentsPerVector
4, // bytesPerComponent
24, // offset
32 // stride
)
const elements = []
const indexData0 = []
const indexData1 = []
const indexData2 = []
const offset1 = (this.radialSegmentCount + 1) * 2
const offset2 = (this.radialSegmentCount + 1) * 4
for(let i=0; i<this.radialSegmentCount; i++){
const base0 = i * 2
indexData0.push(base0, base0 + 3, base0 + 1)
indexData0.push(base0, base0 + 2, base0 + 3)
const base1 = offset1 + base0
indexData1.push(base1, base1 + 2, base1 + 3)
const base2 = offset2 + base0
indexData2.push(base2, base2 + 2, base2 + 3)
}
elements.push(new SCNGeometryElement(indexData0, SCNGeometryPrimitiveType.triangles))
elements.push(new SCNGeometryElement(indexData1, SCNGeometryPrimitiveType.triangles))
elements.push(new SCNGeometryElement(indexData2, SCNGeometryPrimitiveType.triangles))
this._geometryElements = elements
this._geometrySources = [vertexSource, normalSource, texcoordSource]
this.boundingBox = {
min: new SCNVector3(-this.radius, bottom, -this.radius),
max: new SCNVector3(this.radius, top, this.radius)
}
}
_updateBoundingBoxForSkinner(skinner = null){
if(skinner === null){
return this.boundingBox
}
return super._updateBoundingBoxForSkinner(skinner)
}
}