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.
TVector3D.Normalise
implementation requiresTVector3D
type to be a reference counted interface instead of a class. – EatsnUVNormed
andnonUnitVector
actually refer to the same object. And likewise for the other pair. So you have access after free, and double free. And those problems will eat you up. So I urge you to start using records ASAP and so avoid these pitfalls. I've supplied you some code to get going. This is code from this program: orcina.com/SoftwareProducts/OrcaFlex – Spanking