I have a very simple class definition for 3D Vectors, TVector3D, and a few methods used to implement the TVector3D.Normalise function. If I pass the Normalise function a vector that is already normalised, I want it to return the vector I passed it. Here I have used Result := Self but I am having some crazy returns.
The console application:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TVector3D = Class
public
x : Single;
y : Single;
z : Single;
constructor Create(x : Single;
y : Single;
z : Single);
function GetMagnitude() : Single;
function IsUnitVector() : Boolean;
function Normalise() : TVector3D;
end;
constructor TVector3D.Create(x : Single;
y : Single;
z : Single);
begin
Self.x := x;
Self.y := y;
Self.z := z;
end;
function TVector3D.GetMagnitude;
begin
Result := Sqrt(Sqr(Self.x) + Sqr(Self.y) + Sqr(Self.z));
end;
function TVector3D.IsUnitVector;
begin
if Self.GetMagnitude = 1 then
Result := True
else
Result := False;
end;
function TVector3D.Normalise;
var
x : Single;
y : Single;
z : Single;
MagnitudeFactor : Single;
begin
if IsUnitVector then
Result := Self
else
MagnitudeFactor := 1/(Self.GetMagnitude);
x := Self.x*MagnitudeFactor;
y := Self.y*MagnitudeFactor;
z := Self.z*MagnitudeFactor;
Result := TVector3D.Create(x,
y,
z);
end;
procedure TestNormalise;
var
nonUnitVector : TVector3D;
unitVector : TVector3D;
nUVNormed : TVector3D;
uVNormed : TVector3D;
begin
//Setup Vectors for Test
nonUnitVector := TVector3D.Create(1,
1,
1);
unitVector := TVector3D.Create(1,
0,
0);
//Normalise Vectors & Free Memory
nUVNormed := nonUnitVector.Normalise;
nonUnitVector.Free;
uVNormed := unitVector.Normalise;
unitVector.Free;
//Print Output & Free Memory
WriteLn('nUVNormed = (' + FloatToStr(nUVNormed.x) + ', ' + FloatToStr(nUVNormed.y) + ', ' + FloatToStr(nUVNormed.z) + ')');
nUVNormed.Free;
WriteLn('uVNormed = (' + FloatToStr(uVNormed.x) + ', ' + FloatToStr(uVNormed.y) + ', ' + FloatToStr(uVNormed.z) + ')');
uVNormed.Free;
end;
begin
try
TestNormalise;
Sleep(10000);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Normalise works fine for non-unit vecors, i.e. IsUnitVector returns false. But for unit vectors, such as (1,0,0), instead of returning itself I get a result with very low nonzero numbers wherever there was a nonzero previously, such as (8.47122...E-38,0,0).
If I run this through the debugger with a breakpoint on the line Result := Self set to evaluate Self, Self is (1,0,0) yet result becomes (Very Low Number,0,0). Where Very Low Number changes each time I run the programme but always seems to be around E-38/E-39.
I do not understand why this happens. Why does it happen and how is it best to alter my Normalise function to avoid it.
Your current
TVector3D.Normaliseimplementation has some issues:begin-endblock after theelse,Self, but always a new instance,IsUnitVectorreturnsTrue, then the assignment ofMagnitudeFactorwill be skipped, and it will be a random value (currently present at that memory’s address), which explains why you get rubish. You are also warned by the compiler for this: VariableMagnitudeFactormight not have been initialized.Instead, I would rewrite the routine as follows: