Right-hand z-forward coordinate system
This commit is contained in:
parent
5c347516ba
commit
2688769103
3 changed files with 338 additions and 55 deletions
38
README.md
38
README.md
|
@ -89,6 +89,44 @@ vmathObjArrayBased ................ 73.968 ms 74.292 ms ±0.631 x100
|
|||
* [3d Ray Trace Benchmark](tests/bench_raytracer.nim)
|
||||
* [2d SVG Render Benchmark](https://github.com/treeform/pixie/blob/master/tests/bench_svg.nim)
|
||||
|
||||
## Zmod - GLSL mod
|
||||
|
||||
GLSL uses a different type of float point mod. Because mod is a Nim keyword please use `zmod` when you need GLSL `mod` behavior.
|
||||
|
||||
## Coordinate System
|
||||
|
||||
Right-hand z-forward coordinate system
|
||||
|
||||
This is the same system used in the GLTF file format.
|
||||
|
||||
> glTF uses a right-handed coordinate system.
|
||||
> glTF defines +Y as up, +Z as forward, and -X as right;
|
||||
> the front of a glTF asset faces +Z.
|
||||
|
||||
[glTF Spec 2.0](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#coordinate-system-and-units)
|
||||
|
||||
## OpenGL matrix column-major notation.
|
||||
|
||||
> [9.005](https://www.opengl.org/archives/resources/faq/technical/transformations.htm) For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out contiguously in memory. The translation components occupy the 13th, 14th, and 15th elements of the 16-element matrix, where indices are numbered from 1 to 16 as described in section 2.11.2 of the [OpenGL 2.1 Specification](https://registry.khronos.org/OpenGL/specs/gl/glspec21.pdf).
|
||||
>
|
||||
> Sadly, the use of column-major format in the spec and blue book has resulted in endless confusion in the OpenGL programming community. Column-major notation suggests that matrices are not laid out in memory as a programmer would expect.
|
||||
|
||||
OpenGL/GLSL/vmath vs Math/Specification notation:
|
||||
```
|
||||
mat4([
|
||||
a, b, c, 0, | a d g x |
|
||||
d, e, f, 0, | b e h y |
|
||||
g, h, i, 0, | c f i z |
|
||||
x, y, z, 1 | 0 0 0 1 |
|
||||
])
|
||||
```
|
||||
|
||||
# 1.x.x to 2.0.0 vmath breaking changes:
|
||||
* New right-hand-z-forward cordate system and functions that care about
|
||||
coordinate system where moved there.
|
||||
* deprecated `lookAt()` please use `toAngles()`/`fromAngles()` instead.
|
||||
* deprecated `fractional()` use `frac()` instead.
|
||||
|
||||
# 0.x.x to 1.0.0 vmath breaking changes:
|
||||
|
||||
* `vec3(v)` no longer works please use `vec3(v.x, v.y, 0)` instead.
|
||||
|
|
199
src/vmath.nim
199
src/vmath.nim
|
@ -30,6 +30,8 @@ float32 float Vec2 Vec3 Vec4 Mat3 Mat4 Quat
|
|||
float64 double DVec2 DVec3 DVec4 DMat3 DMat4 DQuat
|
||||
======= ====== ===== ===== ===== ===== ===== =====
|
||||
|
||||
|
||||
|
||||
]##
|
||||
|
||||
import macros, math, strutils
|
||||
|
@ -406,13 +408,17 @@ proc quantize*[T: SomeFloat](v, n: T): T =
|
|||
## Makes v be multiple of n. Rounding to integer quantize by 1.0.
|
||||
trunc(v / n) * n
|
||||
|
||||
proc fractional*[T: SomeFloat](v: T): T =
|
||||
proc frac*[T: SomeFloat](v: T): T =
|
||||
## Returns fractional part of a number.
|
||||
## 3.14 -> 0.14
|
||||
## -3.14 -> 0.14
|
||||
result = abs(v)
|
||||
result = result - trunc(result)
|
||||
|
||||
proc fractional*[T: SomeFloat](v: T): T {.deprecated: "Use frac() insetad"} =
|
||||
## Returns fractional part of a number.
|
||||
frac(v)
|
||||
|
||||
proc inversesqrt*[T: float32|float64](v: T): T =
|
||||
## Returns inverse square root.
|
||||
1/sqrt(v)
|
||||
|
@ -425,11 +431,11 @@ proc mix*[T: SomeFloat](a, b, v: T): T =
|
|||
v * (b - a) + a
|
||||
|
||||
proc fixAngle*[T: SomeFloat](angle: T): T =
|
||||
## Make angle be from -PI to PI radians.
|
||||
## Normalize the angle be from -PI to PI radians.
|
||||
result = angle
|
||||
while result > PI:
|
||||
result -= PI * 2
|
||||
while result < -PI:
|
||||
while result <= -PI:
|
||||
result += PI * 2
|
||||
|
||||
proc angleBetween*[T: SomeFloat](a, b: T): T =
|
||||
|
@ -725,6 +731,11 @@ genMathFn(sqrt)
|
|||
genMathFn(floor)
|
||||
genMathFn(ceil)
|
||||
genMathFn(abs)
|
||||
genMathFn(trunc)
|
||||
genMathFn(frac)
|
||||
genMathFn(quantize)
|
||||
genMathFn(toRadians)
|
||||
genMathFn(toDegrees)
|
||||
|
||||
template genBoolFn(fn, op: untyped) =
|
||||
proc fn*[T](a, b: GVec2[T]): BVec2 =
|
||||
|
@ -970,10 +981,42 @@ proc `pos=`*[T](a: var GMat3[T], pos: GVec2[T]) =
|
|||
a[2, 0] = pos.x
|
||||
a[2, 1] = pos.y
|
||||
|
||||
proc forward*[T](a: GMat4[T]): GVec3[T] {.inline.} =
|
||||
## Vector facing +Z.
|
||||
result.x = a[2, 0]
|
||||
result.y = a[2, 1]
|
||||
result.z = a[2, 2]
|
||||
|
||||
proc back*[T](a: GMat4[T]): GVec3[T] {.inline.} =
|
||||
## Vector facing -Z.
|
||||
-a.forward()
|
||||
|
||||
proc left*[T](a: GMat4[T]): GVec3[T] {.inline.} =
|
||||
## Vector facing +X.
|
||||
result.x = a[0, 0]
|
||||
result.y = a[0, 1]
|
||||
result.z = a[0, 2]
|
||||
|
||||
proc right*[T](a: GMat4[T]): GVec3[T] {.inline.} =
|
||||
## Vector facing -X.
|
||||
-a.left()
|
||||
|
||||
proc up*[T](a: GMat4[T]): GVec3[T] {.inline.} =
|
||||
## Vector facing +Y.
|
||||
result.x = a[1, 0]
|
||||
result.y = a[1, 1]
|
||||
result.z = a[1, 2]
|
||||
|
||||
proc down*[T](a: GMat4[T]): GVec3[T] {.inline.} =
|
||||
## Vector facing -X.
|
||||
-a.up()
|
||||
|
||||
proc pos*[T](a: GMat4[T]): GVec3[T] =
|
||||
## Position of the matrix.
|
||||
gvec3[T](a[3].x, a[3].y, a[3].z)
|
||||
|
||||
proc `pos=`*[T](a: var GMat4[T], pos: GVec3[T]) =
|
||||
## See the position of the matrix.
|
||||
a[3, 0] = pos.x
|
||||
a[3, 1] = pos.y
|
||||
a[3, 2] = pos.z
|
||||
|
@ -1244,7 +1287,7 @@ proc translate*[T](v: GVec3[T]): GMat4[T] =
|
|||
)
|
||||
|
||||
proc rotate*[T](angle: T): GMat3[T] =
|
||||
## Create a rotation matrix by an angle.
|
||||
## Create a 2d rotation matrix by an angle.
|
||||
let
|
||||
sin = sin(angle)
|
||||
cos = cos(angle)
|
||||
|
@ -1254,22 +1297,119 @@ proc rotate*[T](angle: T): GMat3[T] =
|
|||
0, 0, 1
|
||||
)
|
||||
|
||||
proc hrp*[T](m: GMat4[T]): GVec3[T] =
|
||||
## Return heading, rotation and pivot of a matrix.
|
||||
var heading, pitch, roll: float32
|
||||
if m[1] > 0.998: # singularity at north pole
|
||||
heading = arctan2(m[2], m[10])
|
||||
pitch = PI / 2
|
||||
roll = 0
|
||||
elif m[1] < -0.998: # singularity at south pole
|
||||
heading = arctan2(m[2], m[10])
|
||||
pitch = -PI / 2
|
||||
roll = 0
|
||||
proc rotationOnly*[T](a: GMat4[T]): GMat4[T] {.inline.} =
|
||||
## Clears the positional component and returns rotation only.
|
||||
## Assumes matrix has not been scaled.
|
||||
result = a
|
||||
result.pos = gvec3(0, 0, 0)
|
||||
|
||||
proc rotateX*[T](angle: T): GMat4[T] =
|
||||
## Return a rotation matrix around X with angle.
|
||||
result[0, 0] = 1
|
||||
result[0, 1] = 0
|
||||
result[0, 2] = 0
|
||||
result[0, 3] = 0
|
||||
|
||||
result[1, 0] = 0
|
||||
result[1, 1] = cos(angle)
|
||||
result[1, 2] = -sin(angle)
|
||||
result[1, 3] = 0
|
||||
|
||||
result[2, 0] = 0
|
||||
result[2, 1] = sin(angle)
|
||||
result[2, 2] = cos(angle)
|
||||
result[2, 3] = 0
|
||||
|
||||
result[3, 0] = 0
|
||||
result[3, 1] = 0
|
||||
result[3, 2] = 0
|
||||
result[3, 3] = 1
|
||||
|
||||
proc rotateY*[T](angle: T): GMat4[T] =
|
||||
## Return a rotation matrix around Y with angle.
|
||||
result[0, 0] = cos(angle)
|
||||
result[0, 1] = 0
|
||||
result[0, 2] = sin(angle)
|
||||
result[0, 3] = 0
|
||||
|
||||
result[1, 0] = 0
|
||||
result[1, 1] = 1
|
||||
result[1, 2] = 0
|
||||
result[1, 3] = 0
|
||||
|
||||
result[2, 0] = -sin(angle)
|
||||
result[2, 1] = 0
|
||||
result[2, 2] = cos(angle)
|
||||
result[2, 3] = 0
|
||||
|
||||
result[3, 0] = 0
|
||||
result[3, 1] = 0
|
||||
result[3, 2] = 0
|
||||
result[3, 3] = 1
|
||||
|
||||
proc rotateZ*[T](angle: T): GMat4[T] =
|
||||
## Return a rotation matrix around Z with angle.
|
||||
result[0, 0] = cos(angle)
|
||||
result[0, 1] = -sin(angle)
|
||||
result[0, 2] = 0
|
||||
result[0, 3] = 0
|
||||
|
||||
result[1, 0] = sin(angle)
|
||||
result[1, 1] = cos(angle)
|
||||
result[1, 2] = 0
|
||||
result[1, 3] = 0
|
||||
|
||||
result[2, 0] = 0
|
||||
result[2, 1] = 0
|
||||
result[2, 2] = 1
|
||||
result[2, 3] = 0
|
||||
|
||||
result[3, 0] = 0
|
||||
result[3, 1] = 0
|
||||
result[3, 2] = 0
|
||||
result[3, 3] = 1
|
||||
|
||||
proc toAngles*[T](a: GVec3[T]): GVec3[T] =
|
||||
## Given a 3d vector, computes euler angles: pitch and yaw
|
||||
## pitch (x rotation)
|
||||
## yaw (y rotation)
|
||||
## roll (z rotation) - always 0 in vector case
|
||||
if a == vec3(0, 0, 0):
|
||||
return
|
||||
let
|
||||
yaw = -arctan2(a.x, a.z)
|
||||
pitch = -arctan2(sqrt(a.x*a.x + a.z*a.z), a.y) + T(PI/2)
|
||||
result.x = pitch.fixAngle
|
||||
result.y = yaw.fixAngle
|
||||
|
||||
proc toAngles*[T](origin, target: GVec3[T]): GVec3[T] =
|
||||
## Gives euler angles from origin to target
|
||||
## pitch (x rotation)
|
||||
## yaw (y rotation)
|
||||
## roll (z rotation) - always 0 in vector case
|
||||
toAngles(target - origin)
|
||||
|
||||
proc toAngles*[T](m: GMat4[T]): GVec3[T] =
|
||||
## Decomposes the matrix into euler angles:
|
||||
## pitch (x rotation)
|
||||
## yaw (y rotation)
|
||||
## roll (z rotation)
|
||||
## Assumes matrix has not been scaled.
|
||||
result.x = arcsin(m[2,1])
|
||||
if result.x > PI/2:
|
||||
# Degenerate case over north pole.
|
||||
result.y = arctan2(m[0, 2], m[0, 0])
|
||||
elif result.x < -PI/2:
|
||||
# Degenerate case over south pole.
|
||||
result.y = arctan2(m[0, 2], m[0, 0])
|
||||
else:
|
||||
heading = arctan2(-m[8], m[0])
|
||||
pitch = arctan2(-m[6], m[5])
|
||||
roll = arcsin(m[4])
|
||||
gvec3[T](heading, pitch, roll)
|
||||
# Normal case.
|
||||
result.y = -arctan2(m[2, 0], m[2, 2])
|
||||
result.z = -arctan2(m[0, 1], m[1, 1])
|
||||
|
||||
proc fromAngles*[T](a: GVec3[T]): GMat4[T] =
|
||||
## Takes a vector containing euler angles and returns a matrix.
|
||||
rotateY(a.y) * rotateX(a.x) * rotateZ(a.z)
|
||||
|
||||
proc frustum*[T](left, right, bottom, top, near, far: T): GMat4[T] =
|
||||
## Create a frustum matrix.
|
||||
|
@ -1332,7 +1472,10 @@ proc ortho*[T](left, right, bottom, top, near, far: T): GMat4[T] =
|
|||
result[3, 2] = T(-(far + near) / fn)
|
||||
result[3, 3] = 1
|
||||
|
||||
proc lookAt*[T](eye, center, up: GVec3[T]): GMat4[T] =
|
||||
proc lookAt*[T](eye, center, up: GVec3[T]): GMat4[T]
|
||||
{.deprecated: "Wrong coordinate system. " &
|
||||
"Use toAngles(eye, center).fromAngles() instead to get " &
|
||||
"right-handed-z-forward coordinate system".} =
|
||||
## Create a matrix that would convert eye pos to looking at center.
|
||||
let
|
||||
eyex = eye[0]
|
||||
|
@ -1413,7 +1556,10 @@ proc lookAt*[T](eye, center, up: GVec3[T]): GMat4[T] =
|
|||
result[3, 2] = -(z0 * eyex + z1 * eyey + z2 * eyez)
|
||||
result[3, 3] = 1
|
||||
|
||||
proc lookAt*[T](eye, center: GVec3[T]): GMat4[T] =
|
||||
proc lookAt*[T](eye, center: GVec3[T]): GMat4[T]
|
||||
{.deprecated: "Wrong coordinate system. " &
|
||||
"Use toAngles(eye, center).fromAngles() instead to get " &
|
||||
"right-handed-z-forward coordinate system".} =
|
||||
## Look center from eye with default UP vector.
|
||||
lookAt(eye, center, gvec3(T(0), 0, 1))
|
||||
|
||||
|
@ -1598,17 +1744,6 @@ proc rotate*[T](angle: T, axis: GVec3[T]): GMat4[T] =
|
|||
## Return a rotation matrix with axis and angle.
|
||||
fromAxisAngle(axis, angle).mat4()
|
||||
|
||||
proc rotateX*[T](angle: T): GMat4[T] =
|
||||
## Return a rotation matrix around X with angle.
|
||||
fromAxisAngle(gvec3[T](1, 0, 0), angle).mat4()
|
||||
|
||||
proc rotateY*[T](angle: T): GMat4[T] =
|
||||
## Return a rotation matrix around Y with angle.
|
||||
fromAxisAngle(gvec3[T](0, 1, 0), angle).mat4()
|
||||
|
||||
proc rotateZ*[T](angle: T): GMat4[T] =
|
||||
## Return a rotation matrix around Z with angle.
|
||||
fromAxisAngle(gvec3[T](0, 0, 1), angle).mat4()
|
||||
|
||||
when defined(release):
|
||||
{.pop.}
|
||||
|
|
156
tests/test.nim
156
tests/test.nim
|
@ -863,29 +863,7 @@ block:
|
|||
)
|
||||
|
||||
block:
|
||||
# test quat and matrix lookat
|
||||
doAssert lookAt(vec3(1, 2, 3), vec3(0, 0, 0)).quat ~=
|
||||
quat(
|
||||
0.07232953608036041,
|
||||
0.3063928484916687,
|
||||
0.9237624406814575,
|
||||
0.2180707305669785
|
||||
)
|
||||
doAssert lookAt(vec3(0, 0, 0), vec3(0, 0, 0)).quat ~= quat(0.0, 0.0, 0.0, 1.0)
|
||||
doAssert lookAt(vec3(1, 0, 0), vec3(0, 0, 0)).quat ~= quat(0.5, 0.5, 0.5, 0.5)
|
||||
doAssert lookAt(vec3(0, 1, 0), vec3(0, 0, 0)).quat ~=
|
||||
quat(
|
||||
0.0,
|
||||
0.7071067690849304,
|
||||
0.7071067690849304,
|
||||
0.0
|
||||
)
|
||||
doAssert lookAt(vec3(0, 0, 1), vec3(0, 0, 0)).quat ~= quat(0.0, 0.0, 0.0, 1.0)
|
||||
|
||||
let
|
||||
a = lookAt(vec3(1, 2, 3), vec3(0, 0, 0))
|
||||
b = lookAt(dvec3(1, 2, 3), dvec3(0, 0, 0))
|
||||
|
||||
# test quat and matrix
|
||||
doAssert ortho[float32](-1, 1, 1, -1, -1000, 1000) ~= mat4(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, -1.0, 0.0, 0.0,
|
||||
|
@ -1045,4 +1023,136 @@ block:
|
|||
b = a / 2
|
||||
when compiles(b = a div 2): doAssert false # type mismatch
|
||||
|
||||
proc closeAngles(a, b: Vec3): bool =
|
||||
const epsilon = 0.001
|
||||
return abs(angleBetween(a.x, b.x)) < epsilon and
|
||||
abs(angleBetween(a.y, b.y)) < epsilon and
|
||||
abs(angleBetween(a.z, b.z)) < epsilon
|
||||
|
||||
const PI = PI.float32
|
||||
|
||||
block:
|
||||
# test Euler angles from a vector
|
||||
doAssert vec3(0, 0, 0).toAngles.closeAngles vec3(0f, 0f, 0f)
|
||||
doAssert vec3(0, 0, 1).toAngles.closeAngles vec3(0f, 0f, 0f) # forward
|
||||
doAssert vec3(0, 0, -1).toAngles.closeAngles vec3(0f, PI, 0f) # back
|
||||
doAssert vec3(-1, 0, 0).toAngles.closeAngles vec3(0f, PI/2, 0f) # right
|
||||
doAssert vec3(1, 0, 0).toAngles.closeAngles vec3(0f, -PI/2, 0f) # left
|
||||
doAssert vec3(0, 1, 0).toAngles.closeAngles vec3(PI/2, 0f, 0f) # up
|
||||
doAssert vec3(0, -1, 0).toAngles.closeAngles vec3(-PI/2, 0f, 0f) # down
|
||||
|
||||
block:
|
||||
# test Euler angles from a matrix
|
||||
doAssert translate(vec3(0, 0, 0)).toAngles.closeAngles vec3(0f, 0f, 0f)
|
||||
doAssert rotateX(0f).toAngles.closeAngles vec3(0f, 0f, 0f) # forward
|
||||
doAssert rotateY(PI).toAngles.closeAngles vec3(0f, -PI, 0f) # back
|
||||
doAssert rotateY(PI/2).toAngles.closeAngles vec3(0f, PI/2, 0f) # back
|
||||
doAssert rotateY(-PI/2).toAngles.closeAngles vec3(0f, -PI/2, 0f) # back
|
||||
doAssert rotateX(PI/2).toAngles.closeAngles vec3(PI/2, 0f, 0f) # up
|
||||
doAssert rotateX(-PI/2).toAngles.closeAngles vec3(-PI/2, 0f, 0f) # down
|
||||
doAssert rotateZ(PI/2).toAngles.closeAngles vec3(0f, 0f, PI/2) # tilt right
|
||||
doAssert rotateZ(-PI/2).toAngles.closeAngles vec3(0f, 0f, -PI/2) # tilt left
|
||||
|
||||
doAssert Mat4().toAngles.closeAngles vec3(0, 0, 0)
|
||||
|
||||
doAssert rotateX(10.toRadians()).toAngles.closeAngles vec3(10.toRadians(), 0, 0)
|
||||
doAssert rotateY(10.toRadians()).toAngles.closeAngles vec3(0, 10.toRadians(), 0)
|
||||
doAssert rotateZ(10.toRadians()).toAngles.closeAngles vec3(0, 0, 10.toRadians())
|
||||
doAssert rotateX(89.toRadians()).toAngles.closeAngles vec3(89.toRadians(), 0, 0)
|
||||
doAssert rotateY(89.toRadians()).toAngles.closeAngles vec3(0, 89.toRadians(), 0)
|
||||
doAssert rotateZ(89.toRadians()).toAngles.closeAngles vec3(0, 0, 89.toRadians())
|
||||
doAssert rotateX(90.toRadians()).toAngles.closeAngles vec3(90.toRadians(), 0, 0)
|
||||
doAssert rotateY(90.toRadians()).toAngles.closeAngles vec3(0, 90.toRadians(), 0)
|
||||
doAssert rotateZ(90.toRadians()).toAngles.closeAngles vec3(0, 0, 90.toRadians())
|
||||
doAssert rotateX(90.toRadians()).toAngles.closeAngles vec3(90.toRadians(), 0, 0)
|
||||
doAssert rotateY(90.toRadians()).toAngles.closeAngles vec3(0, 90.toRadians(), 0)
|
||||
doAssert rotateZ(-90.toRadians()).toAngles.closeAngles vec3(0, 0, -90.toRadians())
|
||||
doAssert rotateY(180.toRadians()).toAngles.closeAngles vec3(0, -180.toRadians(), 0)
|
||||
doAssert rotateZ(180.toRadians()).toAngles.closeAngles vec3(0, 0, 180.toRadians())
|
||||
doAssert rotateY(-180.toRadians()).toAngles.closeAngles vec3(0, 180.toRadians(), 0)
|
||||
doAssert rotateZ(-180.toRadians()).toAngles.closeAngles vec3(0, 0, 180.toRadians())
|
||||
|
||||
block:
|
||||
# Euler angles fuzzing tests.
|
||||
|
||||
# Test fromAngles with and without roll have same forward
|
||||
for i in 0 .. 1000:
|
||||
let
|
||||
xr = rand(-89.9f .. 89.9f).toRadians
|
||||
yr = rand(-180 .. 180).toRadians
|
||||
zr = rand(-180 .. 180).toRadians
|
||||
a = vec3(xr, yr, zr)
|
||||
b = vec3(xr, yr, 0f)
|
||||
ma = fromAngles(a)
|
||||
mb = fromAngles(b)
|
||||
|
||||
doAssert ma.forward() ~= mb.forward()
|
||||
|
||||
# Test forward/back, right/left, up/down combos
|
||||
for i in 0 .. 1000:
|
||||
let
|
||||
xr = rand(-89.9f .. 89.9f).toRadians
|
||||
yr = rand(-180 .. 180).toRadians
|
||||
zr = rand(-180 .. 180).toRadians
|
||||
b = vec3(xr, yr, zr)
|
||||
m = fromAngles(b)
|
||||
|
||||
doAssert m.forward() ~= m * vec3(0, 0, 1)
|
||||
doAssert m.back() ~= m * vec3(0, 0, -1)
|
||||
|
||||
doAssert m.right() ~= m * vec3(-1, 0, 0)
|
||||
doAssert m.left() ~= m * vec3(1, 0, 0)
|
||||
|
||||
doAssert m.up() ~= m * vec3(0, 1, 0)
|
||||
doAssert m.down() ~= m * vec3(0, -1, 0)
|
||||
|
||||
# Test non-polar and non-rotated cases
|
||||
for i in 0 .. 1000:
|
||||
let
|
||||
xr = rand(-89.9f .. 89.9f).toRadians
|
||||
yr = rand(-180 .. 180).toRadians
|
||||
zr = 0f
|
||||
b = vec3(xr, yr, zr)
|
||||
m = fromAngles(b)
|
||||
a = m.toAngles()
|
||||
doAssert a.closeAngles(b)
|
||||
|
||||
# Test non-polar cases
|
||||
for i in 0 .. 1000:
|
||||
let
|
||||
xr = rand(-89.9f .. 89.9f).toRadians
|
||||
yr = rand(-180 .. 180).toRadians
|
||||
zr = rand(-180 .. 180).toRadians
|
||||
b = vec3(xr, yr, zr)
|
||||
m = fromAngles(b)
|
||||
a = m.toAngles()
|
||||
doAssert a.closeAngles(b)
|
||||
|
||||
# Test polar and non-rotated cases
|
||||
for i in 0 .. 1000:
|
||||
let
|
||||
xr = sample([-90, 90]).toRadians
|
||||
yr = rand(-180 .. 180).toRadians
|
||||
zr = 0f
|
||||
b = vec3(xr, yr, zr)
|
||||
m = fromAngles(b)
|
||||
a = m.toAngles()
|
||||
doAssert a.closeAngles(b)
|
||||
|
||||
# Test polar and crazy rotated cases
|
||||
for i in 0 .. 1000:
|
||||
let
|
||||
xr = sample([-90, 90]).toRadians
|
||||
yr = rand(-180 .. 180).toRadians
|
||||
zr = rand(-180 .. 180).toRadians
|
||||
b = vec3(xr, yr, zr)
|
||||
m = fromAngles(b)
|
||||
a = m.toAngles()
|
||||
|
||||
doAssert abs(angleBetween(a.x, b.x)) < 0.001
|
||||
if xr > 0:
|
||||
doAssert abs(angleBetween(a.y, b.y + b.z)) < 0.001
|
||||
else:
|
||||
doAssert abs(angleBetween(a.y, b.y - b.z)) < 0.001
|
||||
|
||||
echo "test finished successfully"
|
||||
|
|
Loading…
Reference in a new issue