/Nim

# Module basic3d

Basic 3d support with vectors, points, matrices and some basic utilities. Vectors are implemented as direction vectors, ie. when transformed with a matrix the translation part of matrix is ignored. The coordinate system used is right handed, because its compatible with 2d coordinate system (rotation around zaxis equals 2d rotation). Operators + , - , * , / , += , -= , *= and /= are implemented for vectors and scalars.

Quick start example:

```# Create a matrix which first rotates, then scales and at last translates

var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)

# Create a 3d point at (100,150,200) and a vector (5,2,3)

var pt:Point3d=point3d(100.0,150.0,200.0)

var vec:Vector3d=vector3d(5.0,2.0,3.0)

pt &= m # transforms pt in place

var pt2:Point3d=pt & m #concatenates pt with m and returns a new point

var vec2:Vector3d=vec & m #concatenates vec with m and returns a new vector```

## Imports

math, strutils, times

## Types

```Matrix3d = object
ax*, ay*, az*, aw*, bx*, by*, bz*, bw*, cx*, cy*, cz*, cw*, tx*, ty*, tz*, tw*: float```
Implements a row major 3d matrix, which means transformations are applied the order they are concatenated. This matrix is stored as an 4x4 matrix: [ ax ay az aw ] [ bx by bz bw ] [ cx cy cz cw ] [ tx ty tz tw ]
```Point3d = object
x*, y*, z*: float```
Implements a non-homogeneous 3d point stored as an x , y and z coordinate.
```Vector3d = object
x*, y*, z*: float```
Implements a 3d direction vector stored as an x , y and z coordinate. Direction vector means, that when transforming a vector with a matrix, the translational part of the matrix is ignored.

## Lets

```IDMATRIX: Matrix3d = matrix3d(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0)```
`ORIGO: Point3d = point3d(0.0, 0.0, 0.0)`
`XAXIS: Vector3d = vector3d(1.0, 0.0, 0.0)`
`YAXIS: Vector3d = vector3d(0.0, 1.0, 0.0)`
`ZAXIS: Vector3d = vector3d(0.0, 0.0, 1.0)`

## Procs

```proc setElements(t: var Matrix3d; ax, ay, az, aw, bx, by, bz, bw, cx, cy, cz, cw, tx, ty, tz, tw: float) {.
inline, raises: [], tags: [].}```
Sets arbitrary elements in an exisitng matrix.
```proc matrix3d(ax, ay, az, aw, bx, by, bz, bw, cx, cy, cz, cw, tx, ty, tz, tw: float): Matrix3d {.
noInit, raises: [], tags: [].}```
Creates a new 4x4 3d transformation matrix. ax , ay , az is the local x axis. bx , by , bz is the local y axis. cx , cy , cz is the local z axis. tx , ty , tz is the translation.
`proc `&`(a, b: Matrix3d): Matrix3d {.noinit, raises: [], tags: [].}`
Concatenates matrices returning a new matrix.
`proc scale(s: float): Matrix3d {.noInit, raises: [], tags: [].}`
Returns a new scaling matrix.
`proc scale(s: float; org: Point3d): Matrix3d {.noInit, raises: [], tags: [].}`
Returns a new scaling matrix using, org as scale origin.
`proc stretch(sx, sy, sz: float): Matrix3d {.noInit, raises: [], tags: [].}`
Returns new a stretch matrix, which is a scale matrix with non uniform scale in x,y and z.
`proc stretch(sx, sy, sz: float; org: Point3d): Matrix3d {.noInit, raises: [], tags: [].}`
Returns a new stretch matrix, which is a scale matrix with non uniform scale in x,y and z. org is used as stretch origin.
`proc move(dx, dy, dz: float): Matrix3d {.noInit, raises: [], tags: [].}`
Returns a new translation matrix.
`proc move(v: Vector3d): Matrix3d {.noInit, raises: [], tags: [].}`
Returns a new translation matrix from a vector.
```proc rotate(angle: float; axis: Vector3d): Matrix3d {.noInit,
raises: [Exception, DivByZeroError], tags: [RootEffect].}```
Creates a rotation matrix that rotates angle radians over axis, which passes through origo.
```proc rotate(angle: float; org: Point3d; axis: Vector3d): Matrix3d {.noInit,
raises: [Exception, DivByZeroError], tags: [RootEffect].}```
Creates a rotation matrix that rotates angle radians over axis, which passes through org.
`proc rotateX(angle: float): Matrix3d {.noInit, raises: [], tags: [].}`
Creates a matrix that rotates around the x-axis with angle radians, which is also called a 'roll' matrix.
`proc rotateY(angle: float): Matrix3d {.noInit, raises: [], tags: [].}`
Creates a matrix that rotates around the y-axis with angle radians, which is also called a 'pitch' matrix.
`proc rotateZ(angle: float): Matrix3d {.noInit, raises: [], tags: [].}`
Creates a matrix that rotates around the z-axis with angle radians, which is also called a 'yaw' matrix.
`proc isUniform(m: Matrix3d; tol = 1e-006): bool {.raises: [], tags: [].}`
Checks if the transform is uniform, that is perpendicular axes of equal length, which means (for example) it cannot transform a sphere into an ellipsoid. tol is used as tolerance for both equal length comparison and perpendicular comparison.
```proc mirror(planeperp: Vector3d): Matrix3d {.noInit,
raises: [Exception, DivByZeroError], tags: [RootEffect].}```
Creates a matrix that mirrors over the plane that has planeperp as normal, and passes through origo. planeperp does not need to be normalized.
```proc mirror(org: Point3d; planeperp: Vector3d): Matrix3d {.noInit,
raises: [Exception, DivByZeroError], tags: [RootEffect].}```
Creates a matrix that mirrors over the plane that has planeperp as normal, and passes through org. planeperp does not need to be normalized.
`proc determinant(m: Matrix3d): float {.raises: [], tags: [].}`
Computes the determinant of matrix m.
`proc inverse(m: Matrix3d): Matrix3d {.noInit, raises: [DivByZeroError], tags: [].}`
Computes the inverse of matrix m. If the matrix determinant is zero, thus not invertible, a EDivByZero will be raised.
`proc equals(m1: Matrix3d; m2: Matrix3d; tol = 1e-006): bool {.raises: [], tags: [].}`
Checks if all elements of m1`and `m2 is equal within a given tolerance tol.
`proc `=~`(m1, m2: Matrix3d): bool {.raises: [], tags: [].}`
Checks if m1 and m2 is approximately equal, using a tolerance of 1e-6.
`proc transpose(m: Matrix3d): Matrix3d {.noInit, raises: [], tags: [].}`
Returns the transpose of m
`proc getXAxis(m: Matrix3d): Vector3d {.noInit, raises: [], tags: [].}`
Gets the local x axis of m
`proc getYAxis(m: Matrix3d): Vector3d {.noInit, raises: [], tags: [].}`
Gets the local y axis of m
`proc getZAxis(m: Matrix3d): Vector3d {.noInit, raises: [], tags: [].}`
Gets the local y axis of m
`proc `\$`(m: Matrix3d): string {.raises: [], tags: [].}`
String representation of m
`proc apply(m: Matrix3d; x, y, z: var float; translate = false) {.raises: [], tags: [].}`
Applies transformation m onto x , y , z , optionally using the translation part of the matrix.
`proc vector3d(x, y, z: float): Vector3d {.noInit, inline, raises: [], tags: [].}`
Returns a new 3d vector (x,`y`,`z`)
`proc len(v: Vector3d): float {.raises: [], tags: [].}`
Returns the length of the vector v.
`proc len=(v: var Vector3d; newlen: float) {.noInit, raises: [], tags: [].}`
Sets the length of the vector, keeping its direction. If the vector has zero length before changing it's length, an arbitrary vector of the requested length is returned.
`proc sqrLen(v: Vector3d): float {.inline, raises: [], tags: [].}`
Computes the squared length of the vector, which is faster than computing the absolute length.
`proc `\$`(v: Vector3d): string {.raises: [], tags: [].}`
String representation of v
`proc `&`(v: Vector3d; m: Matrix3d): Vector3d {.noInit, raises: [], tags: [].}`
Concatenate vector v with a transformation matrix. Transforming a vector ignores the translational part of the matrix.
`proc `&=`(v: var Vector3d; m: Matrix3d) {.noInit, raises: [], tags: [].}`
Applies transformation m onto v in place. Transforming a vector ignores the translational part of the matrix.
`proc transformNorm(v: var Vector3d; m: Matrix3d) {.raises: [DivByZeroError], tags: [].}`
Applies a normal direction transformation m onto v in place. The resulting vector is not normalized. Transforming a vector ignores the translational part of the matrix. If the matrix is not invertible (determinant=0), an EDivByZero will be raised.
`proc transformInv(v: var Vector3d; m: Matrix3d) {.raises: [DivByZeroError], tags: [].}`
Applies the inverse of m on vector v. Transforming a vector ignores the translational part of the matrix. Transforming a vector ignores the translational part of the matrix. If the matrix is not invertible (determinant=0), an EDivByZero will be raised.
`proc transformNormInv(vec: var Vector3d; m: Matrix3d) {.raises: [], tags: [].}`
Applies an inverse normal direction transformation m onto v in place. This is faster than creating an inverse matrix and transformNorm(...) it. Transforming a vector ignores the translational part of the matrix.
`proc tryNormalize(v: var Vector3d): bool {.raises: [], tags: [].}`
Modifies v to have a length of 1.0, keeping its angle. If v has zero length (and thus no angle), it is left unmodified and false is returned, otherwise true is returned.
`proc normalize(v: var Vector3d) {.inline, raises: [DivByZeroError], tags: [].}`
Modifies v to have a length of 1.0, keeping its angle. If v has zero length, an EDivByZero will be raised.
```proc rotate(vec: var Vector3d; angle: float; axis: Vector3d) {.raises: [DivByZeroError],
tags: [].}```
Rotates vec in place, with angle radians over axis, which passes through origo.
`proc scale(v: var Vector3d; s: float) {.raises: [], tags: [].}`
Scales the vector in place with factor s
`proc stretch(v: var Vector3d; sx, sy, sz: float) {.raises: [], tags: [].}`
Scales the vector non uniformly with factors sx , sy , sz
`proc mirror(v: var Vector3d; planeperp: Vector3d) {.raises: [DivByZeroError], tags: [].}`
Computes the mirrored vector of v over the plane that has planeperp as normal direction. planeperp does not need to be normalized.
`proc `-`(v: Vector3d): Vector3d {.raises: [], tags: [].}`
Negates a vector
`proc `+`(a135939, b135941: Vector3d): Vector3d {.inline, noInit, raises: [], tags: [].}`
```proc `+`(a135943: Vector3d; b135945: float): Vector3d {.inline, noInit, raises: [],
tags: [].}```
```proc `+`(a135947: float; b135949: Vector3d): Vector3d {.inline, noInit, raises: [],
tags: [].}```
`proc `-`(a135960, b135962: Vector3d): Vector3d {.inline, noInit, raises: [], tags: [].}`
```proc `-`(a135964: Vector3d; b135966: float): Vector3d {.inline, noInit, raises: [],
tags: [].}```
```proc `-`(a135968: float; b135970: Vector3d): Vector3d {.inline, noInit, raises: [],
tags: [].}```
`proc `*`(a135981, b135983: Vector3d): Vector3d {.inline, noInit, raises: [], tags: [].}`
```proc `*`(a135985: Vector3d; b135987: float): Vector3d {.inline, noInit, raises: [],
tags: [].}```
```proc `*`(a135989: float; b135991: Vector3d): Vector3d {.inline, noInit, raises: [],
tags: [].}```
`proc `/`(a136002, b136004: Vector3d): Vector3d {.inline, noInit, raises: [], tags: [].}`
```proc `/`(a136006: Vector3d; b136008: float): Vector3d {.inline, noInit, raises: [],
tags: [].}```
```proc `/`(a136010: float; b136012: Vector3d): Vector3d {.inline, noInit, raises: [],
tags: [].}```
`proc `+=`(a136023: var Vector3d; b136025: Vector3d) {.inline, raises: [], tags: [].}`
`proc `+=`(a136027: var Vector3d; b136029: float) {.inline, raises: [], tags: [].}`
`proc `-=`(a136097: var Vector3d; b136099: Vector3d) {.inline, raises: [], tags: [].}`
`proc `-=`(a136101: var Vector3d; b136103: float) {.inline, raises: [], tags: [].}`
`proc `*=`(a136171: var Vector3d; b136173: Vector3d) {.inline, raises: [], tags: [].}`
`proc `*=`(a136175: var Vector3d; b136177: float) {.inline, raises: [], tags: [].}`
`proc `/=`(a136245: var Vector3d; b136247: Vector3d) {.inline, raises: [], tags: [].}`
`proc `/=`(a136249: var Vector3d; b136251: float) {.inline, raises: [], tags: [].}`
`proc dot(v1, v2: Vector3d): float {.inline, raises: [], tags: [].}`
Computes the dot product of two vectors. Returns 0.0 if the vectors are perpendicular.
`proc cross(v1, v2: Vector3d): Vector3d {.inline, raises: [], tags: [].}`
Computes the cross product of two vectors. The result is a vector which is perpendicular to the plane of v1 and v2, which means cross(xaxis,yaxis)=zaxis. The magnitude of the result is zero if the vectors are colinear.
`proc equals(v1, v2: Vector3d; tol = 1e-006): bool {.raises: [], tags: [].}`
Checks if two vectors approximately equals with a tolerance.
`proc `=~`(v1, v2: Vector3d): bool {.raises: [], tags: [].}`
Checks if two vectors approximately equals with a hardcoded tolerance 1e-6
`proc angleTo(v1, v2: Vector3d): float {.raises: [], tags: [].}`
Returns the smallest angle between v1 and v2, which is in range 0-PI
```proc arbitraryAxis(norm: Vector3d): Matrix3d {.noInit, raises: [DivByZeroError],
tags: [].}```
Computes the rotation matrix that would transform world z vector into norm. The inverse of this matrix is useful to transform a planar 3d object to 2d space. This is the same algorithm used to interpret DXF and DWG files.
`proc bisect(v1, v2: Vector3d): Vector3d {.noInit, raises: [DivByZeroError], tags: [].}`
Computes the bisector between v1 and v2 as a normalized vector. If one of the input vectors has zero length, a normalized version of the other is returned. If both input vectors has zero length, an arbitrary normalized vector v1 is returned.
`proc point3d(x, y, z: float): Point3d {.noInit, inline, raises: [], tags: [].}`
Returns a new 4d point (x,`y`,`z`)
`proc sqrDist(a, b: Point3d): float {.raises: [], tags: [].}`
Computes the squared distance between a`and `b
`proc dist(a, b: Point3d): float {.inline, raises: [], tags: [].}`
Computes the absolute distance between a`and `b
`proc `\$`(p: Point3d): string {.raises: [], tags: [].}`
String representation of p
`proc `&`(p: Point3d; m: Matrix3d): Point3d {.raises: [], tags: [].}`
Concatenates a point p with a transform m, resulting in a new, transformed point.
`proc `&=`(p: var Point3d; m: Matrix3d) {.raises: [], tags: [].}`
Applies transformation m onto p in place.
`proc transformInv(p: var Point3d; m: Matrix3d) {.raises: [DivByZeroError], tags: [].}`
Applies the inverse of transformation m onto p in place. If the matrix is not invertable (determinant=0) , EDivByZero will be raised.
`proc `+`(p: Point3d; v: Vector3d): Point3d {.noInit, inline, raises: [], tags: [].}`
Adds a vector v to a point p, resulting in a new point.
`proc `+=`(p: var Point3d; v: Vector3d) {.noInit, inline, raises: [], tags: [].}`
Adds a vector v to a point p in place.
`proc `-`(p: Point3d; v: Vector3d): Point3d {.noInit, inline, raises: [], tags: [].}`
Subtracts a vector v from a point p, resulting in a new point.
`proc `-`(p1, p2: Point3d): Vector3d {.noInit, inline, raises: [], tags: [].}`
Subtracts p2`from `p1 resulting in a difference vector.
`proc `-=`(p: var Point3d; v: Vector3d) {.noInit, inline, raises: [], tags: [].}`
Subtracts a vector v from a point p in place.
`proc `=~`(p1, p2: Point3d): bool {.inline, raises: [], tags: [].}`
Checks if two vectors approximately equals with a hardcoded tolerance 1e-6
```proc rotate(p: var Point3d; rad: float; axis: Vector3d) {.raises: [DivByZeroError],
tags: [].}```
```proc rotate(p: var Point3d; angle: float; org: Point3d; axis: Vector3d) {.
raises: [DivByZeroError], tags: [].}```
`proc scale(p: var Point3d; fac: float) {.inline, raises: [], tags: [].}`
Scales a point in place fac times with world origo as origin.
`proc scale(p: var Point3d; fac: float; org: Point3d) {.inline, raises: [], tags: [].}`
Scales the point in place fac times with org as origin.
`proc stretch(p: var Point3d; facx, facy, facz: float) {.inline, raises: [], tags: [].}`
Scales a point in place non uniformly facx , facy , facz times with world origo as origin.
```proc stretch(p: var Point3d; facx, facy, facz: float; org: Point3d) {.inline, raises: [],
tags: [].}```
Scales the point in place non uniformly facx , facy , facz times with org as origin.
`proc move(p: var Point3d; dx, dy, dz: float) {.inline, raises: [], tags: [].}`
Translates a point dx , dy , dz in place.
`proc move(p: var Point3d; v: Vector3d) {.inline, raises: [], tags: [].}`
Translates a point with vector v in place.
`proc area(a, b, c: Point3d): float {.inline, raises: [], tags: [].}`
Computes the area of the triangle thru points a , b and c