js/SceneKit/SCNVector4.js
- 'use strict'
-
- import SCNMatrix4 from './SCNMatrix4'
- import SCNVector3 from './SCNVector3'
-
- /**
- * A representation of a four-component vector.
- * @access public
- * @see https://developer.apple.com/documentation/scenekit/scnvector4
- */
- export default class SCNVector4 {
- // Initializers
-
- /**
- *
- * @access public
- * @constructor
- * @param {number} x -
- * @param {number} y -
- * @param {number} z -
- * @param {number} w -
- * @see https://developer.apple.com/documentation/scenekit/scnvector4/1523931-init
- */
- constructor(x = 0, y = 0, z = 0, w = 0) {
- // Instance Properties
- /** @type {number} */
- this.x = x
- /** @type {number} */
- this.y = y
- /** @type {number} */
- this.z = z
- /** @type {number} */
- this.w = w
-
- //if(x instanceof Ammo.btVector4){
- // this.x = x.x()
- // this.y = x.y()
- // this.z = x.z()
- // this.w = x.w()
- //}
- }
-
- /**
- * @access private
- * @param {Buffer} data -
- * @param {number} [offset = 0] -
- * @param {boolean} [bigEndian = false] -
- * @returns {SCNVector4} -
- */
- static _initWithData(data, offset = 0, bigEndian = false) {
- const instance = new SCNVector4()
- if(bigEndian){
- instance.x = data.readFloatBE(offset + 0)
- instance.y = data.readFloatBE(offset + 4)
- instance.z = data.readFloatBE(offset + 8)
- instance.w = data.readFloatBE(offset + 12)
- }else{
- instance.x = data.readFloatLE(offset + 0)
- instance.y = data.readFloatLE(offset + 4)
- instance.z = data.readFloatLE(offset + 8)
- instance.w = data.readFloatLE(offset + 12)
- }
- return instance
- }
-
- _copy() {
- return new SCNVector4(this.x, this.y, this.z, this.w)
- }
-
- _copyFrom(v) {
- this.x = v.x
- this.y = v.y
- this.z = v.z
- this.w = v.z
- }
-
- // extensions
-
- zero() {
- return new SCNVector4()
- }
-
- /**
- * @access public
- * @param {SCNVector4} v -
- * @returns {SCNVector4} -
- */
- add(v) {
- const r = new SCNVector4()
- r.x = this.x + v.x
- r.y = this.y + v.y
- r.z = this.z + v.z
- r.w = this.w + v.w
- return r
- }
-
- /**
- * @access public
- * @param {SCNVector4} v -
- * @returns {SCNVector4} -
- */
- sub(v) {
- const r = new SCNVector4()
- r.x = this.x - v.x
- r.y = this.y - v.y
- r.z = this.z - v.z
- r.w = this.w - v.w
- return r
- }
-
- /**
- * @access public
- * @param {number} n -
- * @returns {SCNVector4} -
- */
- mul(n) {
- const r = new SCNVector4()
- r.x = this.x * n
- r.y = this.y * n
- r.z = this.z * n
- r.w = this.w * n
- return r
- }
-
- /**
- * @access public
- * @param {SCNVector4} v -
- * @returns {SCNVector4}
- */
- mulv(v) {
- const r = new SCNVector4()
- r.x = this.x * v.x
- r.y = this.y * v.y
- r.z = this.z * v.z
- r.z = this.w * v.w
- return r
- }
-
- /**
- * @access public
- * @param {SCNVector4} v -
- * @returns {number} -
- */
- dot(v) {
- return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w
- }
-
- /**
- * @access public
- * @param {SCNVecor4} v -
- * @returns {SCNVector4} -
- */
- cross(v) {
- const r = new SCNVector4()
- r.x = this.w * v.x + this.x * v.w + this.y * v.z - this.z * v.y
- r.y = this.w * v.y - this.x * v.z + this.y * v.w + this.z * v.x
- r.z = this.w * v.z + this.x * v.y - this.y * v.x + this.z * v.w
- r.w = this.w * v.w - this.x * v.x - this.y * v.y - this.z * v.z
- return r
- }
-
- /**
- * @access public
- * @param {SCNVector4} v -
- * @param {number} rate -
- * @returns {SCNVector4} -
- */
- lerp(v, rate) {
- const r = new SCNVector4()
- r.x = this.x + rate * (v.x - this.x)
- r.y = this.y + rate * (v.y - this.y)
- r.z = this.z + rate * (v.z - this.z)
- r.w = this.w + rate * (v.w - this.w)
- return r
- }
-
- /**
- * @access public
- * @param {SCNVector4} v -
- * @param {number} rate -
- * @returns {SCNVector4} -
- */
- slerp(v, rate) {
- const r = new SCNVector4()
- const qr = this.dot(v)
-
- if(qr < 0){
- r.x = this.x - (this.x + v.x) * rate
- r.y = this.y - (this.y + v.y) * rate
- r.z = this.z - (this.z + v.z) * rate
- r.w = this.w - (this.w + v.w) * rate
- }else{
- r.x = this.x + (v.x - this.x) * rate
- r.y = this.y + (v.y - this.y) * rate
- r.z = this.z + (v.z - this.z) * rate
- r.w = this.w + (v.w - this.w) * rate
- }
- return r.normalize()
- }
-
- /**
- * @access public
- * @returns {SCNVector4} -
- */
- normalize() {
- const r = new SCNVector4()
- const sqr = 1.0 / this.length()
- r.x = this.x * sqr
- r.y = this.y * sqr
- r.z = this.z * sqr
- r.w = this.w * sqr
- return r
- }
-
- /**
- * @access public
- * @returns {number} -
- */
- length2() {
- return (this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
- }
-
- /**
- * @access public
- * @returns {number} -
- */
- length() {
- return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
- }
-
- transform(m) {
- const r = new SCNVector4()
- r.x = this.x * m.m11 + this.y * m.m21 + this.z * m.m31 + this.w * m.m41
- r.y = this.x * m.m12 + this.y * m.m22 + this.z * m.m32 + this.w * m.m42
- r.z = this.x * m.m13 + this.y * m.m23 + this.z * m.m33 + this.w * m.m43
- r.w = this.x * m.m14 + this.y * m.m24 + this.z * m.m34 + this.w * m.m44
- return r
- }
-
- /**
- * @access public
- * @returns {SCNVector4} -
- */
- ln() {
- const r = new SCNVector4()
- const v = this.normalize()
-
- const n = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
- if(n === 0){
- r.x = 0
- r.y = 0
- r.z = 0
- r.w = 0
- return r
- }
- const theta = Math.atan2(n, v.w) / n
-
- r.x = theta * v.x
- r.y = theta * v.y
- r.z = theta * v.z
- r.w = 0
- return r
- }
-
- /**
- * @access public
- * @returns {SCNVector4} -
- */
- exp() {
- const r = new SCNVector4()
- const n = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
-
- if(n > 0.0){
- const sinn = Math.sin(n)
- r.x = sinn * this.x / n
- r.y = sinn * this.y / n
- r.z = sinn * this.z / n
- r.w = Math.cos(n)
- }else{
- r.x = 0.0
- r.y = 0.0
- r.z = 0.0
- r.w = 1.0
- }
- return r
- }
-
- /**
- * @access public
- * @returns {SCNMatrix4} -
- */
- rotMatrix() {
- const r = new SCNMatrix4()
- const x2 = this.x * this.x * 2.0
- const y2 = this.y * this.y * 2.0
- const z2 = this.z * this.z * 2.0
- const xy = this.x * this.y * 2.0
- const yz = this.y * this.z * 2.0
- const zx = this.z * this.x * 2.0
- const xw = this.x * this.w * 2.0
- const yw = this.y * this.w * 2.0
- const zw = this.z * this.w * 2.0
-
- r.m11 = 1.0 - y2 - z2
- r.m12 = xy + zw
- r.m13 = zx - yw
- r.m14 = 0.0
- r.m21 = xy - zw
- r.m22 = 1.0 - z2 - x2
- r.m23 = yz + xw
- r.m24 = 0.0
- r.m31 = zx + yw
- r.m32 = yz - xw
- r.m33 = 1.0 - x2 - y2
- r.m34 = 0.0
- r.m41 = 0.0
- r.m42 = 0.0
- r.m43 = 0.0
- r.m44 = 1.0
- return r
- }
-
- /**
- * @access public
- * @returns {SCNVector4} -
- */
- rotationToQuat() {
- const quat = new SCNVector4()
- if(this.x === 0 && this.y === 0 && this.z === 0){
- quat.x = 0
- quat.y = 0
- quat.z = 0
- quat.w = 1.0
- }else{
- const r = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
- const cosW = Math.cos(this.w * 0.5)
- const sinW = Math.sin(this.w * 0.5) * r
- quat.x = this.x * sinW
- quat.y = this.y * sinW
- quat.z = this.z * sinW
- quat.w = cosW
- }
-
- return quat
- }
-
- /**
- * @access public
- * @returns {SCNVector4} -
- */
- quatToRotation() {
- const rot = new SCNVector4()
- if(this.x === 0 && this.y === 0 && this.z === 0){
- rot.x = 0
- rot.y = 0
- rot.z = 0
- if(Math.abs(this.w) > 1){
- // actually, if this.w < -1, rotation will be NaN...
- rot.w = 0
- }else{
- // I don't know why it needs to be double but I make it the same as SceneKit
- rot.w = Math.acos(this.w) * 2.0
- }
- }else{
- const quat = this.normalize()
- const r = 1.0 / Math.sqrt(quat.x * quat.x + quat.y * quat.y + quat.z * quat.z)
- rot.x = quat.x * r
- rot.y = quat.y * r
- rot.z = quat.z * r
-
- const w = Math.acos(quat.w)
- if(isNaN(w)){
- rot.w = 0
- }else{
- // I don't know why it needs to be double but I make it the same as SceneKit
- rot.w = w * 2.0
- }
- }
- return rot
- }
-
- /**
- * @access public
- * @returns {SCNVector3} -
- */
- rotationToEulerAngles() {
- const euler = new SCNVector3()
- const sinW = Math.sin(this.w)
- const cosW = Math.cos(this.w)
- const cosWR = 1.0 - cosW
- const len2 = this.x * this.x + this.y * this.y + this.z * this.z
- if(len2 === 0){
- return euler
- }
- const r = 1.0 / Math.sqrt(len2)
- const x = this.x * r
- const y = this.y * r
- const z = this.z * r
- const s = y * sinW - x * z * cosWR
-
- //console.log(`s: ${s}`)
- //const threshold = 0.998
- const threshold = 0.999999
- if(s > threshold){
- // TODO: check SceneKit implementation
- euler.x = 0
- euler.y = -Math.PI * 0.5
- euler.z = -2.0 * Math.atan2(z * Math.sin(this.w * 0.5), Math.cos(this.w * 0.5))
- }else if(s < -threshold){
- // TODO: check SceneKit implementation
- euler.x = 0
- euler.y = Math.PI * 0.5
- euler.z = 2.0 * Math.atan2(z * Math.sin(this.w * 0.5), Math.cos(this.w * 0.5))
- }else{
- const m23 = x * sinW + y * z * cosWR
- //const m33 = 1 - (y * y + x * x) * cosWR
- const m33 = cosW + z * z * cosWR
- const m12 = z * sinW + x * y * cosWR
- //const m11 = 1 - (z * z + y * y) * cosWR
- const m11 = cosW + x * x * cosWR
- euler.x = Math.atan2(m23, m33)
- euler.y = Math.asin(s) // How can I get euler.y > pi/2 ?
- euler.z = Math.atan2(m12, m11)
- }
-
- return euler
- }
-
- /**
- * @access public
- * @returns {SCNVector3} -
- */
- quatToEulerAngles() {
- return this.quatToRotation().rotationToEulerAngles()
- }
-
- get angle() {
- return this.quatToRotation().w
- }
-
- /**
- * @access public
- * @returns {number[]} -
- */
- floatArray() {
- return [this.x, this.y, this.z, this.w]
- }
-
- /**
- * @access public
- * @returns {Float32Array} -
- */
- float32Array() {
- return new Float32Array([this.x, this.y, this.z, this.w])
- }
-
- /**
- * @access private
- * @returns {Ammo.btVector4} -
- * @desc call Ammo.destroy(vec) after using it.
- */
- _createBtVector4() {
- //return new Ammo.btVector4(this.x, this.y, this.z, this.w)
- }
-
- /**
- * @access private
- * @returns {Ammo.btQuaternion} -
- * @desc call Ammo.destroy(quat) after using it.
- */
- _createBtQuaternion() {
- //return new Ammo.btQuaternion(this.x, this.y, this.z, this.w)
- }
- }