<> <> <> <> DIRECTORY Matrix3d, RealFns, SV2d, SV3d, SVVector3d; Matrix3dImpl: PROGRAM IMPORTS RealFns, SVVector3d EXPORTS Matrix3d = BEGIN <> <<| x1 | | 1 0 0 tx | | x0 | | y1 | = | 0 1 0 ty | | y0 | | z1 | | 0 0 1 tz | | z0 | | 1 | | 0 0 0 1 | | 1 | >> Matrix4by4: TYPE = ARRAY [1..4] OF ARRAY [1..4] OF REAL; Matrix3by3: TYPE = ARRAY [1..3] OF ARRAY [1..3] OF REAL; Plane: TYPE = REF PlaneObj; PlaneObj: TYPE = SV3d.PlaneObj; Point3d: TYPE = SV3d.Point3d; Point2d: TYPE = SV2d.Point2d; Vector: TYPE = SVVector3d.Vector; Identity: PUBLIC PROCEDURE [] RETURNS [identityMat: Matrix4by4] = { i, j: CARDINAL; FOR i IN [1..4] DO -- in the first three columns, the elements are copied FOR j IN [1..4] DO IF i = j THEN identityMat[i][j] _ 1.0 ELSE identityMat[i][j] _ 0.0; ENDLOOP; ENDLOOP; }; Update: PUBLIC PROCEDURE [mat: Matrix4by4, point: Point3d] RETURNS [newPoint: Point3d] = { <> FOR i: NAT IN [1..3] DO newPoint[i] _ mat[i][1]*point[1] + mat[i][2]*point[2] + mat[i][3]*point[3] + mat[i][4]; ENDLOOP; }; <> UpdateVectorWithInverse: PUBLIC PROC [inverse: Matrix4by4, vec: Vector] RETURNS [newVec: Vector] = { <> FOR i: NAT IN [1..3] DO newVec[i] _ inverse[1][i]*vec[1] + inverse[2][i]*vec[2] + inverse[3][i]*vec[3]; ENDLOOP; }; UpdateVectorComputeInverse: PUBLIC PROC [mat: Matrix4by4, vec: Vector] RETURNS [newVec: Vector] = { inverse: Matrix4by4 _ Inverse[mat]; FOR i: NAT IN [1..3] DO newVec[i] _ inverse[1][i]*vec[1] + inverse[2][i]*vec[2] + inverse[3][i]*vec[3]; ENDLOOP; }; UpdateVectorEvenScaling: PUBLIC PROC [mat: Matrix4by4, vec: Vector] RETURNS [newVec: Vector] = { FOR i: NAT IN [1..3] DO newVec[i] _ mat[i][1]*vec[1] + mat[i][2]*vec[2] + mat[i][3]*vec[3]; ENDLOOP; }; UpdatePlaneWithInverse: PUBLIC PROC [inverse: Matrix4by4, plane: Plane] RETURNS [newPlane: Plane] = { <> newPlane _ NEW[PlaneObj]; newPlane.A _ inverse[1][1]*plane.A + inverse[2][1]*plane.B + inverse[3][1]*plane.C + inverse[4][1]*plane.D; newPlane.B _ inverse[1][2]*plane.A + inverse[2][2]*plane.B + inverse[3][2]*plane.C + inverse[4][2]*plane.D; newPlane.C _ inverse[1][3]*plane.A + inverse[2][3]*plane.B + inverse[3][3]*plane.C + inverse[4][3]*plane.D; newPlane.D _ inverse[1][4]*plane.A + inverse[2][4]*plane.B + inverse[3][4]*plane.C + inverse[4][4]*plane.D; }; UpdatePlaneComputeInverse: PUBLIC PROC [mat: Matrix4by4, plane: Plane] RETURNS [newPlane: Plane] = { inverse: Matrix4by4 _ Inverse[mat]; newPlane _ NEW[PlaneObj]; newPlane.A _ inverse[1][1]*plane.A + inverse[2][1]*plane.B + inverse[3][1]*plane.C + inverse[4][1]*plane.D; newPlane.B _ inverse[1][2]*plane.A + inverse[2][2]*plane.B + inverse[3][2]*plane.C + inverse[4][2]*plane.D; newPlane.C _ inverse[1][3]*plane.A + inverse[2][3]*plane.B + inverse[3][3]*plane.C + inverse[4][3]*plane.D; newPlane.D _ inverse[1][4]*plane.A + inverse[2][4]*plane.B + inverse[3][4]*plane.C + inverse[4][4]*plane.D; }; GetPerspective: PUBLIC PROC [oldPoint: Point3d, focalDistance: REAL _ 1800] RETURNS [newPoint: Point2d] = { IF oldPoint[3] >= focalDistance THEN SIGNAL IllegalDepth; newPoint[1] _ oldPoint[1]*focalDistance/(focalDistance - oldPoint[3]); -- x' = xf/(f-z) newPoint[2] _ oldPoint[2]*focalDistance/(focalDistance - oldPoint[3]); -- y' = yf/(f-z) }; IllegalDepth: PUBLIC SIGNAL = CODE; Scale: PUBLIC PROCEDURE [mat: Matrix4by4, sx, sy, sz: REAL] RETURNS [transMat: Matrix4by4] = { FOR j: NAT IN[1..4] DO transMat[1][j] _ mat[1][j]*sx; ENDLOOP; FOR j: NAT IN[1..4] DO transMat[2][j] _ mat[2][j]*sy; ENDLOOP; FOR j: NAT IN[1..4] DO transMat[3][j] _ mat[3][j]*sz; ENDLOOP; transMat[4][1] _ transMat[4][2] _ transMat[4][3] _ 0.0; transMat[4][4] _ 1.0; }; Translate: PUBLIC PROCEDURE [mat: Matrix4by4, dx, dy, dz: REAL] RETURNS [transMat: Matrix4by4] = { i, j: CARDINAL; FOR i IN [1..4] DO -- in the first three columns, the elements are copied FOR j IN [1..3] DO transMat[i][j] _ mat[i][j]; ENDLOOP; ENDLOOP; transMat[1][4] _ mat[1][4] + dx; -- add the translation offset to the last row transMat[2][4] _ mat[2][4] + dy; transMat[3][4] _ mat[3][4] + dz; transMat[4][4] _ 1.0; }; RotateAboutXAxis: PUBLIC PROCEDURE [mat: Matrix4by4, degrees: REAL] RETURNS [transMat: Matrix4by4] = { <> sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotMat: Matrix4by4 _ Identity[]; rotMat[2][2] _ rotMat[3][3] _ cosd; rotMat[2][3] _ -sind; rotMat[3][2] _ sind; transMat _ MatMult[rotMat,mat]; }; RotateAboutYAxis: PUBLIC PROCEDURE [mat: Matrix4by4, degrees: REAL] RETURNS [transMat: Matrix4by4] = { <> sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotMat: Matrix4by4 _ Identity[]; rotMat[1][1] _ rotMat[3][3] _ cosd; rotMat[3][1] _ -sind; rotMat[1][3] _ sind; transMat _ MatMult[rotMat,mat]; }; RotateAboutZAxis: PUBLIC PROCEDURE [mat: Matrix4by4, degrees: REAL] RETURNS [transMat: Matrix4by4] = { <> sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotMat: Matrix4by4 _ Identity[]; rotMat[1][1] _ rotMat[2][2] _ cosd; rotMat[1][2] _ -sind; rotMat[2][1] _ sind; transMat _ MatMult[rotMat,mat]; }; LocalScale: PUBLIC PROCEDURE [mat: Matrix4by4, sx, sy, sz: REAL] RETURNS [transMat: Matrix4by4] = { FOR i: NAT IN[1..3] DO transMat[i][1] _ mat[i][1]*sx; ENDLOOP; FOR i: NAT IN[1..3] DO transMat[i][2] _ mat[i][2]*sy; ENDLOOP; FOR i: NAT IN[1..3] DO transMat[i][3] _ mat[i][3]*sz; ENDLOOP; FOR i: NAT IN[1..3] DO transMat[i][4] _ mat[i][4]; ENDLOOP; transMat[4][1] _ transMat[4][2] _ transMat[4][3] _ 0.0; transMat[4][4] _ 1.0; }; LocalTranslate: PUBLIC PROCEDURE [mat: Matrix4by4, dx, dy, dz: REAL] RETURNS [transMat: Matrix4by4] = { i, j: CARDINAL; FOR i IN [1..4] DO -- in the first three columns, the elements are copied FOR j IN [1..3] DO transMat[i][j] _ mat[i][j]; ENDLOOP; ENDLOOP; transMat[1][4] _ mat[1][1]*dx + mat[1][2]*dy + mat[1][3]*dz + mat[1][4]; transMat[2][4] _ mat[2][1]*dx + mat[2][2]*dy + mat[2][3]*dz + mat[2][4]; transMat[3][4] _ mat[3][1]*dx + mat[3][2]*dy + mat[3][3]*dz + mat[3][4]; transMat[4][4] _ 1.0; }; LocalRotateAboutXAxis: PUBLIC PROCEDURE [mat: Matrix4by4, degrees: REAL] RETURNS [transMat: Matrix4by4] = { sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotMat: Matrix4by4 _ Identity[]; rotMat[2][2] _ rotMat[3][3] _ cosd; rotMat[2][3] _ -sind; rotMat[3][2] _ sind; transMat _ MatMult[mat,rotMat]; }; LocalRotateAboutYAxis: PUBLIC PROCEDURE [mat: Matrix4by4, degrees: REAL] RETURNS [transMat: Matrix4by4] = { sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotMat: Matrix4by4 _ Identity[]; rotMat[1][1] _ rotMat[3][3] _ cosd; rotMat[3][1] _ -sind; rotMat[1][3] _ sind; transMat _ MatMult[mat,rotMat]; }; LocalRotateAboutZAxis: PUBLIC PROCEDURE [mat: Matrix4by4, degrees: REAL] RETURNS [transMat: Matrix4by4] = { sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotMat: Matrix4by4 _ Identity[]; rotMat[1][1] _ rotMat[2][2] _ cosd; rotMat[1][2] _ -sind; rotMat[2][1] _ sind; transMat _ MatMult[mat,rotMat]; }; MatMult: PUBLIC PROCEDURE [left, right: Matrix4by4] RETURNS [transMat: Matrix4by4] = { i, j: CARDINAL; FOR i IN [1..3] DO -- The last row is always [0 0 0 1] FOR j IN [1..3] DO -- In the first three columns, the last element is always zero transMat[i][j] _ left[i][1]*right[1][j] + left[i][2]*right[2][j] + left[i][3]*right[3][j]; ENDLOOP; ENDLOOP; FOR i IN [1..3] DO -- calculate the last column transMat[i][4] _ left[i][1]*right[1][4] + left[i][2]*right[2][4] + left[i][3]*right[3][4] + left[i][4]; ENDLOOP; transMat[4][1] _ transMat[4][2] _ transMat[4][3] _ 0; -- Last row transMat[4][4] _ 1.0; }; MakeScaleMat: PUBLIC PROC [sx, sy, sz: REAL] RETURNS [scale: Matrix4by4] = { scale _ Identity[]; scale[1][1] _ sx; scale[2][2] _ sy; scale[3][3] _ sz; }; MakeTranslateMat: PUBLIC PROC [dx, dy, dz: REAL] RETURNS [trans: Matrix4by4] = { trans _ Identity[]; trans[1][4] _ dx; trans[2][4] _ dy; trans[3][4] _ dz; }; MakeRotateXMat: PUBLIC PROC [degrees: REAL] RETURNS [rotx: Matrix4by4] = { sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotx _ Identity[]; rotx[2][2] _ rotx[3][3] _ cosd; rotx[2][3] _ -sind; rotx[3][2] _ sind; }; MakeRotateYMat: PUBLIC PROC [degrees: REAL] RETURNS [roty: Matrix4by4] = { sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; roty _ Identity[]; roty[1][1] _ roty[3][3] _ cosd; roty[3][1] _ -sind; roty[1][3] _ sind; }; MakeRotateZMat: PUBLIC PROC [degrees: REAL] RETURNS [rotz: Matrix4by4] = { sind: REAL _ RealFns.SinDeg[degrees]; cosd: REAL _ RealFns.CosDeg[degrees]; rotz _ Identity[]; rotz[1][1] _ rotz[2][2] _ cosd; rotz[1][2] _ -sind; rotz[2][1] _ sind; }; MakeMatFromZandXAxis: PUBLIC PROC [zAxis, xAxis: Vector, origin: Point3d] RETURNS [mat: Matrix4by4] = { << Uses the normalized version of the zAxis given as it is. Projects xAxis onto the plane of zAxis and normalizes. Finds y by cross product.>> normZ: Vector _ SVVector3d.Normalize[zAxis]; normY: Vector _ SVVector3d.Normalize[SVVector3d.CrossProduct[normZ, xAxis]]; normX: Vector _ SVVector3d.CrossProduct[normY, normZ]; FOR i: NAT IN[1..3] DO mat[i][1] _ normX[i]; mat[i][2] _ normY[i]; mat[i][3] _ normZ[i]; mat[i][4] _ origin[i]; ENDLOOP; mat[4][1] _ mat[4][2] _ mat[4][3] _ 0; mat[4][4] _ 1; }; MakeHorizontalMatFromZAxis: PUBLIC PROC [zAxis: Vector, origin: Point3d] RETURNS [mat: Matrix4by4] = { << Uses zAxis as it is. Finds a horizontal x axis orthogonal to zAxis. If zAxis is vertical, then there are infinitely many. Chooses [1,0,0] in this case.>> xAxis: Vector; IF zAxis[1] = 0 AND zAxis[3] = 0 THEN xAxis _ [1,0,0] ELSE xAxis _ SVVector3d.CrossProduct[[0,1,0], zAxis]; mat _ MakeMatFromZandXAxis[zAxis, xAxis, origin]; }; MakeMatFromAxes: PUBLIC PROC [xAxis, yAxis, zAxis: Vector, origin: Point3d] RETURNS [mat: Matrix4by4] = { FOR i: NAT IN[1..3] DO mat[i][1] _ xAxis[i]; mat[i][2] _ yAxis[i]; mat[i][3] _ zAxis[i]; mat[i][4] _ origin[i]; ENDLOOP; mat[4][1] _ mat[4][2] _ mat[4][3] _ 0; mat[4][4] _ 1; }; ScaleFromMatrix: PUBLIC PROC [mat: Matrix4by4] RETURNS [sx, sy, sz: REAL] = { << Finds out how much this matrix will scale an object it is applied to when you apply a scaling matrix to some matrix A = [a b c d] [Sx 0 0 0] [Sx*a Sy*b Sz*c d] [e f g h] * [0 Sy 0 0] you get [Sx*e Sy*f Sz*g h] [i j k l] [0 0 Sz 0] [Sx*i Sy*j Sz*k l] [0 0 0 1] [0 0 0 1] [0 0 0 1] Since [a,e,i], [b,f,j], and [c,g,k] are unit vectors to begin with, we can find the scaling factors by taking the magnitudes of these quantities>> sx _ SVVector3d.Magnitude[[mat[1][1],mat[2][1],mat[3][1]]]; sy _ SVVector3d.Magnitude[[mat[1][2],mat[2][2],mat[3][2]]]; sz _ SVVector3d.Magnitude[[mat[1][3],mat[2][3],mat[3][3]]]; }; OriginOfMatrix: PUBLIC PROC [mat: Matrix4by4] RETURNS [origin: Point3d] = { origin[1] _ mat[1][4]; origin[2] _ mat[2][4]; origin[3] _ mat[3][4]; }; XAxisOfMatrix: PUBLIC PROC [mat: Matrix4by4] RETURNS [xAxis: Vector] = { xAxis[1] _ mat[1][1]; xAxis[2] _ mat[2][1]; xAxis[3] _ mat[3][1]; }; YAxisOfMatrix: PUBLIC PROC [mat: Matrix4by4] RETURNS [yAxis: Vector] = { yAxis[1] _ mat[1][2]; yAxis[2] _ mat[2][2]; yAxis[3] _ mat[3][2]; }; ZAxisOfMatrix: PUBLIC PROC [mat: Matrix4by4] RETURNS [zAxis: Vector] = { zAxis[1] _ mat[1][3]; zAxis[2] _ mat[2][3]; zAxis[3] _ mat[3][3]; }; <> InverseNoScaling: PUBLIC PROC [mat: Matrix4by4] RETURNS [inverse: Matrix4by4] = { FOR i: NAT IN[1..3] DO FOR j: NAT IN[1..3] DO inverse[i][j] _ mat[j][i]; ENDLOOP; ENDLOOP; <> inverse[1][4] _ -(mat[1][4]*mat[1][1] + mat[2][4]*mat[2][1] + mat[3][4]*mat[3][1]); inverse[2][4] _ -(mat[1][4]*mat[1][2] + mat[2][4]*mat[2][2] + mat[3][4]*mat[3][2]); inverse[3][4] _ -(mat[1][4]*mat[1][3] + mat[2][4]*mat[2][3] + mat[3][4]*mat[3][3]); inverse[4][1] _ inverse[4][2] _ inverse[4][3] _ 0.0; inverse[4][4] _ 1.0; }; DegenerateInverse: PUBLIC SIGNAL = CODE; Cofactor: PUBLIC PROC [mat: Matrix4by4, row, col: NAT] RETURNS [cof: REAL] = { << returns the determinant of the 3 by 3 matrix which results by crossing off row row and column col in mat.>> rowCount, colCount: NAT; exponent: NAT; m3: Matrix3by3; rowCount _ 0; FOR i: NAT IN[1..4] DO IF i = row THEN LOOP; rowCount _ rowCount + 1; colCount _ 0; FOR j: NAT IN[1..4] DO IF j = col THEN LOOP; colCount _ colCount + 1; m3[rowCount][colCount] _ mat[i][j]; ENDLOOP; ENDLOOP; cof _ Determinant3by3[m3]; exponent _ row+col; IF (exponent/2)*2 # exponent THEN cof _ -cof; }; Determinant3by3: PROC [m3: Matrix3by3] RETURNS [det: REAL] = { det _ m3[1][1]*m3[2][2]*m3[3][3] + m3[2][1]*m3[3][2]*m3[1][3] + m3[3][1]*m3[1][2]*m3[2][3] - (m3[3][1]*m3[2][2]*m3[1][3] + m3[2][1]*m3[1][2]*m3[3][3] + m3[1][1]*m3[3][2]*m3[2][3]); }; Determinant: PUBLIC PROC [mat: Matrix4by4] RETURNS [det: REAL] = { << Because the bottom row of mat has three zeros and a 1, this reduces to finding the cofactor of (4,4).>> det _ Cofactor[mat,4,4]; }; Transpose: PUBLIC PROC [mat: Matrix4by4] RETURNS [matT: Matrix4by4] = { <> FOR i: NAT IN[1..4] DO FOR j: NAT IN[1..4] DO matT[i][j] _ mat[j][i]; ENDLOOP; ENDLOOP; }; Inverse: PUBLIC PROC [mat: Matrix4by4] RETURNS [inverse: Matrix4by4] = { << inverts any matrix of the given form (even with scaling). Find the matrix of Cofactors, transpose it and divide by the determinant>> det: REAL_ Determinant[mat]; IF det = 0 THEN SIGNAL DegenerateInverse; FOR i: NAT IN[1..3] DO FOR j: NAT IN[1..4] DO inverse[i][j] _ Cofactor[mat,j,i]/det; -- transpose as you do it ENDLOOP; ENDLOOP; inverse[4][1] _ inverse[4][2] _ inverse[4][3] _ 0.0; inverse[4][4] _ 1.0; -- these would also be the calculated values if we did them in the loop }; WorldToLocal: PUBLIC PROC [AinWorld,BinWorld: Matrix4by4] RETURNS [BinTermsOfA: Matrix4by4] = { << clearly (AinWorld)(BinTermsOfA) = BinWorld;>> << Hence BinTermsOfA = (Inverse[AinWorld])(BinWorld);>> worldAInverse: Matrix4by4 _ Inverse[AinWorld]; BinTermsOfA _ MatMult[worldAInverse,BinWorld]; }; <> UnitNormalize: PUBLIC PROC [mat: Matrix4by4] RETURNS [unitMat: Matrix4by4] = { <> n, o, a: Vector; FOR i: NAT IN[1..3] DO n[i] _ mat[i][1]; o[i] _ mat[i][2]; ENDLOOP; n _ SVVector3d.Normalize[n]; a _ SVVector3d.CrossProduct[n,o]; a _ SVVector3d.Normalize[a]; o _ SVVector3d.CrossProduct[a,n]; FOR i: NAT IN[1..3] DO unitMat[i][1] _ n[i]; unitMat[i][2] _ o[i]; unitMat[i][3] _ a[i]; unitMat[i][4] _ mat[i][4]; ENDLOOP; }; -- end of UnitNormalize Normalize: PUBLIC PROC [mat: Matrix4by4] RETURNS [normMat: Matrix4by4] = { << Finds a matrix with the same translation, rotation and scaling as mat, but with the axes guaranteed to be orthogonal. take n, o, and a as in "UnitNormalize" above. Start with n. Find magnitude of o, and a. a _ (n x o)|a| / |n x o| o _ (a x n)|o| / |a x n|>> magA, magO: REAL; n, o, a: Vector; origin: Point3d; nCrossO, aCrossN: Vector; n _ XAxisOfMatrix[mat]; o _ YAxisOfMatrix[mat]; a _ ZAxisOfMatrix[mat]; origin _ OriginOfMatrix[mat]; magA _ SVVector3d.Magnitude[a]; magO _ SVVector3d.Magnitude[o]; nCrossO _ SVVector3d.CrossProduct[n,o]; a _ SVVector3d.Scale[nCrossO, magA/SVVector3d.Magnitude[nCrossO]]; aCrossN _ SVVector3d.CrossProduct[a,n]; o _ SVVector3d.Scale[aCrossN, magO/SVVector3d.Magnitude[aCrossN]]; normMat _ MakeMatFromAxes[n,o,a,origin]; }; -- end of Normalize Max: PRIVATE PROC [a, b: REAL] RETURNS [REAL] = { RETURN[IF a >= b THEN a ELSE b]; }; NormalizeUniformScale: PUBLIC PROC [mat: Matrix4by4] RETURNS [normMat: Matrix4by4] = { <> magA, magO, magN, s: REAL; n, o, a: Vector; nCrossO, aCrossN: Vector; FOR i: NAT IN[1..3] DO n[i] _ mat[i][1]; o[i] _ mat[i][2]; ENDLOOP; magN _ SVVector3d.Magnitude[n]; magA _ SVVector3d.Magnitude[a]; magO _ SVVector3d.Magnitude[o]; s _ Max[magN, magA]; s _ Max[s, magO]; n _ SVVector3d.Scale[n, s/magN]; nCrossO _ SVVector3d.CrossProduct[n,o]; a _ SVVector3d.Scale[nCrossO, s/SVVector3d.Magnitude[nCrossO]]; aCrossN _ SVVector3d.CrossProduct[a,n]; o _ SVVector3d.Scale[aCrossN, s/SVVector3d.Magnitude[aCrossN]]; FOR i: NAT IN[1..3] DO normMat[i][1] _ n[i]; normMat[i][2] _ o[i]; normMat[i][3] _ a[i]; normMat[i][4] _ mat[i][4]; ENDLOOP; }; -- end of NormalizeUniformScale NormalizeNoRotation: PUBLIC PROC [mat: Matrix4by4] RETURNS [normMat: Matrix4by4] = { <> transMat: Matrix4by4 _ MakeTranslateMat[mat[1][4], mat[2][4], mat[3][4]]; magA, magO, magN: REAL; n, o, a: Vector; FOR i: NAT IN[1..3] DO n[i] _ mat[i][1]; o[i] _ mat[i][2]; a[i] _ mat[i][3]; ENDLOOP; magN _ SVVector3d.Magnitude[n]; magO _ SVVector3d.Magnitude[o]; magA _ SVVector3d.Magnitude[a]; FOR i: NAT IN[1..3] DO normMat[i][1] _ transMat[i][1]*magN; normMat[i][2] _ transMat[i][2]*magO; normMat[i][3] _ transMat[i][3]*magA; normMat[i][4] _ mat[i][4]; ENDLOOP; }; -- end of NormalizeNoRotation NormalizeNoTranslation: PUBLIC PROC [mat: Matrix4by4] RETURNS [normMat: Matrix4by4] = { <> normMat _ Normalize[mat]; FOR i: NAT IN[1..3] DO normMat[i][4] _ 0; ENDLOOP; }; END. <> <> <<>> <<>>