クォータニオン四つを組みとして使った四次元座標の回転演算

四次元の座標をxyz,yzw,zwx,wxyの四つの三次元空間に分割してそれぞれの空間に対して位置ベクトルとクォータニオンを割り当てれば
ユークリッド四次元の回転演算ができる話

Three.jsは[ここから](https://threejs.org/)入手

```html
<!DOCTYPE html>
<html lang="ja">
	<head>
		<title>THREE.JS</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<script type="module">
			import * as THREE from './three.module.js'
			// 初期位置
			let positions = {
				x:1,
				y:0,
				z:0,
				w:0,
			}
			// 回転面 [0,0,0,0]を中心としたxy,yz,zw,wx,zx,wyそれぞれの面での回転
			let rotations = {
				xy:90,
				yz:90,
				zw:90,
				wx:90,
				zx:0,
				wy:0,
			}
			// 回転演算用のベクトル
			let locations = {
				xyz:(new THREE.Vector3(positions.x,positions.y,positions.z)),
				yzw:(new THREE.Vector3(positions.y,positions.z,positions.w)),
				zwx:(new THREE.Vector3(positions.z,positions.w,positions.x)),
				wxy:(new THREE.Vector3(positions.w,positions.x,positions.y)),
			}
			// 回転演算用のクオータニオン
			let quaternions = {
				xyz:(new THREE.Quaternion()),
				yzw:(new THREE.Quaternion()),
				zwx:(new THREE.Quaternion()),
				wxy:(new THREE.Quaternion()),
			}
			
			output('初期位置',locations)
			// xy xyz空間上でx→yの演算をした後yzw,zwx,wxy空間にそれぞれx,y座標を再配置
			quaternions.xyz.setFromAxisAngle(new THREE.Vector3(0,0,1),(Math.PI/180)*rotations.xy)
			locations.xyz.applyQuaternion(quaternions.xyz)
			// Vector3の要素はxyzなので第1要素x,第2要素y,第3要素zとして取り出して配置
			locations.yzw.x=locations.xyz.y // y
			locations.zwx.z=locations.xyz.x // x
			locations.wxy.y=locations.xyz.x // x
			locations.wxy.z=locations.xyz.y // y
			output('xy回転後',locations)
			// yz 以下同様
			quaternions.xyz.setFromAxisAngle(new THREE.Vector3(1,0,0),(Math.PI/180)*rotations.yz)
			locations.xyz.applyQuaternion(quaternions.xyz)
			locations.yzw.x=locations.xyz.y // y
			locations.yzw.y=locations.xyz.z // z
			locations.zwx.x=locations.xyz.z // z
			locations.wxy.z=locations.xyz.y // y
			output('yz回転後',locations)
			// zw
			quaternions.yzw.setFromAxisAngle(new THREE.Vector3(1,0,0),(Math.PI/180)*rotations.zw)
			locations.yzw.applyQuaternion(quaternions.yzw)
			locations.xyz.z=locations.yzw.y // z
			locations.zwx.x=locations.yzw.y // z
			locations.zwx.y=locations.yzw.z // w
			locations.wxy.x=locations.yzw.z // w
			output('zw回転後',locations)
			// wx
			quaternions.zwx.setFromAxisAngle(new THREE.Vector3(1,0,0),(Math.PI/180)*rotations.wx)
			locations.zwx.applyQuaternion(quaternions.zwx)
			locations.xyz.x=locations.zwx.z // x
			locations.yzw.z=locations.zwx.y // w
			locations.wxy.x=locations.zwx.y // w
			locations.wxy.y=locations.zwx.z // x
			output('wx回転後',locations)
			// zx
			quaternions.xyz.setFromAxisAngle(new THREE.Vector3(0,1,0),(Math.PI/180)*rotations.zx)
			locations.xyz.applyQuaternion(quaternions.xyz)
			locations.yzw.y=locations.xyz.z // z
			locations.zwx.z=locations.xyz.x // x
			locations.zwx.x=locations.xyz.z // z
			locations.wxy.y=locations.xyz.x // x
			output('zx回転後',locations)
			// wy
			quaternions.yzw.setFromAxisAngle(new THREE.Vector3(0,1,0),(Math.PI/180)*rotations.wy)
			locations.yzw.applyQuaternion(quaternions.yzw)
			locations.xyz.y=locations.yzw.x // y
			locations.zwx.y=locations.yzw.z // w
			locations.wxy.x=locations.yzw.z // w
			locations.wxy.z=locations.yzw.x // y
			output('wy回転後',locations)

			function output(caption,locations)
			{
				console.log(caption)
				let result = {
					x:locations.xyz.x,
					y:locations.yzw.x,
					z:locations.zwx.x,
					w:locations.wxy.x,
				}
				console.log(result)
			}
		</script>
	</body>
</html>
```

![img_20190712.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314532/310b7001-f0b6-0599-6405-a05a7154386f.png)

回転のイメージ
![rotate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314532/12a914ea-acc7-e6ac-80f6-d6cfe6103d20.png)