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:TMatrix3d=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:TPoint3d=point3d(100.0,150.0,200.0)

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

pt &= m # transforms pt in place

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

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

Types

TMatrix3d = 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 ]   Source
TPoint3d = object 
  x*, y*, z*: float
Implements a non-homegeneous 2d point stored as an x , y and z coordinate.   Source
TVector3d = 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.   Source

Lets

IDMATRIX: TMatrix3d = 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)
Quick access to a 3d identity matrix   Source
ORIGO: TPoint3d = point3d(0.0, 0.0, 0.0)
Quick access to point (0,0)   Source
XAXIS: TVector3d = vector3d(1.0, 0.0, 0.0)
Quick access to an 3d x-axis unit vector   Source
YAXIS: TVector3d = vector3d(0.0, 1.0, 0.0)
Quick access to an 3d y-axis unit vector   Source
ZAXIS: TVector3d = vector3d(0.0, 0.0, 1.0)
Quick access to an 3d z-axis unit vector   Source

Procs

proc setElements(t: var TMatrix3d; 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.   Source
proc matrix3d(ax, ay, az, aw, bx, by, bz, bw, cx, cy, cz, cw, tx, ty, tz, tw: float): TMatrix3d {.
    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.   Source
proc `&`(a, b: TMatrix3d): TMatrix3d {.noinit, raises: [], tags: [].}
Concatenates matrices returning a new matrix.   Source
proc scale(s: float): TMatrix3d {.noInit, raises: [], tags: [].}
Returns a new scaling matrix.   Source
proc scale(s: float; org: TPoint3d): TMatrix3d {.noInit, raises: [], tags: [].}
Returns a new scaling matrix using, org as scale origin.   Source
proc stretch(sx, sy, sz: float): TMatrix3d {.noInit, raises: [], tags: [].}
Returns new a stretch matrix, which is a scale matrix with non uniform scale in x,y and z.   Source
proc stretch(sx, sy, sz: float; org: TPoint3d): TMatrix3d {.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.   Source
proc move(dx, dy, dz: float): TMatrix3d {.noInit, raises: [], tags: [].}
Returns a new translation matrix.   Source
proc move(v: TVector3d): TMatrix3d {.noInit, raises: [], tags: [].}
Returns a new translation matrix from a vector.   Source
proc rotate(angle: float; axis: TVector3d): TMatrix3d {.noInit, 
    raises: [Exception, DivByZeroError], tags: [RootEffect].}
Creates a rotation matrix that rotates angle radians over axis, which passes through origo.   Source
proc rotate(angle: float; org: TPoint3d; axis: TVector3d): TMatrix3d {.noInit, 
    raises: [Exception, DivByZeroError], tags: [RootEffect].}
Creates a rotation matrix that rotates angle radians over axis, which passes through org.   Source
proc rotateX(angle: float): TMatrix3d {.noInit, raises: [], tags: [].}
Creates a matrix that rotates around the x-axis with angle radians, which is also called a 'roll' matrix.   Source
proc rotateY(angle: float): TMatrix3d {.noInit, raises: [], tags: [].}
Creates a matrix that rotates around the y-axis with angle radians, which is also called a 'pitch' matrix.   Source
proc rotateZ(angle: float): TMatrix3d {.noInit, raises: [], tags: [].}
Creates a matrix that rotates around the z-axis with angle radians, which is also called a 'yaw' matrix.   Source
proc isUniform(m: TMatrix3d; tol = 1e-06): 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.   Source
proc mirror(planeperp: TVector3d): TMatrix3d {.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.   Source
proc mirror(org: TPoint3d; planeperp: TVector3d): TMatrix3d {.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.   Source
proc determinant(m: TMatrix3d): float {.raises: [], tags: [].}
Computes the determinant of matrix m.   Source
proc inverse(m: TMatrix3d): TMatrix3d {.noInit, raises: [DivByZeroError], 
                                        tags: [].}
Computes the inverse of matrix m. If the matrix determinant is zero, thus not invertible, a EDivByZero will be raised.   Source
proc equals(m1: TMatrix3d; m2: TMatrix3d; tol = 1e-06): bool {.raises: [], 
    tags: [].}
Checks if all elements of m1`and `m2 is equal within a given tolerance tol.   Source
proc `=~`(m1, m2: TMatrix3d): bool {.raises: [], tags: [].}
Checks if m1 and m2 is approximately equal, using a tolerance of 1e-6.   Source
proc transpose(m: TMatrix3d): TMatrix3d {.noInit, raises: [], tags: [].}
Returns the transpose of m   Source
proc getXAxis(m: TMatrix3d): TVector3d {.noInit, raises: [], tags: [].}
Gets the local x axis of m   Source
proc getYAxis(m: TMatrix3d): TVector3d {.noInit, raises: [], tags: [].}
Gets the local y axis of m   Source
proc getZAxis(m: TMatrix3d): TVector3d {.noInit, raises: [], tags: [].}
Gets the local y axis of m   Source
proc `$`(m: TMatrix3d): string {.raises: [], tags: [].}
String representation of m   Source
proc apply(m: TMatrix3d; x, y, z: var float; translate = false) {.raises: [], 
    tags: [].}
Applies transformation m onto x , y , z , optionally using the translation part of the matrix.   Source
proc vector3d(x, y, z: float): TVector3d {.noInit, inline, raises: [], tags: [].}
Returns a new 3d vector (x,`y`,`z`)   Source
proc len(v: TVector3d): float {.raises: [], tags: [].}
Returns the length of the vector v.   Source
proc len=(v: var TVector3d; 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.   Source
proc sqrLen(v: TVector3d): float {.inline, raises: [], tags: [].}
Computes the squared length of the vector, which is faster than computing the absolute length.   Source
proc `$`(v: TVector3d): string {.raises: [], tags: [].}
String representation of v   Source
proc `&`(v: TVector3d; m: TMatrix3d): TVector3d {.noInit, raises: [], tags: [].}
Concatenate vector v with a transformation matrix. Transforming a vector ignores the translational part of the matrix.   Source
proc `&=`(v: var TVector3d; m: TMatrix3d) {.noInit, raises: [], tags: [].}
Applies transformation m onto v in place. Transforming a vector ignores the translational part of the matrix.   Source
proc transformNorm(v: var TVector3d; m: TMatrix3d) {.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.   Source
proc transformInv(v: var TVector3d; m: TMatrix3d) {.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.   Source
proc transformNormInv(vec: var TVector3d; m: TMatrix3d) {.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.   Source
proc tryNormalize(v: var TVector3d): 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.   Source
proc normalize(v: var TVector3d) {.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.   Source
proc rotate(vec: var TVector3d; angle: float; axis: TVector3d) {.
    raises: [DivByZeroError], tags: [].}
Rotates vec in place, with angle radians over axis, which passes through origo.   Source
proc scale(v: var TVector3d; s: float) {.raises: [], tags: [].}
Scales the vector in place with factor s   Source
proc stretch(v: var TVector3d; sx, sy, sz: float) {.raises: [], tags: [].}
Scales the vector non uniformly with factors sx , sy , sz   Source
proc mirror(v: var TVector3d; planeperp: TVector3d) {.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.   Source
proc `-`(v: TVector3d): TVector3d {.raises: [], tags: [].}
Negates a vector   Source
proc `+`(a, b: TVector3d): TVector3d {.inline, noInit, raises: [], tags: [].}
  Source
proc `+`(a: TVector3d; b: float): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `+`(a: float; b: TVector3d): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `-`(a, b: TVector3d): TVector3d {.inline, noInit, raises: [], tags: [].}
  Source
proc `-`(a: TVector3d; b: float): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `-`(a: float; b: TVector3d): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `*`(a, b: TVector3d): TVector3d {.inline, noInit, raises: [], tags: [].}
  Source
proc `*`(a: TVector3d; b: float): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `*`(a: float; b: TVector3d): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `/`(a, b: TVector3d): TVector3d {.inline, noInit, raises: [], tags: [].}
  Source
proc `/`(a: TVector3d; b: float): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `/`(a: float; b: TVector3d): TVector3d {.inline, noInit, raises: [], 
    tags: [].}
  Source
proc `+=`(a: var TVector3d; b: TVector3d) {.inline, raises: [], tags: [].}
  Source
proc `+=`(a: var TVector3d; b: float) {.inline, raises: [], tags: [].}
  Source
proc `-=`(a: var TVector3d; b: TVector3d) {.inline, raises: [], tags: [].}
  Source
proc `-=`(a: var TVector3d; b: float) {.inline, raises: [], tags: [].}
  Source
proc `*=`(a: var TVector3d; b: TVector3d) {.inline, raises: [], tags: [].}
  Source
proc `*=`(a: var TVector3d; b: float) {.inline, raises: [], tags: [].}
  Source
proc `/=`(a: var TVector3d; b: TVector3d) {.inline, raises: [], tags: [].}
  Source
proc `/=`(a: var TVector3d; b: float) {.inline, raises: [], tags: [].}
  Source
proc dot(v1, v2: TVector3d): float {.inline, raises: [], tags: [].}
Computes the dot product of two vectors. Returns 0.0 if the vectors are perpendicular.   Source
proc cross(v1, v2: TVector3d): TVector3d {.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.   Source
proc equals(v1, v2: TVector3d; tol = 1e-06): bool {.raises: [], tags: [].}
Checks if two vectors approximately equals with a tolerance.   Source
proc `=~`(v1, v2: TVector3d): bool {.raises: [], tags: [].}
Checks if two vectors approximately equals with a hardcoded tolerance 1e-6   Source
proc angleTo(v1, v2: TVector3d): float {.raises: [], tags: [].}
Returns the smallest angle between v1 and v2, which is in range 0-PI   Source
proc arbitraryAxis(norm: TVector3d): TMatrix3d {.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.   Source
proc bisect(v1, v2: TVector3d): TVector3d {.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.   Source
proc point3d(x, y, z: float): TPoint3d {.noInit, inline, raises: [], tags: [].}
Returns a new 4d point (x,`y`,`z`)   Source
proc sqrDist(a, b: TPoint3d): float {.raises: [], tags: [].}
Computes the squared distance between a`and `b   Source
proc dist(a, b: TPoint3d): float {.inline, raises: [], tags: [].}
Computes the absolute distance between a`and `b   Source
proc `$`(p: TPoint3d): string {.raises: [], tags: [].}
String representation of p   Source
proc `&`(p: TPoint3d; m: TMatrix3d): TPoint3d {.raises: [], tags: [].}
Concatenates a point p with a transform m, resulting in a new, transformed point.   Source
proc `&=`(p: var TPoint3d; m: TMatrix3d) {.raises: [], tags: [].}
Applies transformation m onto p in place.   Source
proc transformInv(p: var TPoint3d; m: TMatrix3d) {.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.   Source
proc `+`(p: TPoint3d; v: TVector3d): TPoint3d {.noInit, inline, raises: [], 
    tags: [].}
Adds a vector v to a point p, resulting in a new point.   Source
proc `+=`(p: var TPoint3d; v: TVector3d) {.noInit, inline, raises: [], tags: [].}
Adds a vector v to a point p in place.   Source
proc `-`(p: TPoint3d; v: TVector3d): TPoint3d {.noInit, inline, raises: [], 
    tags: [].}
Subtracts a vector v from a point p, resulting in a new point.   Source
proc `-`(p1, p2: TPoint3d): TVector3d {.noInit, inline, raises: [], tags: [].}
Subtracts p2`from `p1 resulting in a difference vector.   Source
proc `-=`(p: var TPoint3d; v: TVector3d) {.noInit, inline, raises: [], tags: [].}
Subtracts a vector v from a point p in place.   Source
proc `=~`(p1, p2: TPoint3d): bool {.inline, raises: [], tags: [].}
Checks if two vectors approximately equals with a hardcoded tolerance 1e-6   Source
proc rotate(p: var TPoint3d; rad: float; axis: TVector3d) {.
    raises: [DivByZeroError], tags: [].}
Rotates point p in place rad radians about an axis passing through origo.   Source
proc rotate(p: var TPoint3d; angle: float; org: TPoint3d; axis: TVector3d) {.
    raises: [DivByZeroError], tags: [].}
Rotates point p in place rad radians about an axis passing through org   Source
proc scale(p: var TPoint3d; fac: float) {.inline, raises: [], tags: [].}
Scales a point in place fac times with world origo as origin.   Source
proc scale(p: var TPoint3d; fac: float; org: TPoint3d) {.inline, raises: [], 
    tags: [].}
Scales the point in place fac times with org as origin.   Source
proc stretch(p: var TPoint3d; facx, facy, facz: float) {.inline, raises: [], 
    tags: [].}
Scales a point in place non uniformly facx , facy , facz times with world origo as origin.   Source
proc stretch(p: var TPoint3d; facx, facy, facz: float; org: TPoint3d) {.inline, 
    raises: [], tags: [].}
Scales the point in place non uniformly facx , facy , facz times with org as origin.   Source
proc move(p: var TPoint3d; dx, dy, dz: float) {.inline, raises: [], tags: [].}
Translates a point dx , dy , dz in place.   Source
proc move(p: var TPoint3d; v: TVector3d) {.inline, raises: [], tags: [].}
Translates a point with vector v in place.   Source
proc area(a, b, c: TPoint3d): float {.inline, raises: [], tags: [].}
Computes the area of the triangle thru points a , b and c   Source