Home Reference Source Repository

js/SceneKit/SCNPlane.js

'use strict'

import SCNGeometry from './SCNGeometry'
import SCNGeometrySource from './SCNGeometrySource'
import SCNGeometryElement from './SCNGeometryElement'
import SCNGeometryPrimitiveType from './SCNGeometryPrimitiveType'
import SCNMaterial from './SCNMaterial'
import SCNVector3 from './SCNVector3'

/**
 * A rectangular, one-sided plane geometry of specified width and height.
 * @access public
 * @extends {SCNGeometry}
 * @see https://developer.apple.com/documentation/scenekit/scnplane
 */
export default class SCNPlane extends SCNGeometry {
  static get _propTypes() {
    return {
      width: 'float',
      height: 'float',
      widthSegmentCount: 'integer',
      heightSegmentCount: 'integer',
      cornerRadius: 'float',
      cornerSegmentCount: 'integer',
      materials: 'NSArray',
      tessellator: 'SCNGeometryTessellator',
      wantsAdaptiveSubdivision: 'boolean',

      name: 'string',
      primitiveType: ['integer', null],
      subdivisionLevel: 'integer',
      subdivisionSettings: ['bytes', null]
    }
  }

  // Creating a Plane

  /**
   * Creates a plane geometry with the specified width and height.
   * @access public
   * @constructor
   * @param {number} width - The width of the plane along the x-axis of its local coordinate space.
   * @param {number} height - The height of the plane along the y-axis of its local coordinate space.
   * @desc The plane is centered in its local coordinate system. For example, if you create a plane whose width and height are both 10.0, it extends from -5.0 to 5.0 along both the x- and y-axes, and the z-coordinate of all points in the plane is zero.
   * @see https://developer.apple.com/documentation/scenekit/scnplane/1523631-init
   */
  constructor(width = 1.0, height = 1.0) {
    super([], [])

    // Adjusting a Plane’s Dimensions

    /**
     * The extent of the plane along its horizontal axis. Animatable.
     * @type {number}
     * @see https://developer.apple.com/documentation/scenekit/scnplane/1523782-width
     */
    this.width = width

    /**
     * The extent of the plane along its vertical axis. Animatable.
     * @type {number}
     * @see https://developer.apple.com/documentation/scenekit/scnplane/1522837-height
     */
    this.height = height


    // Adjusting Geometric Detail

    /**
     * The number of subdivisions in the plane’s surface along its horizontal axis. Animatable.
     * @type {number}
     * @see https://developer.apple.com/documentation/scenekit/scnplane/1523991-widthsegmentcount
     */
    this.widthSegmentCount = 1

    /**
     * The number of subdivisions in the plane’s surface along its vertical axis. Animatable.
     * @type {number}
     * @see https://developer.apple.com/documentation/scenekit/scnplane/1522889-heightsegmentcount
     */
    this.heightSegmentCount = 1


    // Adding Rounded Corners

    /**
     * The radius of curvature for the plane’s corners. Animatable.
     * @type {number}
     * @see https://developer.apple.com/documentation/scenekit/scnplane/1523005-cornerradius
     */
    this.cornerRadius = 0

    /**
     * The number of line segments used to create each rounded corner of the plane. Animatable.
     * @type {number}
     * @see https://developer.apple.com/documentation/scenekit/scnplane/1524234-cornersegmentcount
     */
    this.cornerSegmentCount = 10

    this._createGeometry()
    this.materials.push(new SCNMaterial())
  }

  _createGeometry() {
    const sourceData = []
    const indexData = []

    // TODO: chamfer
    const wStep = 1.0 / this.widthSegmentCount
    for(let i=0; i<=this.heightSegmentCount; i++){
      const ty = i / this.heightSegmentCount
      const y = (-0.5 + ty) * this.height
      for(let j=0; j<=this.widthSegmentCount; j++){
        const tx = j / this.widthSegmentCount
        const x = (-0.5 + tx) * this.width

        sourceData.push(x, y, 0.0) // position
        sourceData.push(0.0, 0.0, 1.0) // normal
        sourceData.push(tx, 1.0 - ty) // texcoord
      }
    }

    const numSegments = this.widthSegmentCount * this.heightSegmentCount
    for(let i=0; i<numSegments; i++){
      const index = i * 4
      indexData.push(index, index + 1, index + 3)
      indexData.push(index, index + 3, index + 2)
    }

    const vectorCount = (this.widthSegmentCount + 1) * (this.heightSegmentCount + 1)

    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 element = new SCNGeometryElement(indexData, SCNGeometryPrimitiveType.triangles)

    this._geometryElements = [element]
    this._geometrySources = [vertexSource, normalSource, texcoordSource]
    this.boundingBox = {
      min: new SCNVector3(-0.5 * this.width, -0.5 * this.height, 0.0),
      max: new SCNVector3(0.5 * this.width, 0.5 * this.height, 0.0)
    }
  }
}