[Bmax] Praktisches, skalierbares 2D-Array als Type

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Fetze

Betreff: [Bmax] Praktisches, skalierbares 2D-Array als Type

BeitragMi, Jul 19, 2006 19:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Da ich selber derzeit Bedarf an sowas hatte, es aber bisher keine zufriedenstellende Lösung gab, habe ich mich mal hieran gemacht. Das Testprogramm - Erst lesen, dann starten! - sollte eigentlich alles erklären.

Code: [AUSKLAPPEN]
'##### TESTPROGRAMM #####
SuperStrict

SeedRnd MilliSecs()
Graphics 800, 600, 0, -1

'Erstmal ein DDObjArray erstellen, Größe 1 * 1, also genau 1 Feld an Position [0, 0]
Global ddaTest:DDObjArray = DDObjArray.Create(1, 1)

'Jetzt weisen wir der Position [0, 0] das String-Objekt "Hallo" zu..
ddaTest.Set(0, 0, "Hallo")
Print "ddaTest.Set(0, 0, ''Hallo'')"

'..das wir mit Get nun an Position [0, 0] zurückgeliefert bekommen
Print "ddaTest.Get(0, 0) = " + String(ddaTest.Get(0, 0))

'Übrigens kann man ein DDObjArray auch gefahrlos kopieren.
ddaTest = ddaTest.Copy()
'In diesem Fall überschreiben wir unser Array mit einer Kopie von sich selbst.

'Jetzt weisen wir dem DDObjArray einen neuen Ursprungspunkt zu, der bei [1, 1] liegt. Alle "Koordinaten"
'des DDObjArrays stehen RELATIV zu diesem Ursprungspunkt. Wenn ich also den Wert unserer alten [0, 0]-Position
'erfahren will, muss ich im folgenden nach [-1, -1] fragen, da [0, 0] relativ zu [1, 1] = [-1, -1].
'So lange SetOrigin und AssertPos (Aber dazu kommen wir später) nicht aufgerufen werden, sind die RELATIVEN
'Koordinaten gleich den ABSOLUTEN koordinaten, da der Ursprungspunkt dann an [0, 0] liegt.
ddaTest.SetOrigin(1, 1)
Print "ddaTest.SetOrigin(1, 1)"

'Hier ein kleiner Beweis:
Print "ddaTest.Get(-1, -1) = " + String(ddaTest.Get(-1, -1))
'Würden wir Get weiterhin mit [0, 0] aufrufen, bekämen wir nen ordentlichen "Index out of bounds"-Fehler:
'Damit würden wir nach der ABSOLUTEN Position [1, 1] (die RELATIV zum Urpsrung [1, 1] an [0, 0] liegt) fragen,
'die bei unserem 1 * 1 - Array jedoch nicht existiert.

'Wir wollen aber um jeden Preis weiterhin auch an [0, 0] speichern und laden können. Hier gibt es 2 Möglichkeiten:
'Entweder, wir erhöhen die absolute Größe des DDObjArrays:
ddaTest.Resize(2, 2)
Print "ddaTest.Resize(2, 2)"
'Zur Erinnerung: Größe 2 * 2 bedeutet absolut von [0, 0] bis [1, 1], in diesem Fall relativ aber von
'[-1, -1] bis [0, 0]

ddaTest.Set(0, -1, "Das ist")
ddaTest.Set(-1, 0, "ein")
ddaTest.Set(0, 0, "Test")
Print "ddaTest.Set(0, -1, ''Das ist'')"
Print "ddaTest.Set(-1, 0, ''ein'')"
Print "ddaTest.Set(0, 0, ''Test'')"

Print "ddaTest.Get(-1, -1) = " + String(ddaTest.Get(-1, -1))
Print "ddaTest.Get(0, -1) = " + String(ddaTest.Get(0, -1))
Print "ddaTest.Get(-1, 0) = " + String(ddaTest.Get(-1, 0))
Print "ddaTest.Get(0, 0) = " + String(ddaTest.Get(0, 0))

'Kommen wir nun zur anderen Möglichkeit. Ich werde unsere Veränderungen dafür rückgängig machen:
ddaTest.Resize(1, 1)
Print "ddaTest.Resize(1, 1)"

'Die andere Möglichkeit ist der Befehl "AssertPos". Mit ihm kann man "auf eine beliebige Position bestehen", d.h.
'Diese Position wird auf jeden Fall existieren, allerdings nicht unbedingt NUR diese Position. Denn: AssertPos
'ist im wesentlichen eine Automatische Fassung von Resize, kombiniert mit SetOrigin. AssertPost garantiert, dass
'sich danach noch alles an seinem Platz befindet, jedoch mehr Platz vorhanden ist, in dem sich etwas befinden
'kann - und das genau dort, wo man es braucht. Hier nocheinmal genau dasselbe wie eben, nur mit AssertPos statt
'Resize:

ddaTest.AssertPos(0, 0)
Print "ddaTest.AssertPos(0, 0)"
'Das Array wurde nun auf die absolute Größe [2, 2] vergrößert, damit es vom relativen [-1, -1] in der relative
'[0, 0] hineinragt und [0, 0] nun mit einem Objekt befüllt werden kann. Dabei wurden auch gleichzeitig die
'Koordinaten [0, -1] und [-1, 0] frei. Im wesentlichen also genau wie Resize(2, 2).

ddaTest.Set(0, -1, "Das ist")
ddaTest.Set(-1, 0, "ein")
ddaTest.Set(0, 0, "Test")
Print "ddaTest.Set(0, -1, ''Das ist'')"
Print "ddaTest.Set(-1, 0, ''ein'')"
Print "ddaTest.Set(0, 0, ''Test'')"

Print "ddaTest.Get(-1, -1) = " + String(ddaTest.Get(-1, -1))
Print "ddaTest.Get(0, -1) = " + String(ddaTest.Get(0, -1))
Print "ddaTest.Get(-1, 0) = " + String(ddaTest.Get(-1, 0))
Print "ddaTest.Get(0, 0) = " + String(ddaTest.Get(0, 0))

'Aber: AssertPost kann noch mehr. Wenn wir beispielsweise in unserem Beispiel-Array die Position [-2, -2]
'belegen wollen, kommen wir mit Resize nicht weit, sondern müssten auch den Ursprung umverlegen. Damit man
'sich mit diesem ganzen kram nicht befassen muss, gibt es die AssertPost-Methode, die das alles vollautomatisch
'erledigt.

ddaTest.AssertPos(-2, -2)
Print "ddaTest.AssertPos(-2, -2)"
'Dadurch haben wir nun nebenbei bemerkt ein 3 * 3 - Array, das von [-2, -2] bis [0, 0] reicht.

ddaTest.Set(-2, -2, "Blub")
Print "ddaTest.Set(-2, -2, ''Blub'')"

Print "ddaTest.Get(-2, -2) = " + String(ddaTest.Get(-2, -2))

'Natürlich sind solcherlei Arrays recht kompliziert in Schleifen zu verwenden, da man möglicherweise nicht
'weis, wo man beginnen und wo enden soll. Damit trotzdem keine Verwirrung entsteht, gibt es einige Methoden,
'die es einem etwas erleichtern:

For Local iYLoop:Int = ddaTest.GetLowestY() To ddaTest.GetHighestY()
   For Local iXLoop:Int = ddaTest.GetLowestX() To ddaTest.GetHighestX()
      Print "[" + iXLoop + ", " + iYLoop + "] = " + String(ddaTest.Get(iXLoop, iYLoop))
   Next
Next

'Und hier unser Array nocheinmal grafisch dargestellt! ^^
SetClsColor 255, 255, 255
Repeat
   Cls
   
   For Local iYLoop:Int = ddaTest.GetLowestY() To ddaTest.GetHighestY()
      For Local iXLoop:Int = ddaTest.GetLowestX() To ddaTest.GetHighestX()
         SetColor 200, (iYLoop - ddaTest.GetLowestY()) * 75, (iXLoop - ddaTest.GetLowestX()) * 75
         DrawRect (iXLoop - ddaTest.GetLowestX()) * 100, (iYLoop - ddaTest.GetLowestY()) * 100, 100, 100
         SetColor 0, 0, 0
         DrawText iXLoop + " " + iYLoop, (iXLoop - ddaTest.GetLowestX()) * 100, ((iYLoop - ddaTest.GetLowestY()) * 100) + 10
         DrawText String(ddaTest.Get(iXLoop, iYLoop)), (iXLoop - ddaTest.GetLowestX()) * 100, ((iYLoop - ddaTest.GetLowestY()) * 100) + 20
      Next
   Next
   
   Flip
Until KeyHit(KEY_ESCAPE)

'Zu guter letzt:
ddaTest.Clear()
'Die Clear-Methode löscht den Inhalt des Arrays komplett. Dabei bleibt seine Größe jedoch erhalten.

'########################

Type DDObjArray
   Field objArray   :Object[][]
   Field iWidth   :Int
   Field iHeight   :Int
   Field iOriginX   :Int
   Field iOriginY   :Int
   
   'Setzt den Ausgangspunkt des Arrays neu. Beispiel: SetOrigin(1, 1) sorgt dafür, dass man objArray[0][0] per
   'Get-Methode unter Get(-1, -1) erreicht, da jede Position nun relativ zu [1, 1] gesehen wird.
   Method SetOrigin:Byte(iParX:Int, iParY:Int)
      iOriginX = iParX
      iOriginY = iParY
      Return True
   End Method
   
   'Sorgt durch Resize und SetOrigin dafür, dass die angegebene Position innerhalb des Arrays liegt. Die
   'bisherigen Datensätze bleiben unter derselben Position erreichbar wie vorher.
   Method AssertPos:Byte(iParX:Int, iParY:Int)
      If ValidPos(iParX, iParY) = True Then Return True
      
      Local iNewWidth:Int = iWidth
      Local iNewHeight:Int = iHeight
      Local iNewOriginX:Int = iOriginX
      Local iNewOriginY:Int = iOriginY
      
      If iParX + iOriginX + 1 > iWidth Then
         iNewWidth = iParX + iOriginX + 1
      ElseIf iParX + iOriginX < 0 Then
         iNewWidth = iWidth - (iParX + iOriginX)
         iNewOriginX:- (iParX + iOriginX)
      End If
      If iParY + iOriginY + 1 > iHeight Then
         iNewHeight = iParY + iOriginY + 1
      ElseIf iParY + iOriginY < 0 Then
         iNewHeight = iHeight - (iParY + iOriginY)
         iNewOriginY:- (iParY + iOriginY)
      End If
      
      Resize(iNewWidth, iNewHeight)
      
      Rem
      Local iRelX:Int = iNewOriginX - iOriginX
      Local iRelY:Int = iNewOriginY - iOriginY
      If iRelX <> 0 Or iRelY <> 0 Then
         Local ddaTemp:DDObjArray = Self.Copy()
         Self.Clear()
         For Local iYTemp:Int = GetLowestY() To GetHighestY()
            For Local iXTemp:Int = GetLowestX() To GetHighestX()
               If iXTemp + iRelX + iOriginX < 0 Then Continue
               If iXTemp + iRelX + iOriginX >= iWidth Then Continue
               If iYTemp + iRelY + iOriginY < 0 Then Continue
               If iYTemp + iRelY + iOriginY >= iHeight Then Continue
               Set(iXTemp + iRelX, iYTemp + iRelY, ddaTemp.Get(iXTemp, iYTemp))
            Next
         Next
      End If
      End Rem
      Local iRelX:Int = iNewOriginX - iOriginX
      Local iRelY:Int = iNewOriginY - iOriginY
      If iRelX <> 0 Or iRelY <> 0 Then
         For Local iYTemp:Int = GetHighestY() To GetLowestY() Step -1
            For Local iXTemp:Int = GetHighestX() To GetLowestX() Step -1
               If ValidPos(iXTemp + iRelX, iYTemp + iRelY) = False Then Continue
               If ValidPos(iXTemp, iYTemp) = False Then Continue
               Set(iXTemp + iRelX, iYTemp + iRelY, Get(iXTemp, iYTemp))
               Set(iXTemp, iYTemp, Null)
            Next
         Next
      End If
      
      SetOrigin(iNewOriginX, iNewOriginY)
      
      Return True
   End Method
   
   'Zuschneiden des DDObjArrays: Inhalt und Position des Inhalts bleibt gleich, größe des Arrays wird jedoch
   'auf ein Minimum reduziert.
   Method CutDown:Byte()
      If iWidth = 1 And iHeight = 1 Then Return True
      
      Local iLowestX:Int = 2147483647 'Höchstmöglicher Integer-Wert
      Local iLowestY:Int = 2147483647
      Local iHighestX:Int = -2147483647 'Niedrigstmöglicher Integer-Wert
      Local iHighestY:Int = -2147483647
      
      'Eckpunkte finden:
      For Local iLoop:Int = GetLowestX() To GetHighestX()
         For Local iLoop2:Int = GetLowestY() To GetHighestY()
            If Get(iLoop, iLoop2) <> Null Then
               If iLoop < iLowestX Then iLowestX = iLoop
               If iLoop2 < iLowestY Then iLowestY = iLoop2
               If iLoop > iHighestX Then iHighestX = iLoop
               If iLoop2 > iHighestY Then iHighestY = iLoop2
            End If
         Next
      Next
      'Einzige Schlussfolgerung, wenn gar kein Wert gesetzt wurde: Das Array ist leer.
      If iLowestX = 2147483647 And iLowestY = 2147483647 And iHighestX = -2147483647 And iHighestY = -2147483647 Then
         Resize(1, 1)
         Return True
      End If
      
      Local iNewWidth:Int = iHighestX - iLowestX + 1
      Local iNewHeight:Int = iHighestY - iLowestY + 1
      Local iNewOriginX:Int = iOriginX - (iLowestX - GetLowestX())
      Local iNewOriginY:Int = iOriginY - (iLowestY - GetLowestY())
      
      Local iRelX:Int = iNewOriginX - iOriginX
      Local iRelY:Int = iNewOriginY - iOriginY
      If iRelX <> 0 Or iRelY <> 0 Then
         For Local iYTemp:Int = GetLowestY() To GetHighestY()
            For Local iXTemp:Int = GetLowestX() To GetHighestX()
               If ValidPos(iXTemp + iRelX, iYTemp + iRelY) = False Then Continue
               If ValidPos(iXTemp, iYTemp) = False Then Continue
               Set(iXTemp + iRelX, iYTemp + iRelY, Get(iXTemp, iYTemp))
               Set(iXTemp, iYTemp, Null)
            Next
         Next
      End If
      
      Resize(iNewWidth, iNewHeight)
      SetOrigin(iNewOriginX, iNewOriginY)
      
      Return True
   End Method
   
   Method GetLowestX:Int()
      Return -iOriginX
   End Method
   
   Method GetLowestY:Int()
      Return -iOriginY
   End Method
   
   Method GetHighestX:Int()
      Return iWidth - iOriginX - 1
   End Method
   
   Method GetHighestY:Int()
      Return iHeight - iOriginY - 1
   End Method
   
   Method Copy:DDObjArray()
      Local ddaTemp:DDObjArray = New DDObjArray
      
      ddaTemp.objArray = objArray[..]
      For Local iLoop:Int = 0 To objArray.length - 1
         ddaTemp.objArray[iLoop] = objArray[iLoop][..]
      Next
      ddaTemp.iWidth = iWidth
      ddaTemp.iHeight = iHeight
      ddaTemp.iOriginX = iOriginX
      ddaTemp.iOriginY = iOriginY
      
      Return ddaTemp
   End Method
   
   Method Set:Byte(iParX:Int, iParY:Int, objPar:Object)
      objArray[iParX + iOriginX][iParY + iOriginY] = objPar
      Return True
   End Method
   
   Method Get:Object(iParX:Int, iParY:Int)
      Return objArray[iParX + iOriginX][iParY + iOriginY]
   End Method
   
   Method ValidPos:Byte(iParX:Int, iParY:Int)
      If iParX + iOriginX >= 0 And iParX + iOriginX < iWidth Then
         If iParY + iOriginY >= 0 And iParY + iOriginY < iHeight Then
            Return True
         End If
      End If
      Return False
   End Method
   
   Method Clear:Byte()
      Local iOldWidth:Int = iWidth
      Local iOldHeight:Int = iHeight
      
      Resize(0, 0)
      Resize(iOldWidth, iOldHeight)
      
      Return True
   End Method
   
   Method Resize:Byte(iParWidth:Int, iParHeight:Int)
      ?Debug
      If iParWidth = 0 Then RuntimeError("Invalid Width-Parameter: " + iParWidth)
      If iParHeight = 0 Then RuntimeError("Invalid Height-Parameter: " + iParHeight)
      ?
      
      If objArray.length <> iParWidth Then
         objArray = objArray[..iParWidth]
      End If
      For Local iLoop:Int = 0 To iParWidth - 1
         If objArray[iLoop].length <> iParHeight Then
            objArray[iLoop] = objArray[iLoop][..iParHeight]
         End If
      Next
      iWidth = iParWidth
      iHeight = iParHeight
      
      Return True
   End Method
   
   Function Create:DDObjArray(iParWidth:Int, iParHeight:Int)
      Local ddaTemp:DDObjArray = New DDObjArray
      
      ddaTemp.Resize(iParWidth, iParHeight)
      
      Return ddaTemp
   End Function
End Type


Viel Spaß damit, Kommentare und Erweiterungen erwünscht Wink
  • Zuletzt bearbeitet von Fetze am Do, Jul 20, 2006 16:19, insgesamt einmal bearbeitet

Fetze

BeitragDo, Jul 20, 2006 10:54
Antworten mit Zitat
Benutzer-Profile anzeigen
So, gibt ne neue Methode. Mit der kann man das DDObjArray bei gleichbleibenden Abfragepositionen zuschneiden lassen, d.h. leere Ränder werden entfernt.

Einfach mal testen, indem man irgendeine irrsinnige AssertPos aufruft und direkt danach ein CutDown. Nach dem CutDown sollte alles wieder genauso aussehen wie vorher.

Code: [AUSKLAPPEN]

'Zuschneiden des DDObjArrays: Inhalt und Position des Inhalts bleibt gleich, größe des Arrays wird jedoch
   'auf ein Minimum reduziert.
   Method CutDown:Byte()
      If iWidth = 1 And iHeight = 1 Then Return True
      
      Local iLowestX:Int = 2147483647 'Höchstmöglicher Integer-Wert
      Local iLowestY:Int = 2147483647
      Local iHighestX:Int = -2147483647 'Niedrigstmöglicher Integer-Wert
      Local iHighestY:Int = -2147483647
      
      'Eckpunkte finden:
      For Local iLoop:Int = GetLowestX() To GetHighestX()
         For Local iLoop2:Int = GetLowestY() To GetHighestY()
            If Get(iLoop, iLoop2) <> Null Then
               If iLoop < iLowestX Then iLowestX = iLoop
               If iLoop2 < iLowestY Then iLowestY = iLoop2
               If iLoop > iHighestX Then iHighestX = iLoop
               If iLoop2 > iHighestY Then iHighestY = iLoop2
            End If
         Next
      Next
      'Einzige Schlussfolgerung, wenn gar kein Wert gesetzt wurde: Das Array ist leer.
      If iLowestX = 2147483647 And iLowestY = 2147483647 And iHighestX = -2147483647 And iHighestY = -2147483647 Then
         Resize(1, 1)
         Return True
      End If
      
      Local iNewWidth:Int = iHighestX - iLowestX + 1
      Local iNewHeight:Int = iHighestY - iLowestY + 1
      Local iNewOriginX:Int = iOriginX - (iLowestX - GetLowestX())
      Local iNewOriginY:Int = iOriginY - (iLowestY - GetLowestY())
      
      Local iRelX:Int = iNewOriginX - iOriginX
      Local iRelY:Int = iNewOriginY - iOriginY
      If iRelX <> 0 Or iRelY <> 0 Then
         For Local iYTemp:Int = GetLowestY() To GetHighestY()
            For Local iXTemp:Int = GetLowestX() To GetHighestX()
               If iXTemp + iRelX + iOriginX < 0 Then Continue
               If iXTemp + iRelX + iOriginX >= iWidth Then Continue
               If iYTemp + iRelY + iOriginY < 0 Then Continue
               If iYTemp + iRelY + iOriginY >= iHeight Then Continue
               Set(iXTemp + iRelX, iYTemp + iRelY, Get(iXTemp, iYTemp))
               Set(iXTemp, iYTemp, Null)
            Next
         Next
      End If
      
      Resize(iNewWidth, iNewHeight)
      SetOrigin(iNewOriginX, iNewOriginY)
      
      Return True
   End Method

Fetze

BeitragDo, Jul 20, 2006 16:19
Antworten mit Zitat
Benutzer-Profile anzeigen
Habe gerade die (hoffentlich) letzten Bugs entfernt, jetzt sollte das Array wirklich 100% stabil laufen. Habe meinen ersten Post editiert.

Artemis

BeitragDo, Jul 20, 2006 16:48
Antworten mit Zitat
Benutzer-Profile anzeigen
Gute Idee. Kann man bestimmt mal gebrauchen. *thumbs up*

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group