Mega Code Archive

 
Categories / Delphi / Examples
 

Getnormalfunc

type TVector = record x : Real; y : Real; z : Real; xcl : Integer; ycl : Integer; zcl : Integer; end; TPoint3D = record x: Real; y: Real; z: Real; end; function TWireFrame.GetNormalVector(pt1, pt2, pt3: TPoint3D): TVector; var vector1: TVector; vector2: TVector; tempVector: TVector; normal: TVector; temp: Double; begin {first use 'subtraction' to calculate two vectors THAT LIE IN THE PLANE OF THE GIVEN POINTS...} vector1.x := (pt1.x - pt2.x); vector1.y := (pt1.y - pt2.y); vector1.z := (pt1.z - pt2.z); vector2.x := (pt3.x - pt2.x); vector2.y := (pt3.y - pt2.y); vector2.z := (pt3.z - pt2.z); {now use the CROSS-PRODUCT operation to find the normal...} {if we have two vectors, 'A' and 'B', where vector1 is (vector1.x, vector1.y, vector1.z) and vector2 is (vector2.x, vector2.y, vector2.z) and we call the cross-product (tempVector.x, tempVector.y, tempVector.z) then the operations involved in calculating the cross-product are as follows:} tempVector.x := ((vector1.y * vector2.z) - (vector2.y * vector1.z)); tempVector.y := (((-vector1.x) * vector2.z) + (vector2.x * vector1.z)); tempVector.z := ((vector1.x * vector2.y) - (vector2.x * vector1.y)); {now recalculate the x, y, and z components of the normal vector [normal.x, normal.y and normal.z] so that this vector has a length of 1 unit: this then becomes the 'unit vector' that has these particular x, y and z values...} temp := sqrt((sqr(tempVector.x)) + (sqr(tempVector.y)) + (sqr(tempVector.z))); normal.x := (tempVector.x / temp); normal.y := (tempVector.y / temp); normal.z := (tempVector.z / temp); {next code block is definitely non-standard, and is used for the time being to determine the SIGN of each column (x values, y values, or z values) in the three pts we are handed. If some are positive, and some are negative, we do nothing...} if ((pt1.x >= 0) and (pt2.x >= 0) and (pt3.x >= 0)) then if (normal.x < 0) then normal.x := (normal.x * -1); if ((pt1.x < 0) and (pt2.x < 0) and (pt3.x < 0)) then if (normal.x >= 0) then normal.x := (normal.x * -1); if ((pt1.y >= 0) and (pt2.y >= 0) and (pt3.y >= 0)) then if (normal.y < 0) then normal.y := (normal.y * -1); if ((pt1.y < 0) and (pt2.y < 0) and (pt3.y < 0)) then if (normal.y >= 0) then normal.y := (normal.y * -1); if ((pt1.z >= 0) and (pt2.z >= 0) and (pt3.z >= 0)) then if (normal.z < 0) then normal.z := (normal.z * -1); if ((pt1.z < 0) and (pt2.z < 0) and (pt3.z < 0)) then if (normal.z >= 0) then normal.z := (normal.z * -1); Result := normal; end; ********************************************************** CALCULATION OF 'NORMALS' Richard D Ebbs Christmas 1999 PSUEDOCODE AND DESCRIPTION To determine the normal vector for a given plane use the 'cross product' of any two vectors that lie in the plane. The cross product of these two vectors will then itself be a vector perpendicular to the original plane. So, given the 3D coordinates of vertices that form the corners of a flat polygon we know first of all that these points lie in the same plane (and we are only dealing with 'flat' polygons which do lie in one plane here) and furthermore we can construct two vectors that lie in the plane from the coordinate data for any three points on the plane (eg face corners aka vertices). The first step is to treat the COORDINATE values for each of these points as VECTORS. (So that Point(11,12,13) is VECTOR(11,12,13) -simple as that!). Next we find the two vectors that define the two lines connecting these three points. Let's say we have three points: x1, y1 and z1 x2, y2 and z2 x3, y3 and z3 then we find the vector for the line that connects the first two points by a simple 'subtraction' operation, as follows: vector1.x := (x1 - x2); vector1.y := (y1 - y2); vector1.z := (z1 - z2); and calculate the vector for the second line in a similar way (using the next pair of points) vector2.x := (x3 - x2); vector2.y := (y3 - y2); vector2.z := (z3 - z2); OK, we've now got the vectors for two lines that lie in the plane of the surface we are dealing with. The next step is to use the CROSS-PRODUCT operation to enable us to find the vector that is normal (ie perpendicular) to that plane. {If we have two vectors, 'A' and 'B', where vector1 is (vector1.x, vector1.y, vector1.z) and vector2 is (vector2.x, vector2.y, vector2.z) and we call the cross-product (vector.x, vector.y, vector.z) then the operations involved in calculating the cross-product are as follows:} vector.x := ((vector1.y * vector2.z) - (vector2.y * vector1.z)); vector.y := (((-vector1.x) * vector2.z) + (vector2.x * vector1.z)); vector.z := ((vector1.x * vector2.y) - (vector2.x * vector1.y)); {and we also denote the cross-product of A and B as AxB...} Lastly we may want to recalculate the x, y, and z components of the normal vector [vector.x, vector.y and vector.z] so that this vector has a length of 1 unit: this then becomes the 'unit vector' that has these particular x,y and z values. This is how we do that: temp := sqrt((sqr(vector.x)) + (sqr(vector.y)) + (sqr(vector.z))); vector.x := (vector.x / temp); vector.y := (vector.y / temp); vector.z := (vector.z / temp); Which gives us our final result. Note also that when we calculate the normal for a plane there are two possible results, namely normals that 'point' in totally opposite directions in 3D space. In using the 'cross-product' operation the direction of the resulting normal is determined by the order of the vectors that we hand to it. When looking down on the plane, we should specify the vectors in a counter-clockwise order in order to get the normal that runs 'in our direction'. If we specify vectors in a clockwise direction, the cross product will point in the opposite direction (ie 'away from us'). here's a Delphi test function type TDigitChars = set of Char; TPoint3D = record x: Real; y: Real; z: Real; end; TVector = record x: Real; y: Real; z: Real; end; TLine3D = record endPt1: Integer; endPt2: Integer; {temporary} lineName: String; end; TPtMatrix3D = array[0..MaxMPoints] of TPoint3D; TWFrameLines = array[0..MaxMWFLines] of TLine3D; end; procedure TMainForm.CalculateNormals; var x1: Real; y1: Real; z1: Real; x2: Real; y2: Real; z2: Real; x3: Real; y3: Real; z3: Real; vector1: TVector; vector2: TVector; vector: TVector; temp: Real; begin {temporary code that defines a square polygon face from four vertices along with the lines that connect these vertices...} wireFrame.InitPointsMatrix; wireFrame.InitLinesArray; wireFrame.ptMatrix3D[0].x := 1.0; wireFrame.ptMatrix3D[0].y := 1.0; wireFrame.ptMatrix3D[0].z := 1.0; wireFrame.ptMatrix3D[1].x := -1.0; wireFrame.ptMatrix3D[1].y := 1.0; wireFrame.ptMatrix3D[1].z := 1.0; wireFrame.wFrameLines[0].endPt1 := 0; wireFrame.wFrameLines[0].endPt2 := 1; wireFrame.wFrameLines[0].lineName := 'First Line'; wireFrame.ptMatrix3D[2].x := -1.0; wireFrame.ptMatrix3D[2].y := -1.0; wireFrame.ptMatrix3D[2].z := 1.0; wireFrame.wFrameLines[1].endPt1 := 1; wireFrame.wFrameLines[1].endPt2 := 2; wireFrame.wFrameLines[1].lineName := 'Second Line'; wireFrame.ptMatrix3D[3].x := 1.0; wireFrame.ptMatrix3D[3].y := -1.0; wireFrame.ptMatrix3D[3].z := 1.0; wireFrame.wFrameLines[2].endPt1 := 2; wireFrame.wFrameLines[2].endPt2 := 3; wireFrame.wFrameLines[2].lineName := 'Third Line'; wireFrame.wFrameLines[3].endPt1 := 3; wireFrame.wFrameLines[3].endPt2 := 0; wireFrame.wFrameLines[3].lineName := 'Fourth Line'; x1 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[0].endPt1].x; y1 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[0].endPt1].y; z1 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[0].endPt1].z; x2 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[0].endPt2].x; y2 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[0].endPt2].y; z2 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[0].endPt2].z; x3 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[1].endPt2].x; y3 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[1].endPt2].y; z3 := wireFrame.ptMatrix3D[wireFrame.wFrameLines[1].endPt2].z; {first use 'subtraction' to calculate two vectors THAT LIE IN THE PLANE OF THE POINTS WE ARE GIVEN...} vector1.x := (x1 - x2); vector1.y := (y1 - y2); vector1.z := (z1 - z2); vector2.x := (x3 - x2); vector2.y := (y3 - y2); vector2.z := (z3 - z2); {Now use the CROSS-PRODUCT operation to find the normal...} {If we have two vectors, 'A' and 'B', where vector1 is (vector1.x, vector1.y, vector1.z) and vector2 is (vector2.x, vector2.y, vector2.z) and we call the cross-product (vector.x, vector.y, vector.z) then the operations involved in calculating the cross-product are as follows:} vector.x := ((vector1.y * vector2.z) - (vector2.y * vector1.z)); vector.y := (((-vector1.x) * vector2.z) + (vector2.x * vector1.z)); vector.z := ((vector1.x * vector2.y) - (vector2.x * vector1.y)); {and we also denote the cross-product of A and B as AxB...} {now recalculate the x, y, and z components of the normal vector [vector.x, vector.y and vector.z] so that this vector has a length of 1 unit: this then becomes the 'unit vector' that has these particular x,y and z values...} temp := sqrt((sqr(vector.x)) + (sqr(vector.y)) + (sqr(vector.z))); vector.x := (vector.x / temp); vector.y := (vector.y / temp); vector.z := (vector.z / temp); end; ********************************************************************************** C++ function to return a normal for a plane: void TPoly::GetNormal(TVector *Vec) { Vec->X=0; Vec->Y=1; Vec->Z=0; if (NumOfVectors<3) return; TVector *A=Vectors[0]; TVector *B=Vectors[1]; TVector *C=Vectors[2]; double xu=B->X-A->X; double yu=B->Y-A->Y; double zu=B->Z-A->Z; double xv=C->X-A->X; double yv=C->Y-A->Y; double zv=C->Z-A->Z; double xsn=(yu*zv)-(zu*yv); double ysn=(zu*xv)-(xu*zv); double zsn=(xu*yv)-(yu*xv); double v1=sqrt((xsn*xsn)+(ysn*ysn)+(zsn*zsn)); if (v1!=0) v1=1/v1; Vec->X=-v1*xsn; Vec->Y=-v1*ysn; Vec->Z=-v1*zsn; }