Examples Delphi

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;
}