Autor Beitrag
Symbroson
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 09:09 
Hallo EE,

Ich bin gerade dabei eine Vektor- bzw. Matrix-Klasse für den Info-Unterricht zu schreiben. Leider gibt es ja kein Operator-Overloading bei Delphi7 Klassen, also behelfe ich mich mit add,mul,scalar,cross usw. Methoden, die als Argument einen anderen Vektor erhalten.

Wenn man allerdings einen Vektor nur temporär für eine Rechenoperation braucht müsste man sich ja erst ein Vektor-Objekt erstellen, und kann es später nicht wieder freigeben, weil man ja nicht ständig neue variablen dafür erstellen will.
Also habe ich die Methoden überladen um anstatt dem Vektor die einzelnen x,y,z-Werte zu erwarten.

Jetzt stellt sich mir die Frage, ob man evtl irgendwie erreichen kann, dass das Objekt später wieder freigegeben wird, wenn es keiner Variable zugewiesen wurde. oder irgendwie so. :nixweiss:

Beispiel:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
var v1 : TVec3;
 
begin
    v1 := TVec3.Create(1,2,3);
    v1.add(4,5,6);        //okay
    //v1.add(TVec3(7,8,9)); //memory-Leak
    //edit:
    v1.add(TVec3.Create(2,3,4)); //memory-Leak
    v1.free;
end;


Der Anfang der Vektor-Klasse sieht in etwa so aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
unit MatrixMath;

interface 

uses
    FastMM4;

type
    PVec3 = ^TVec3;
    TVec3 = class
        x, y, z: double;
                                                 
        constructor Create; overload;               
        constructor Create(v: TVec3); overload;
        constructor Create(v: double); overload
        constructor Create(x, y, z: double); overload;

        function copy: TVec3;
        
        function add(v: TVec3): TVec3; overload
        function add(v: double): TVec3; overload;
        function add(x, y, z: double): TVec3; overload;

        {...}


Das FastMM4 nutze ich nur für die Memory-Leak-Erkennung.


Grüße,
Symbroson

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)


Zuletzt bearbeitet von Symbroson am Fr 27.04.18 20:14, insgesamt 2-mal bearbeitet
drstar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 78
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 23.04.18 10:21 
constructor mit overload halte ich für keine gute Idee. Um die Freigabe des Objekts kümmert sich destroy, deshalb kennzeichnet man auch beide mit inherited, um den ursprünglichen Konstruktor bzw. Destruktor aufzurufen. Um die Speicherverwaltung kümmert sich die Klasse dann allein, genau das ist ja der Sinn dahinter.
Um dann die Klasse (nicht das Objekt) wieder freizugeben, ruft man dann destroy auf, auch hier dann mit inherited, um nicht nur den ursprünglichen Destruktor aufzurufen, sondern auch alles "Selbstgemachte" mit zu entsorgen. Wichtig noch die Reihenfolge: Beim Konstruktor als erstes den Ursprungs-Konstruktor aufrufen und dann den eigenen abzuarbeiten, beim Destruktor erst alles Selbstgemachte beseitigen und dann den Ursprungs-Destruktor aufrufen. Dann sollte alles eigentlich kein Problem mit der Speicherverwaltung sein.

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 11:06 
Ok, also ich muss mir das nachmal anschauen wie das zu benutzen ist, sodass das so funktioniert wie ich mir das vorstelle ^^

Danke schonmal für deine Antwort :)

Kannst du mir vllt mein kleines Beispiel so erweitern, dass sich die Klasse (irgendwann, möglichst bald, vllt nach Funktions-/Prozedurende) selbst zerstört?
Sonst bräuchte ich ja irgendwie nen Garbage-Collector...

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Holgerx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 44
Erhaltene Danke: 16

Win95 - Win8.1 / MSServer2000 - MSServer2012
Delphi 6pro / XE4
BeitragVerfasst: Mo 23.04.18 11:12 
Hmm..

Ich glaube, dass was Du haben willst geht mit 'normalen' Objecten nicht.

Hier werfe ich mal 'Interface' in den Raum, da diese eine Referenzzählung haben und freigegeben werden, wenn sie nicht mehr referenziert werden.

Für diesen Beitrag haben gedankt: Symbroson
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 23.04.18 17:12 
Guten Tag Symbroson,

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var v1 : TVec3;
 
begin
    v1 := TVec3.Create(1,2,3);
    v1.add(4,5,6);        //okay
    v1.add(TVec3(7,8,9)); //memory-Leak
    v1.free;
end;

Das kann garnicht funktionieren, da du an jener Stelle den Klammerausdruck direkt castest. In C++ würdest du das auch nicht machen: (TVec3)(123).
Wenn, dann benötigst du eine Methode mit dem Rückgabewert TVec3.
Und ich hab's dir schonmal gesagt, verwende einen try-finally Block, wenn du Objekte freigeben musst!

Damit solltest du weiter kommen:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
unit MatrixMath;

interface

type
  IVec3 = interface
  //['{Eine GUID}']
    function GetX: Double;
    function GetY: Double;
    function GetZ: Double;
    procedure SetX(Value: Double);
    procedure SetY(Value: Double);
    procedure SetZ(Value: Double);
    property X: Double read GetX write SetX;
    property Y: Double read GetY write SetY;
    property Z: Double read GetZ write SetZ;
  end;

  TVec3 = class(TInterfacedObject, IVec3)
  private
    FX, FY, FZ: double;
    function GetX: Double;
    function GetY: Double;
    function GetZ: Double;
    procedure SetX(Value: Double);
    procedure SetY(Value: Double);
    procedure SetZ(Value: Double);
  public
    constructor Create; overload;
    constructor Create(V: IVec3); overload;
    constructor Create(X, Y, Z: double); overload;
    function Add(V: IVec3): TVec3; overload;
    function Add(X, Y, Z: Double): TVec3; overload;
    property X: Double read GetX write SetX;
    property Y: Double read GetY write SetY;
    property Z: Double read GetZ write SetZ;
  end;

function Vec3(X, Y, Z: Double): IVec3;

implementation

function TVec3.GetX;
begin
  result := FX;
end;

function TVec3.GetY;
begin
  result := FY;
end;

function TVec3.GetZ;
begin
  result := FZ;
end;

procedure TVec3.SetX(Value: Double);
begin
  FX := Value;
end;

procedure TVec3.SetY(Value: Double);
begin
  FY := Value;
end;

procedure TVec3.SetZ(Value: Double);
begin
  FZ := Value;
end;

constructor TVec3.Create;
begin
  inherited;
  FX := 0;
  FY := 0;
  FZ := 0;
end;

constructor TVec3.Create(V: IVec3);
begin
  inherited Create;
  FX := V.X;
  FY := V.Y;
  FZ := V.Z;
end;

constructor TVec3.Create(X, Y, Z: Double);
begin
  inherited Create;
  FX := X;
  FY := Y;
  FZ := Z;
end;

function TVec3.Add(V: IVec3): TVec3;
begin
  FX := FX + V.X;
  FY := FY + V.Y;
  FZ := FZ + V.Z;
  result := self;
end;

function TVec3.Add(X, Y, Z: Double): TVec3;
begin
  FX := FX + X;
  FY := FY + Y;
  FZ := FZ + Z;
  result := self;
end;

function Vec3(X, Y, Z: Double): IVec3;
var
  v: TVec3;
begin
  v := TVec3.Create;
  v.X := X;
  v.Y := Y;
  v.Z := Z;
  result := v;
end;

end.

Beispiel:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  v: TVec3;
begin
  v := TVec3.Create(123);
  try
    v.Add(Vec3(456));
    v.Add(789);
    ShowMessageFmt('%f | %f | %f', [v.X, v.Y, v.Z]);
  finally
    v.Free;
  end;
end;

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 17:36 
Wow - vielen Dank! - das hilft mir sehr weiter. :zustimm: ich glaube, ich hätte sehr lange gebraucht, um das so hinzubekommen!

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var v1 : TVec3;
 
begin
    v1 := TVec3.Create(1,2,3);
    v1.add(4,5,6);        //okay
    v1.add(TVec3(7,8,9)); //memory-Leak
    v1.free;
end;

Das kann garnicht funktionieren, da du an jener Stelle den Klammerausdruck direkt castest. In C++ würdest du das auch nicht machen: (TVec3)(123).
Entschuldigung, es hätte heißen müssen  v1.add(TVec3.Create(2,3,4));

Zitat:
Und ich hab's dir schonmal gesagt, verwende einen try-finally Block, wenn du Objekte freigeben musst!
ja 'tschuldigung - ich hätte es später noch hinzugefügt ;)


noch eine Frage: Was genau bewirkt das property? Machen die Get bzw Set funktionen es nicht komplizierter? Oder wofür sind die sonst da?
[Edit:] ach, die sind bestimmt inherited-spezifisch. Das macht Sinn ^^

LG

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 23.04.18 18:10 
Property steht für eine Eigenschaft. Im Gegensatz zu den Feldern, kann man den Wert der Eigenschaft auch nur-lesend (read) machen, im Sinne einer Methode mit einem Rückgabewert. Beim Setzen der Eigenschaft kann man auch etwas Anderes ausführen, wie z.B. das Neuzeichnen eines Steuerelementes einleiten.
Verwende daher Eigenschaften anstelle von Variablen, sofern du es veröffentlichen möchtest.

Das Interface erlaubt keine Felder/Variablen, daher mussten die Getter- und Settermethoden zu jenen Eigenschaften zugewiesen werden. Und nachdem die Klasse mit dem Interface erweitert wurde, mussten jene Methoden und Eigenschaften mit aufgenommen und definiert werden.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 18:20 
Achso dann ist das also soetwas wie bei Canvas.Pixels oder Scanline :idea:

Interessantes Konzept - zumindest flaggt jetzt MM4 nicht mehr :zustimm:

Es sieht auch so aus, als müsste ich für TVec3 und IVec3 jeweils weitere overloads erstellen...

[Edit:] Könnte Ich nicht anstatt der Vec3-class nur das Vec3-interface nehmen und dort alle Methoden hineinpacken? Das macht doch im Endeffekt keinen Unterschied, oder?

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 23.04.18 19:11 
Richtig, .Pixels[] und .Scanline[] sind auch (indizierte) Eigenschaften.
Die Klasse kann ruhig einen TVec3-Parameter haben, sofern du diesem ein "fertig-erzeugtes" Objekt übergibst, welches du nachher auch freigibst:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
var
  v1, v2: TVec3;
begin
  v1 := TVec3.Create(123);
  try
    v2 := TVec3.Create(v1);
    try
      // ...
    finally
      v2.Free;
    end;
  finally
    v1.Free;
  end;
end;

Ja, du könntest es auch so einrichten, dass alles über das Interface läuft. Dazu erweiterst du das Interface mit jenen Methoden, die du auch schon in der Klasse hast und gut ist. Die Klasse muss aber weiterhin bestehen bleiben, ansonsten funktioniert es nicht, denn darin ist die Logik enthalten.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)

Für diesen Beitrag haben gedankt: Symbroson
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Mo 23.04.18 20:38 
Geht das irgendwie, dass ich eine allgemeingültige Free bzw Release-funktion sowohl für IVec3 als auch TVec3 habe? Sonst muss ich beide Objekte jeweils unterschiedlich freigeben (mit := nil bzw Free) - und das kann dann doch eher verwirrend sein...
Alles, was ich bisher versucht habe, hat nicht funktioniert. Ich habe einiges mit try..catch blöcken und _Release <= 0 bzw Free oder FreeAndNil() versucht, hat aber alles irgendwie nicht so ganz geklappt - meistens kam eine EInvalidPointer Exception oder ähnliches.

es sollte am Ende in etwa so aussehen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
var v1 : TVec3;
    v2 : IVec3;
 
begin
    v1 := TVec3.Create(2,3,5);
    v2 := Vec3(1,4,6);
    
    v2.x := v2.x + 9.4;
    writeln('1: (' + floattostr(v1.x) + '|' + floattostr(v1.y) + '|' + floattostr(v1.z) + ')');
    
    v1.add(v2.copy.mul(4));     
    writeln('2: (' + floattostr(v1.x) + '|' + floattostr(v1.y) + '|' + floattostr(v1.z) + ')');
    
    v1.Release;
    v2.Release;
    
    readln;
end.


Danke vielmals

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 23.04.18 22:25 
Das Klassen-Objekt (v1) solltest du mit .Free() freigeben, und beim Interface-Objekt (v2) musst du garnichts tun. Sobald dieses seine "Arbeit" verrichtet hat, wird es automatisch freigegeben (dank der Klasse TInterfacedObject).

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
var
  v1: TVec3;
  v2: IVec3;

begin
  v1 := TVec3.Create(235);
  try
    v2 := Vec3(146);
    v2.X := v2.X + 9.4;
    Writeln(Format('%f | %f | %f', [v1.X, v1.Y, v1.Z]));
    v1.Add(v2);
    Writeln(Format('%f | %f | %f', [v1.X, v1.Y, v1.Z]));
  finally
    v1.Free;
  end;
  Readln;
end.

Was .Copy.Mul() nun ist, das weiss ich nicht. Sollte es immer noch einen Fehler geben, dann an dieser Stelle.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Di 24.04.18 05:11 
copy gibt eine Kopie des Objektes/Interfaces zurück und mul() multipliziert die Werte der Vektoren bzw streckt/skaliert ihn mit einem Wert. Anders als scalar() für Skalarprodukte bzw cross() für Kreuzprodukte. Später werden mit mul() auch Multiplikationen mit einer 3×3 Matrix möglich.

Ich versuche eigentlich so etwas wie die glm Bibliothek von c++ zu schaffen - nur will ich alles auch mal selber gemacht haben anstatt immer nur fertige Bibliotheken zu nutzen - und wo das nun in Info schonmal dran is bietet sich ja dazu die Gelegenheit.

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 27.04.18 11:57 
Die Copy() Methode hat keinen Nutzen, denn diese hat mit dem jeweiligen Objekt identische Werte: v1.Copy.X = v1.X; Wozu soll das gut sein? :gruebel:
Für die 3x3 Darstellung, siehe: Array-Eigenschaft.
Die glm-Bibliothek ist ganz schön umfangreich. Ich würde mich da auf das Nötigste beschränken:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
unit MatrixMath;

interface

type
  TVec3 = class;

  IVec3 = interface
  //['{Eine GUID}']
    function Add(V: IVec3): TVec3; overload;              // Vektor-Addition
    function Add(X, Y, Z: Double): TVec3; overload;       // Vektor-Addition
    function Angle(V: IVec3): Double; overload;           // (Innen-)Winkel
    function Angle(X, Y, Z: Double): Double; overload;    // (Innen-)Winkel
    function Cross(V: IVec3): TVec3; overload;            // Kreuzprodukt
    function Cross(X, Y, Z: Double): TVec3; overload;     // Kreuzprodukt
    function IsPerpendicular: Boolean;                    // Ist-Orthogonal
    function Magnitude: Double;                           // Vektor-Betrag/Länge
    function Multiply(Scalar: Double): TVec3;             // Vektor-Multiplikation
    function Scalar(V: IVec3): Double; overload;          // Skalarprodukt
    function Scalar(X, Y, Z: Double): Double; overload;   // Skalarprodukt
    function Subtract(V: IVec3): TVec3; overload;         // Vektor-Subtraktion
    function Subtract(X, Y, Z: Double): TVec3; overload;  // Vektor-Subtraktion
    function ToCoordinate: string;
    function GetX: Double;
    function GetY: Double;
    function GetZ: Double;
    procedure SetX(Value: Double);
    procedure SetY(Value: Double);
    procedure SetZ(Value: Double);
    property X: Double read GetX write SetX;
    property Y: Double read GetY write SetY;
    property Z: Double read GetZ write SetZ;
  end;

  TVec3 = class(TInterfacedObject, IVec3)
  private
    FX, FY, FZ: Double;
    function GetX: Double;
    function GetY: Double;
    function GetZ: Double;
    procedure SetX(Value: Double);
    procedure SetY(Value: Double);
    procedure SetZ(Value: Double);
  public
    constructor Create; overload;
    constructor Create(V: IVec3); overload;
    constructor Create(X, Y, Z: Double); overload;
    function Add(V: IVec3): TVec3; overload;
    function Add(X, Y, Z: Double): TVec3; overload;
    function Angle(V: IVec3): Double; overload;
    function Angle(X, Y, Z: Double): Double; overload;
    function Cross(V: IVec3): TVec3; overload;
    function Cross(X, Y, Z: Double): TVec3; overload;
    function IsPerpendicular: Boolean;
    function Magnitude: Double;
    function Multiply(Scalar: Double): TVec3;
    function Scalar(V: IVec3): Double; overload;
    function Scalar(X, Y, Z: Double): Double; overload;
    function Subtract(V: IVec3): TVec3; overload;
    function Subtract(X, Y, Z: Double): TVec3; overload;
    function ToCoordinate: string;
    property X: Double read GetX write SetX;
    property Y: Double read GetY write SetY;
    property Z: Double read GetZ write SetZ;
  end;

function Vec3(X, Y, Z: Double): IVec3; overload;
function Vec3(V: TVec3): IVec3; overload;
function ACos(Radiant: Double): Double;
function ASin(Radiant: Double): Double;
function Cos(Degree: Double): Double; overload;
function Sin(Degree: Double): Double; overload;

implementation

uses Math, SysUtils;

const
  DEG = Pi / 180.0;
  RAD = 180.0 / Pi;

function TVec3.GetX;
begin
  result := FX;
end;

function TVec3.GetY;
begin
  result := FY;
end;

function TVec3.GetZ;
begin
  result := FZ;
end;

procedure TVec3.SetX(Value: Double);
begin
  FX := Value;
end;

procedure TVec3.SetY(Value: Double);
begin
  FY := Value;
end;

procedure TVec3.SetZ(Value: Double);
begin
  FZ := Value;
end;

constructor TVec3.Create;
begin
  inherited;
  FX := 0;
  FY := 0;
  FZ := 0;
end;

constructor TVec3.Create(V: IVec3);
begin
  inherited Create;
  if Assigned(V) then
  begin
    FX := V.X;
    FY := V.Y;
    FZ := V.Z;
  end;
end;

constructor TVec3.Create(X, Y, Z: Double);
begin
  inherited Create;
  FX := X;
  FY := Y;
  FZ := Z;
end;

function TVec3.Add(V: IVec3): TVec3;
begin
  result := nil;
  if Assigned(V) then
    result := Add(V.X, V.Y, V.Z);
end;

function TVec3.Add(X, Y, Z: Double): TVec3;
begin
  FX := FX + X;
  FY := FY + Y;
  FZ := FZ + Z;
  result := self;
end;

function TVec3.Angle(V: IVec3): Double;
var
  dRes: Double;
begin
  dRes := 0.0;
  if Assigned(V) then
    dRes := ACos(Scalar(V) / (Magnitude * V.Magnitude));
  result := dRes;
end;

function TVec3.Angle(X, Y, Z: Double): Double;
var
  v: IVec3;
  dRes: Double;
begin
  v := Vec3(X, Y, Z);
  dRes := ACos(Scalar(V) / (Magnitude * v.Magnitude));
  result := dRes;
end;

function TVec3.Cross(V: IVec3): TVec3;
begin
  result := nil;
  if Assigned(V) then
    result := Cross(V.X, V.Y, V.Z);
end;

function TVec3.Cross(X, Y, Z: Double): TVec3;
var
  tempX, tempY: Double;
begin
  tempX := FX;
  tempY := FY;
  FX := tempY * Z - FZ * Y;
  FY := FZ * X - tempX * Z;
  FZ := tempX * Y - tempY * X;
  result := self;
end;

function TVec3.IsPerpendicular: Boolean;
var
  bRes: Boolean;
begin
  bRes := Scalar(FX, FY, FZ) = 0.0;
  result := bRes;
end;

function TVec3.Magnitude: Double;
var
  dRes: Double;
begin
  dRes := Sqrt(FX * FX + FY * FY + FZ * FZ);
  result := dRes;
end;

function TVec3.Multiply(Scalar: Double): TVec3;
begin
  FX := FX * Scalar;
  FY := FY * Scalar;
  FZ := FZ * Scalar;
  result := self;
end;

function TVec3.Scalar(V: IVec3): Double;
begin
  result := 0.0;
  if Assigned(V) then
    result := Scalar(V.X, V.Y, V.Z);
end;

function TVec3.Scalar(X, Y, Z: Double): Double;
var
  dRes: Double;
begin
  dRes := FX * X + FY * Y + FZ * Z;
  result := dRes;
end;

function TVec3.Subtract(V: IVec3): TVec3;
begin
  result := nil;
  if Assigned(V) then
    result := Subtract(V.X, V.Y, V.Z);
end;

function TVec3.Subtract(X, Y, Z: Double): TVec3;
begin
  FX := FX - X;
  FY := FY - Y;
  FZ := FZ - Z;
  result := self;
end;

function TVec3.ToCoordinate: string;
begin
  result := Format('(%f|%f|%f)', [FX, FY, FZ])
end;

function Vec3(X, Y, Z: Double): IVec3;
var
  v: TVec3;
begin
  v := TVec3.Create;
  v.X := X;
  v.Y := Y;
  v.Z := Z;
  result := v;
end;

function Vec3(V: TVec3): IVec3;
begin
  result := IVec3(V);
end;

function ACos(Radiant: Double): Double;
begin
  result := Math.ArcCos(Radiant) * RAD;
end;

function ASin(Radiant: Double): Double;
begin
  result := Math.ArcSin(Radiant) * RAD;
end;

function Cos(Degree: Double): Double;
begin
  result := System.Cos(Degree * DEG);
end;

function Sin(Degree: Double): Double;
begin
  result := System.Sin(Degree * DEG);
end;

end.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)


Zuletzt bearbeitet von Frühlingsrolle am Sa 28.04.18 12:54, insgesamt 1-mal bearbeitet
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Fr 27.04.18 13:56 
Das wäre wirklich nicht nötig gewesen - die Klasse selbst hab ich schon selber gebaut.
Ich habe ja auch schon selber mit glm in C++ OpenGL gearbeitet - ich bin mir dem Umpfang natürlich bewusst
Es kommen aber trotzdem noch Matritzen (3×3 und 4×4 für den Raum) dazu, sowie zumindest 2er Vektoren. Fang jetzt aber bloß nicht an das auch zu machen :lol:

Das copy hab ich hinzugefügt für den Fall, dass ich mit dem Wert einer Variable rechnen, aber nicht die Variable selbst verändern möchte - zB. für die ModelView Matrix für alle Vertices - die sollen ja nicht die Position des Vertex verändern.

Ich überlege auch noch, ob ich nicht vielleicht doch eher class functions/procedures verwende wie auch glm - Ich weiß ja nicht wie Delphi das dann handlet - ob je Funktion pro Kind ein extra Funktionszeiger angelegt werden muss, oder eben nicht. Bei Klassenfunktionen sollte das ja in jedem Fall nicht so sein.

Danke nochmal für deine Hilfe ^^

[Edit] Frage: warum legst du immer eine extra Variable an, anstatt direkt mit result zu arbeiten? Es gibt ja schon extra result... warum es nicht ausnutzen? (zumindest mache ich das meistens)

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)


Zuletzt bearbeitet von Symbroson am Sa 28.04.18 09:03, insgesamt 1-mal bearbeitet
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 27.04.18 19:37 
Nö, bin unmotiviert weiter zu machen.
Ich fürchte, wenn du die Klassenmethoden so einführst, wirst du das Eingangsproblem mit dem Speicherleck wieder haben.
Leichter wäre es, nur das Interface zu nutzen und sämtliche Methoden mit dem Rückgabewert TVec3, auf IVec3 umzustellen. Sinngemäß:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
{ NEU }
type
  IVec3 = interface
    function Add(V: IVec3): IVec3overload;              
    function Add(X, Y, Z: Double): IVec3overload;  
    function ToCoordinate: string;    
  end;

  TVec3 = class(TInterfacedObject, IVec3)
    function Add(V: IVec3): IVec3overload;
    function Add(X, Y, Z: Double): IVec3overload;
    function ToCoordinate: string;
  end;
// ...
implementation

function TVec3.Add(V: IVec3): IVec3;
begin
  result := nil;
  if Assigned(V) then
    result := Add(V.X, V.Y, V.Z);
end;

function TVec3.Add(X, Y, Z: Double): IVec3;
begin
  FX := FX + X;
  FY := FY + Y;
  FZ := FZ + Z;
  result := IVec3(self);
end;

end.


{ Beispiel }
begin
  ShowMessage(Vec3(135).Add(456).ToCoordinate); 
end;

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Fr 27.04.18 19:40 
Ja, das ist mir auch aufgefallen. Dafür hab ich jetzt eine Release fnkt die für beide in den meisten Fällen funktioniert.

Ich hab mal meine aktuelle und fast finale Vec3 Klasse in den Anhang gepackt. Das mit dem IVec3 als Rückgabe versuche ich mal - das würde aber die Nutzung von IVec3 erzwingen, da TVec3 damit nicht kompatibel ist. (umgekehrt aber irgendwie schon...)

Bsp:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
program MatrixTest;
          
{$APPTYPE CONSOLE}

uses
    //FastMM4,
    SysUtils,
    //System, 
    //Dialogs, 
    UVec3 in 'glm/UVec3.pas';

var v1 : TVec3;
    v2 : IVec3;
begin
    try
        v1 := TVec3.Create(2,3,5);
        v2 := Vec3(1,4,6);
    
        v2.x := v2.x + 9.4;
        writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));
    
        v2 := v1.add(v2.copy.mul(4)).copy;     
        writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));

    finally
        TVec3.Release(v1);
        TVec3.Release(v2); //nicht benötigt, wenn in extra prozedur verwendet
    end
    
    readln;
end.
Einloggen, um Attachments anzusehen!
_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 27.04.18 20:36 
user profile iconSymbroson hat folgendes geschrieben:
Ja, das ist mir auch aufgefallen. Dafür hab ich jetzt eine Release fnkt die für beide in den meisten Fällen funktioniert.
user profile iconFrühlingsrolle hat folgendes geschrieben:
Das Klassen-Objekt (v1) solltest du mit .Free() freigeben, und beim Interface-Objekt (v2) musst du garnichts tun. Sobald dieses seine "Arbeit" verrichtet hat, wird es automatisch freigegeben (dank der Klasse TInterfacedObject).


ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
v1 := TVec3.Create(2,3,5); // hier wäre v1 initialisiert
try
  //v1 := TVec3.Create(2,3,5);  // hier ist v1 nicht initialisiert
finally
  v1.Free; // v2 (Interface) ist bis dahin schon frei
end;

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Fr 27.04.18 20:44 
Aus irgendeinem Grund zeigt FastMM4 aber auch beim interface eine Warnung an, wenn ich es in dem Konsolenanwendungs-begin end. definiere. Deswegen das Release

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2165
Erhaltene Danke: 399

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 27.04.18 20:55 
Darf garnicht sein. Kommentier' die Zeile mit deiner Copy() Methode aus und schau nochmal.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Symbroson Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 381
Erhaltene Danke: 67

Raspbian, Ubuntu, Win10
C, C++, Python, JavaScript, Lazarus, Delphi7, Casio Basic
BeitragVerfasst: Fr 27.04.18 21:00 
Das hier erzeugt trotzdem eine Warnung:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
program MatrixTest;
          
{$APPTYPE CONSOLE}

uses FastMM4, SysUtils, {System,{} {Dialogs,{} UVec3 in 'glm/UVec3.pas';

var v1 : TVec3;
    v2 : IVec3;
begin        
    v1 := TVec3.Create(2,3,5);
    v2 := Vec3(1,4,6);
    
    try
        v2.x := v2.x + 9.4;
        writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));
    
        //v1.add(v2.copy.mul(4).add(Vec3(5,3,-2))).copy;     
        writeln(Format('2: |(%f|%f|%f)| = %f', [v1.x, v1.y, v1.z, v1.abs]));

    finally
        TVec3.Release(v1);
        //TVec3.Release(v2); // not required when used in extra procedure
    end
    
    readln;
end.


Auch ganz ohne Befehle im try-Block kommt die Warnung.
Meine Theorie ist, dass entweder FastMM4 es nicht mitbekommt, also zu früh auswertet, oder dass Delphi zu spät merkt, dass v2 keine Referenzen mehr hat und nicht mehr freigibt.

Meistens verwendet man das ja eh nicht in einer Konsolenanwendung, sondern in ner Form oder zumindest in einer extra Funktion - dann hat sich das eh erledigt.
Im Zweifelsfall legt man sich einfach eine main-Prozedur an, in der man das ganze Zeug aus begin {...} end. hineinpackt.

_________________
most good programmers do programming not because they expect to get paid or get adulation by the public, but because it's fun to program. (Linus Torvalds)