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)
}
}