Home Reference Source Repository

js/SceneKit/SCNVector4.js

  1. 'use strict'
  2.  
  3. import SCNMatrix4 from './SCNMatrix4'
  4. import SCNVector3 from './SCNVector3'
  5.  
  6. /**
  7. * A representation of a four-component vector.
  8. * @access public
  9. * @see https://developer.apple.com/documentation/scenekit/scnvector4
  10. */
  11. export default class SCNVector4 {
  12. // Initializers
  13.  
  14. /**
  15. *
  16. * @access public
  17. * @constructor
  18. * @param {number} x -
  19. * @param {number} y -
  20. * @param {number} z -
  21. * @param {number} w -
  22. * @see https://developer.apple.com/documentation/scenekit/scnvector4/1523931-init
  23. */
  24. constructor(x = 0, y = 0, z = 0, w = 0) {
  25. // Instance Properties
  26. /** @type {number} */
  27. this.x = x
  28. /** @type {number} */
  29. this.y = y
  30. /** @type {number} */
  31. this.z = z
  32. /** @type {number} */
  33. this.w = w
  34.  
  35. //if(x instanceof Ammo.btVector4){
  36. // this.x = x.x()
  37. // this.y = x.y()
  38. // this.z = x.z()
  39. // this.w = x.w()
  40. //}
  41. }
  42.  
  43. /**
  44. * @access private
  45. * @param {Buffer} data -
  46. * @param {number} [offset = 0] -
  47. * @param {boolean} [bigEndian = false] -
  48. * @returns {SCNVector4} -
  49. */
  50. static _initWithData(data, offset = 0, bigEndian = false) {
  51. const instance = new SCNVector4()
  52. if(bigEndian){
  53. instance.x = data.readFloatBE(offset + 0)
  54. instance.y = data.readFloatBE(offset + 4)
  55. instance.z = data.readFloatBE(offset + 8)
  56. instance.w = data.readFloatBE(offset + 12)
  57. }else{
  58. instance.x = data.readFloatLE(offset + 0)
  59. instance.y = data.readFloatLE(offset + 4)
  60. instance.z = data.readFloatLE(offset + 8)
  61. instance.w = data.readFloatLE(offset + 12)
  62. }
  63. return instance
  64. }
  65.  
  66. _copy() {
  67. return new SCNVector4(this.x, this.y, this.z, this.w)
  68. }
  69.  
  70. _copyFrom(v) {
  71. this.x = v.x
  72. this.y = v.y
  73. this.z = v.z
  74. this.w = v.z
  75. }
  76.  
  77. // extensions
  78.  
  79. zero() {
  80. return new SCNVector4()
  81. }
  82.  
  83. /**
  84. * @access public
  85. * @param {SCNVector4} v -
  86. * @returns {SCNVector4} -
  87. */
  88. add(v) {
  89. const r = new SCNVector4()
  90. r.x = this.x + v.x
  91. r.y = this.y + v.y
  92. r.z = this.z + v.z
  93. r.w = this.w + v.w
  94. return r
  95. }
  96.  
  97. /**
  98. * @access public
  99. * @param {SCNVector4} v -
  100. * @returns {SCNVector4} -
  101. */
  102. sub(v) {
  103. const r = new SCNVector4()
  104. r.x = this.x - v.x
  105. r.y = this.y - v.y
  106. r.z = this.z - v.z
  107. r.w = this.w - v.w
  108. return r
  109. }
  110.  
  111. /**
  112. * @access public
  113. * @param {number} n -
  114. * @returns {SCNVector4} -
  115. */
  116. mul(n) {
  117. const r = new SCNVector4()
  118. r.x = this.x * n
  119. r.y = this.y * n
  120. r.z = this.z * n
  121. r.w = this.w * n
  122. return r
  123. }
  124.  
  125. /**
  126. * @access public
  127. * @param {SCNVector4} v -
  128. * @returns {SCNVector4}
  129. */
  130. mulv(v) {
  131. const r = new SCNVector4()
  132. r.x = this.x * v.x
  133. r.y = this.y * v.y
  134. r.z = this.z * v.z
  135. r.z = this.w * v.w
  136. return r
  137. }
  138.  
  139. /**
  140. * @access public
  141. * @param {SCNVector4} v -
  142. * @returns {number} -
  143. */
  144. dot(v) {
  145. return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w
  146. }
  147.  
  148. /**
  149. * @access public
  150. * @param {SCNVecor4} v -
  151. * @returns {SCNVector4} -
  152. */
  153. cross(v) {
  154. const r = new SCNVector4()
  155. r.x = this.w * v.x + this.x * v.w + this.y * v.z - this.z * v.y
  156. r.y = this.w * v.y - this.x * v.z + this.y * v.w + this.z * v.x
  157. r.z = this.w * v.z + this.x * v.y - this.y * v.x + this.z * v.w
  158. r.w = this.w * v.w - this.x * v.x - this.y * v.y - this.z * v.z
  159. return r
  160. }
  161.  
  162. /**
  163. * @access public
  164. * @param {SCNVector4} v -
  165. * @param {number} rate -
  166. * @returns {SCNVector4} -
  167. */
  168. lerp(v, rate) {
  169. const r = new SCNVector4()
  170. r.x = this.x + rate * (v.x - this.x)
  171. r.y = this.y + rate * (v.y - this.y)
  172. r.z = this.z + rate * (v.z - this.z)
  173. r.w = this.w + rate * (v.w - this.w)
  174. return r
  175. }
  176.  
  177. /**
  178. * @access public
  179. * @param {SCNVector4} v -
  180. * @param {number} rate -
  181. * @returns {SCNVector4} -
  182. */
  183. slerp(v, rate) {
  184. const r = new SCNVector4()
  185. const qr = this.dot(v)
  186.  
  187. if(qr < 0){
  188. r.x = this.x - (this.x + v.x) * rate
  189. r.y = this.y - (this.y + v.y) * rate
  190. r.z = this.z - (this.z + v.z) * rate
  191. r.w = this.w - (this.w + v.w) * rate
  192. }else{
  193. r.x = this.x + (v.x - this.x) * rate
  194. r.y = this.y + (v.y - this.y) * rate
  195. r.z = this.z + (v.z - this.z) * rate
  196. r.w = this.w + (v.w - this.w) * rate
  197. }
  198. return r.normalize()
  199. }
  200.  
  201. /**
  202. * @access public
  203. * @returns {SCNVector4} -
  204. */
  205. normalize() {
  206. const r = new SCNVector4()
  207. const sqr = 1.0 / this.length()
  208. r.x = this.x * sqr
  209. r.y = this.y * sqr
  210. r.z = this.z * sqr
  211. r.w = this.w * sqr
  212. return r
  213. }
  214.  
  215. /**
  216. * @access public
  217. * @returns {number} -
  218. */
  219. length2() {
  220. return (this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
  221. }
  222.  
  223. /**
  224. * @access public
  225. * @returns {number} -
  226. */
  227. length() {
  228. return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
  229. }
  230.  
  231. transform(m) {
  232. const r = new SCNVector4()
  233. r.x = this.x * m.m11 + this.y * m.m21 + this.z * m.m31 + this.w * m.m41
  234. r.y = this.x * m.m12 + this.y * m.m22 + this.z * m.m32 + this.w * m.m42
  235. r.z = this.x * m.m13 + this.y * m.m23 + this.z * m.m33 + this.w * m.m43
  236. r.w = this.x * m.m14 + this.y * m.m24 + this.z * m.m34 + this.w * m.m44
  237. return r
  238. }
  239.  
  240. /**
  241. * @access public
  242. * @returns {SCNVector4} -
  243. */
  244. ln() {
  245. const r = new SCNVector4()
  246. const v = this.normalize()
  247.  
  248. const n = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
  249. if(n === 0){
  250. r.x = 0
  251. r.y = 0
  252. r.z = 0
  253. r.w = 0
  254. return r
  255. }
  256. const theta = Math.atan2(n, v.w) / n
  257.  
  258. r.x = theta * v.x
  259. r.y = theta * v.y
  260. r.z = theta * v.z
  261. r.w = 0
  262. return r
  263. }
  264.  
  265. /**
  266. * @access public
  267. * @returns {SCNVector4} -
  268. */
  269. exp() {
  270. const r = new SCNVector4()
  271. const n = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
  272. if(n > 0.0){
  273. const sinn = Math.sin(n)
  274. r.x = sinn * this.x / n
  275. r.y = sinn * this.y / n
  276. r.z = sinn * this.z / n
  277. r.w = Math.cos(n)
  278. }else{
  279. r.x = 0.0
  280. r.y = 0.0
  281. r.z = 0.0
  282. r.w = 1.0
  283. }
  284. return r
  285. }
  286.  
  287. /**
  288. * @access public
  289. * @returns {SCNMatrix4} -
  290. */
  291. rotMatrix() {
  292. const r = new SCNMatrix4()
  293. const x2 = this.x * this.x * 2.0
  294. const y2 = this.y * this.y * 2.0
  295. const z2 = this.z * this.z * 2.0
  296. const xy = this.x * this.y * 2.0
  297. const yz = this.y * this.z * 2.0
  298. const zx = this.z * this.x * 2.0
  299. const xw = this.x * this.w * 2.0
  300. const yw = this.y * this.w * 2.0
  301. const zw = this.z * this.w * 2.0
  302.  
  303. r.m11 = 1.0 - y2 - z2
  304. r.m12 = xy + zw
  305. r.m13 = zx - yw
  306. r.m14 = 0.0
  307. r.m21 = xy - zw
  308. r.m22 = 1.0 - z2 - x2
  309. r.m23 = yz + xw
  310. r.m24 = 0.0
  311. r.m31 = zx + yw
  312. r.m32 = yz - xw
  313. r.m33 = 1.0 - x2 - y2
  314. r.m34 = 0.0
  315. r.m41 = 0.0
  316. r.m42 = 0.0
  317. r.m43 = 0.0
  318. r.m44 = 1.0
  319. return r
  320. }
  321.  
  322. /**
  323. * @access public
  324. * @returns {SCNVector4} -
  325. */
  326. rotationToQuat() {
  327. const quat = new SCNVector4()
  328. if(this.x === 0 && this.y === 0 && this.z === 0){
  329. quat.x = 0
  330. quat.y = 0
  331. quat.z = 0
  332. quat.w = 1.0
  333. }else{
  334. const r = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
  335. const cosW = Math.cos(this.w * 0.5)
  336. const sinW = Math.sin(this.w * 0.5) * r
  337. quat.x = this.x * sinW
  338. quat.y = this.y * sinW
  339. quat.z = this.z * sinW
  340. quat.w = cosW
  341. }
  342.  
  343. return quat
  344. }
  345.  
  346. /**
  347. * @access public
  348. * @returns {SCNVector4} -
  349. */
  350. quatToRotation() {
  351. const rot = new SCNVector4()
  352. if(this.x === 0 && this.y === 0 && this.z === 0){
  353. rot.x = 0
  354. rot.y = 0
  355. rot.z = 0
  356. if(Math.abs(this.w) > 1){
  357. // actually, if this.w < -1, rotation will be NaN...
  358. rot.w = 0
  359. }else{
  360. // I don't know why it needs to be double but I make it the same as SceneKit
  361. rot.w = Math.acos(this.w) * 2.0
  362. }
  363. }else{
  364. const quat = this.normalize()
  365. const r = 1.0 / Math.sqrt(quat.x * quat.x + quat.y * quat.y + quat.z * quat.z)
  366. rot.x = quat.x * r
  367. rot.y = quat.y * r
  368. rot.z = quat.z * r
  369.  
  370. const w = Math.acos(quat.w)
  371. if(isNaN(w)){
  372. rot.w = 0
  373. }else{
  374. // I don't know why it needs to be double but I make it the same as SceneKit
  375. rot.w = w * 2.0
  376. }
  377. }
  378. return rot
  379. }
  380.  
  381. /**
  382. * @access public
  383. * @returns {SCNVector3} -
  384. */
  385. rotationToEulerAngles() {
  386. const euler = new SCNVector3()
  387. const sinW = Math.sin(this.w)
  388. const cosW = Math.cos(this.w)
  389. const cosWR = 1.0 - cosW
  390. const len2 = this.x * this.x + this.y * this.y + this.z * this.z
  391. if(len2 === 0){
  392. return euler
  393. }
  394. const r = 1.0 / Math.sqrt(len2)
  395. const x = this.x * r
  396. const y = this.y * r
  397. const z = this.z * r
  398. const s = y * sinW - x * z * cosWR
  399.  
  400. //console.log(`s: ${s}`)
  401. //const threshold = 0.998
  402. const threshold = 0.999999
  403. if(s > threshold){
  404. // TODO: check SceneKit implementation
  405. euler.x = 0
  406. euler.y = -Math.PI * 0.5
  407. euler.z = -2.0 * Math.atan2(z * Math.sin(this.w * 0.5), Math.cos(this.w * 0.5))
  408. }else if(s < -threshold){
  409. // TODO: check SceneKit implementation
  410. euler.x = 0
  411. euler.y = Math.PI * 0.5
  412. euler.z = 2.0 * Math.atan2(z * Math.sin(this.w * 0.5), Math.cos(this.w * 0.5))
  413. }else{
  414. const m23 = x * sinW + y * z * cosWR
  415. //const m33 = 1 - (y * y + x * x) * cosWR
  416. const m33 = cosW + z * z * cosWR
  417. const m12 = z * sinW + x * y * cosWR
  418. //const m11 = 1 - (z * z + y * y) * cosWR
  419. const m11 = cosW + x * x * cosWR
  420. euler.x = Math.atan2(m23, m33)
  421. euler.y = Math.asin(s) // How can I get euler.y > pi/2 ?
  422. euler.z = Math.atan2(m12, m11)
  423. }
  424.  
  425. return euler
  426. }
  427.  
  428. /**
  429. * @access public
  430. * @returns {SCNVector3} -
  431. */
  432. quatToEulerAngles() {
  433. return this.quatToRotation().rotationToEulerAngles()
  434. }
  435.  
  436. get angle() {
  437. return this.quatToRotation().w
  438. }
  439.  
  440. /**
  441. * @access public
  442. * @returns {number[]} -
  443. */
  444. floatArray() {
  445. return [this.x, this.y, this.z, this.w]
  446. }
  447.  
  448. /**
  449. * @access public
  450. * @returns {Float32Array} -
  451. */
  452. float32Array() {
  453. return new Float32Array([this.x, this.y, this.z, this.w])
  454. }
  455.  
  456. /**
  457. * @access private
  458. * @returns {Ammo.btVector4} -
  459. * @desc call Ammo.destroy(vec) after using it.
  460. */
  461. _createBtVector4() {
  462. //return new Ammo.btVector4(this.x, this.y, this.z, this.w)
  463. }
  464.  
  465. /**
  466. * @access private
  467. * @returns {Ammo.btQuaternion} -
  468. * @desc call Ammo.destroy(quat) after using it.
  469. */
  470. _createBtQuaternion() {
  471. //return new Ammo.btQuaternion(this.x, this.y, this.z, this.w)
  472. }
  473. }