Vector-Class

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Vector-Class

Beitrag von remi_meier »

oder so...

# Also was ist das?
Ein Interface zum Rechnen mit Vektoren.
# Ist das schwer?
Nö. Beispiel am Code angehängt.
# Was ist ein Interface?
Lies die PB-Hilfe.
# Vorteile?
Objektorientierter Ansatz, nicht zu langsam, übersichtlich.
# Ne genauere Beschreibung wär schon gut...
Sry, so viel Zeit nehm ich mir nicht.
# Brauch ich nicht.
Mir egal.

Code: Alles auswählen

Structure cOVECTORVT
	; Functions
	fSetXYZ.l
	fSetV.l
	fGetX.l
	fGetY.l
	fGetZ.l
	fGetLength.l
	fGetLengthSqr.l
	fSetX.l
	fSetY.l
	fSetZ.l
	fAddV.l
	fAddXYZ.l
	fSubV.l
	fSubXYZ.l
	fGetDotPV.l
	fCrossPV.l
	fCpy2OV.l
	fMul.l
	fDiv.l
	fNormalize.l
	fGetDataPtr.l
	fGetDataFromMem.l
	fRotateAroundX.l
	fRotateAroundY.l
	fRotateAroundZ.l
	fRotateAroundV.l
EndStructure

Structure cOVECTOR
	VTable.l
	
	; Data
	x.f
	y.f
	z.f
EndStructure

Interface iOVECTOR
	SetXYZ(x.f, y.f, z.f)
	SetV(*v.cOVECTOR)
	GetX.f()
	GetY.f()
	GetZ.f()
	GetLength.f()
	GetLengthSqr.f()
	SetX(x.f)
	SetY(y.f)
	SetZ(z.f)
	AddV(*v.cOVECTOR)
	AddXYZ(x.f, y.f, z.f)
	SubV(*v.cOVECTOR)
	SubXYZ(x.f, y.f, z.f)
	GetDotPV.f(*v.cOVECTOR)
	CrossPV(*v.cOVECTOR)
	Cpy2OV.l()
	Mul(Factor.f)
	Div(Divisor.f)
	Normalize()
	GetDataPtr.l()
	GetDataFromMem(*mem)
	RotateAroundX(a.f)
	RotateAroundY(a.f)
	RotateAroundZ(a.f)
	RotateAroundV(*v.cOVECTOR, a.f)
EndInterface



Procedure vect_SetXYZ(*this.cOVECTOR, x.f, y.f, z.f)
	*this\x = x
	*this\y = y
	*this\z = z
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_SetV(*this.cOVECTOR, *v.cOVECTOR)
	If *v
		*this\x = *v\x
		*this\y = *v\y
		*this\z = *v\z
	EndIf
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure.f vect_GetX(*this.cOVECTOR)
	ProcedureReturn *this\x
EndProcedure

Procedure.f vect_GetY(*this.cOVECTOR)
	ProcedureReturn *this\y
EndProcedure

Procedure.f vect_GetZ(*this.cOVECTOR)
	ProcedureReturn *this\z
EndProcedure

Procedure.f vect_GetLength(*this.cOVECTOR)
	ProcedureReturn Sqr(*this\x * *this\x + *this\y * *this\y + *this\z * *this\z)
EndProcedure

Procedure.f vect_GetLengthSqr(*this.cOVECTOR)
	ProcedureReturn *this\x * *this\x + *this\y * *this\y + *this\z * *this\z
EndProcedure

Procedure vect_SetX(*this.cOVECTOR, x.f)
	*this\x = x
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_SetY(*this.cOVECTOR, y.f)
	*this\y = y
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_SetZ(*this.cOVECTOR, z.f)
	*this\z = z
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_AddV(*this.cOVECTOR, *v.cOVECTOR)
	If *v
		*this\x + *v\x
		*this\y + *v\y
		*this\z + *v\z
	EndIf
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_AddXYZ(*this.cOVECTOR, x.f, y.f, z.f)
	*this\x + x
	*this\y + y
	*this\z + z
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_SubV(*this.cOVECTOR, *v.cOVECTOR)
	If *v
		*this\x - *v\x
		*this\y - *v\y
		*this\z - *v\z
	EndIf
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_SubXYZ(*this.cOVECTOR, x.f, y.f, z.f)
	*this\x - x
	*this\y - y
	*this\z - z
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure.f vect_GetDotPV(*this.cOVECTOR, *v.cOVECTOR)
	If *v
		ProcedureReturn *this\x * *v\x + *this\y * *v\y + *this\z * *v\z
	EndIf
	
	ProcedureReturn 0
EndProcedure

Procedure vect_CrossPV(*this.cOVECTOR, *v.cOVECTOR)
	Protected vx.f, vy.f, tx.f, ty.f, tz.f
	
	If *v
		tx = *this\x
		ty = *this\y
		tz = *this\z
		*this\x = ty * *v\z - tz * *v\y
		*this\y = tz * *v\x - tx * *v\z
		*this\z = tx * *v\y - ty * *v\x
	EndIf
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Declare new_ovector()
Procedure vect_Cpy2OV(*this.cOVECTOR)
	Protected *v.iOVECTOR
	
	*v.iOVECTOR = new_ovector()
	*v\SetV(*this)
	
	ProcedureReturn *v
EndProcedure

Procedure vect_Mul(*this.cOVECTOR, Factor.f)
	*this\x * Factor
	*this\y * Factor
	*this\z * Factor
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_Div(*this.cOVECTOR, Divisor.f)
	Protected f.f
	
	f = 1.0 / Divisor
	*this\x * f
	*this\y * f
	*this\z * f
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_Normalize(*this.cOVECTOR)
	Protected Len.f
	
	Len = 1.0 / vect_GetLength(*this)
	*this\x * Len
	*this\y * Len
	*this\z * Len
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure.l vect_GetDataPtr(*this.cOVECTOR)
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_GetDataFromMem(*this.cOVECTOR, *mem)
	CopyMemory(*mem, *this + OffsetOf(cOVECTOR\x), 3 * 4)
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_RotateAroundX(*this.cOVECTOR, a.f)
	Protected x.f, y.f, z.f
	
	x = *this\x
	y = *this\y
	z = *this\z
	; M * *this
	*this\x = x
	*this\y = Cos(a) * y - Sin(a) * z
	*this\z = Sin(a) * y + Cos(a) * z
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_RotateAroundY(*this.cOVECTOR, a.f)
	Protected x.f, y.f, z.f
	
	x = *this\x
	y = *this\y
	z = *this\z
	; M * *this
	*this\x = Cos(a) * x + Sin(a) * z
	*this\y = y
	*this\z = -Sin(a) * x + Cos(a) * z
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_RotateAroundZ(*this.cOVECTOR, a.f)
	Protected x.f, y.f, z.f
	
	x = *this\x
	y = *this\y
	z = *this\z
	; M * *this
	*this\x = Cos(a) * x - Sin(a) * y
	*this\y = Sin(a) * x + Cos(a) * y
	*this\z = z
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure

Procedure vect_RotateAroundV(*this.cOVECTOR, *v.cOVECTOR, a.f) ; *v has to be normalized
	Protected x.f, y.f, z.f, cosa.f, sina.f, ecosa.f
	
	cosa = Cos(a)
	sina = Sin(a)
	ecosa = 1.0 - cosa
	
	x = *this\x
	y = *this\y
	z = *this\z
	; M * *this
	*this\x = x * (cosa + *v\x * *v\x * ecosa) + y * (*v\x * *v\y * ecosa - *v\z * sina) + z * (*v\x * *v\z * ecosa + *v\y * sina)
	*this\y = x * (*v\y * *v\x * ecosa + *v\z * sina) + y * (cosa + *v\y * *v\y * ecosa) + z * (*v\y * *v\z * ecosa - *v\x * sina)
	*this\z = x * (*v\z * *v\x * ecosa - *v\y * sina) + y * (*v\z * *v\y * ecosa + *v\x * sina) + z * (cosa + *v\z * *v\z * ecosa)
	
	
	ProcedureReturn *this + OffsetOf(cOVECTOR\x)
EndProcedure




Procedure.l new_ovector()
	Protected *v.cOVECTOR
	Static *VTable.cOVECTORVT
	
	If *VTable = 0
		*VTable = AllocateMemory(SizeOf(cOVECTORVT))
		*VTable\fSetXYZ				= @vect_SetXYZ()
		*VTable\fSetV					= @vect_SetV()
		*VTable\fGetX					= @vect_GetX()
		*VTable\fGetY					= @vect_GetY()
		*VTable\fGetZ					= @vect_GetZ()
		*VTable\fGetLength		= @vect_GetLength()
		*VTable\fGetLengthSqr	= @vect_GetLengthSqr()
		*VTable\fSetX					= @vect_SetX()
		*VTable\fSetY					= @vect_SetY()
		*VTable\fSetZ					= @vect_SetZ()
		*VTable\fAddV					= @vect_AddV()
		*VTable\fAddXYZ				= @vect_AddXYZ()
		*VTable\fSubV					= @vect_SubV()
		*VTable\fSubXYZ				= @vect_SubXYZ()
		*VTable\fGetDotPV			= @vect_GetDotPV()
		*VTable\fCrossPV			= @vect_CrossPV()
		*VTable\fCpy2OV				= @vect_Cpy2OV()
		*VTable\fMul					= @vect_Mul()
		*VTable\fDiv					= @vect_Div()
		*VTable\fNormalize		= @vect_Normalize()
		*VTable\fGetDataPtr		= @vect_GetDataPtr()
		*VTable\fGetDataFromMem = @vect_GetDataFromMem()
		*VTable\fRotateAroundX	= @vect_RotateAroundX()
		*VTable\fRotateAroundY	= @vect_RotateAroundY()
		*VTable\fRotateAroundZ	= @vect_RotateAroundZ()
		*VTable\fRotateAroundV	= @vect_RotateAroundV()
	EndIf
	
	*v = AllocateMemory(SizeOf(cOVECTOR))
	*v\VTABLE = *VTable
		
	ProcedureReturn *v
EndProcedure



; DefType.iOVECTOR a, b
; 
; a = new_ovector()
; b = new_ovector()
; 
; a\SetXYZ(1, 2, 0)
; b\SetXYZ(2, 3, 0)
; a\SubV(b)
; 
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
; 
; a\Normalize()
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
; 
; Debug a\GetLength()
; Debug a\GetLengthSqr()

; NewList vec.iOVECTOR()
; AddElement(vec())
; vec() = new_ovector()
; vec()\SetXYZ(1, 2, 0)
; 
; AddElement(vec())
; vec() = new_ovector()
; vec()\SetXYZ(3, 21, 0)
; 
; Debug vec()\GetX()




; DefType.iOVECTOR a, b
; 
; a = new_ovector()
; b = new_ovector()
; 
; a\SetXYZ(0, 10, 0)
; b\SetXYZ(1, 0, 0) ; X-Achse ^^
; b\Normalize() ; wäre nicht nötig hier
; 
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
; 
; Debug "RotateX"
; a\RotateAroundX(3.1415926 / 2.0)
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
; 
; Debug "RotateY"
; a\RotateAroundY(3.1415926 / 2.0)
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
; 
; Debug "RotateZ"
; a\RotateAroundZ(3.1415926 / 2.0)
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
; 
; Debug "RotateV = RotateX"
; a\RotateAroundV(b, 3.1415926 / 2.0)
; Debug a\GetX()
; Debug a\GetY()
; Debug a\GetZ()
Entstand so nebenbei...

Wenns Ergänzungen gibt, kann sie jeder selbst schreiben und hier posten,
oder mir sagen, dann werd ich sie einbauen.

greetz
Remi

[EDIT] VTable nur noch 1x erstellt.
[EDIT2] Paar Funktionen hinzugefügt
Zuletzt geändert von remi_meier am 24.09.2005 21:34, insgesamt 2-mal geändert.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

Hey Remi!

Super Idee, das so zu machen.
Ich frag mich gerade, warum ich das in meiner Raytracing-Engine nicht auch so gelöst habe.

///Edit: Ah, ich weiß warum: Es verbraucht bei zu vielen Vektoren einfach zu viel Speicherplatz. /:->
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

:D
Ok, ich baus ev. um, dass die VTable nur einmal gebraucht wird (ist so
irgendwie schon unsinnig^^)
Benutzeravatar
Sylvia
verheiratet<br>1. PureGolf-Gewinner
Beiträge: 487
Registriert: 29.08.2004 09:42
Wohnort: Old Europe

Beitrag von Sylvia »

>>remi_meier: oder so...

Was ist denn das für eine Art, einen Thread zu beginnen ?
Um was geht es hier eigentlich ? :? :roll:
Basic Pur = PureBasic
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Sylvia hat geschrieben:>>remi_meier: oder so...
Was ist denn das für eine Art, einen Thread zu beginnen ?
Ne eigene <)

> Um was geht es hier eigentlich ?
Recht haste :roll: , hab mal einen kleinen Kommentar oben hinzugefügt.

@Nic:
VTable wir jetzt nur noch einmal erstellt -> SizeOf(cOVECTOR) = 16b
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Beitrag von DarkDragon »

Es ist wirklich schade, dass man Operatoren noch nicht damit bestimmen kann. :lol:

Dann könnte man folgendes machen:

Code: Alles auswählen

Vector3.CVector3F = Vector1.CVector3F+Vector2.CVector3F
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Hellhound66
Beiträge: 476
Registriert: 23.03.2005 23:19

Beitrag von Hellhound66 »

Remi, wenn Du für jeden VT nutzt, dann ists aber auch essig mit ner Vererbung, oder? Ich mein, bei Vektoren stört das relativ wenig, aber bei "höheren" Objekten, ich denke da an meine Objekte, du erinnerst Dich, ist das ziemlich dämlich. Da nutz ich lieber dein "altes" Format. Mir erschließt sich nicht so der Sinn hier hinter. Ich meine das für Vektoren zu tun. Da wird wenig vererbt und der Unterschied zwischen V\Set(1,2,3) und Set(v,1,2,3) ist ja nicht allzu groß.

[edit] Liebe Mods. Bitte schreibt es doch in meinen Post rein, wenn ihr was ändert. Dann erschreck ich mich nicht so.[/edit]
Edit NicTheQuick: "Sorry, habs bei dem ganzen Durcheinander vergessen. Aber das nächste mal ganz bestimmt. :) "
Optimismus ist ein Mangel an Information.
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Ein Konzept von OOP ist die Kapselung von Daten und Funktionen, was
ich hiermit tue! Es muss dir nicht gefallen :wink:
Wenn ich die VT jedem Vektor gegeben hätte (war ja am Anfang so), dann
wäre der Speicherverbrauch pro Vektor bei ca. 90bytes gewesen, was
natürlich zu viel ist. Nun hab ich die VT für jeden Vektor gleich.

Für Vererbung sind Interfaces wohl allgemein nicht geeignet, da wenn
einfach mit 'extends' eine Struktur mit VT geerbt wird, weiss man nicht,
wo nun die Funktionen stehen.
Bsp:

Code: Alles auswählen

Structure cOVECTOR
	VTable.l
	
  f1.l
  f2.l
	; Data
	x.f
	y.f
	z.f
EndStructure

Structure cOVECTOR2 extends cOVECTOR
	
  f3.l
	; Data
  u.f
  v.f
EndStructure
Nun wird ev. cOVECTOR am Anfang eingefügt, wobei zwischen der Funktion
f3 und f2 noch Daten stehen. Somit ist Vererbung so gut wie unmöglich
einfach zu realisieren, da dann die VT für das Interface nicht mehr bei-
einander liegt...
Es geht schon, aber ziemlich mühsam. Wenn man z. B. extra Strukturen
für die VTs anlegt (so wie hier), die können dann gegenseitig vererbt werden.
Das Grundobjekt sieht dann so aus:

Code: Alles auswählen

Structure cBase
  *VTable
  ; Data
  u.f
  v.f
EndStructure
Hier muss natürlich die VTable extern vererbt werden, aber dafür sollte
es gehen...

:| Aus diesem Post kommt sicher niemand draus, hab immer wieder was
geändert und neu überlegt... Widersprüche gibts sicher auch noch :?
Ev. bringts ja doch was^^. Aber interessantes Thema!


greetz
Remi <)

BIGEDIT: Da steht wie ichs gemeint habe:
http://forums.purebasic.com/german/viewtopic.php?t=4885
Hellhound66
Beiträge: 476
Registriert: 23.03.2005 23:19

Beitrag von Hellhound66 »

Das ist eben der Grund, warum ich bei mir die Interfaces weglasse und callfunctionfast nutze. Wie Du ja schon kennst:

Code: Alles auswählen

Structure _OBJECT
  *Show
  *HandleEvent
EndStructure

Structure _WINDOW Extends _OBJECT
  *Move
  
  X.l
  Y.l
EndStructure

;************* Object Procedures ******************
Procedure _obj_Show(*SELF._OBJECT)
  Debug "Object Show"
EndProcedure

Procedure _obj_HandleEvent(*SELF._OBJECT,Handle.l)
  debug "Object HandleEvent"
EndProcedure

;************* Window Procedures ***************
Procedure _win_Show(*SELF._WINDOW)
  Debug "Window Show"
EndProcedure

Procedure _win_Move(*SELF._WINDOW,_x.l,_y.l)
  *SELF\X = _x
  *SELF\Y = _y
EndProcedure

;**************** Main *****************************
Procedure.l O_CreateObject(SIZE.l)
  *SELF._OBJECT = AllocateMemory(SIZE)
  *SELF\SHOW = @_obj_Show()
  *SELF\HandleEvent = @_obj_HandleEvent()
  ProcedureReturn *SELF
EndProcedure 

Procedure.l O_CreateWindow(SIZE.l)
  *SELF._WINDOW = O_CreateObject(SIZE)
  *SELF\SHOW =@_win_Show()
  *SELF\Move = @_win_Move()
  ProcedureReturn *SELF
EndProcedure

*OBJECT._OBJECT = O_CreateObject(SizeOf(_OBJECT))
*WINDOW._WINDOW = O_CreateWindow(SizeOf(_WINDOW))

CallFunctionFast(*OBJECT\SHOW,*OBJECT)
CallFunctionFast(*WINDOW\SHOW,*WINDOW)

CallFunctionFast(*OBJECT\HandleEvent,*OBJECT,0)
CallFunctionFast(*WINDOW\HandleEvent,*WINDOW,0)

So kann man sehr schön Funktionen überladen. Das einzige, was hier noch fehlt, ist, dass die alten überschriebenen Funktionen in einer Tabelle gespeichert werden, um ein "Inherited Function()" Aufruf zu ermöglichen. Aber das brauche ich eigentlich nicht.
Optimismus ist ein Mangel an Information.
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Geht mit Interfaces glaub auch (wüsste nicht, was hier nicht gehen sollte).
Siehe mein BIGEDIT oben. Du musst dich einfach an strikte Regeln halten
und kannst die Funktionen nicht immer so einfach beim Namen nennen,
sondern musst dir auch bewusst sein, so sie im Speicher sind (für VTable).

Ziehs dir mal rein, sollte alles gehen.

greetz
Remi
Antworten