Fraktale: BASIC --> AutoIt

  • Während minx letzte Nacht an dem Hopalong Fraktal gearbeitet hat, habe ich, wie auch schon in der Shoutbox angekündigt, eine kleine UDF geschreiben, um Fraktale von BASIC fast 1:1 in AutoIt übernehmen zu können.

    BASIC.au3
    [autoit]

    #include-once
    ; BASIC.au3 by James1337
    ; thanks to Jojo (forum.qbasic.at)

    [/autoit] [autoit][/autoit] [autoit]

    AutoItSetOption("GUIOnEventMode", 1)

    [/autoit] [autoit][/autoit] [autoit]

    Global $PLOT[3] = [False, -1, -1]
    Func PLOT($x, $y, $color=0xFFFFFF, $gdi32="gdi32.dll")
    If $PLOT[0] Then
    SetPixel($PLOT[2], $x+320, $y+240, $color, $gdi32)
    Else
    $PLOT[0] = True
    $PLOT[1] = GUICreate("BASIC", 640, 480)
    GUISetOnEvent(-3, "ExitB", $PLOT[1])
    GUISetBkColor(0x000000, $PLOT[1])
    GUISetState(@SW_SHOW, $PLOT[1])
    $PLOT[2] = GetDC($PLOT[1])
    PLOT($x, $y, $color, $gdi32)
    EndIf
    EndFunc
    Func IDLE()
    While Sleep(10)
    WEnd
    EndFunc
    Func ExitB()
    Exit
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func POKE($address, $color, $screenwidth=320, $gdi32="gdi32.dll")
    PLOT(Mod($address, $screenwidth), ($address / $screenwidth), $color, $gdi32)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Global $PALETTE[256]
    Func PALETTE($x, $y)
    Local $R = Mod($y, 64) * 4
    Local $G = Mod(($y / 256), 64) * 4
    Local $B = Mod(($y / 65536), 64) * 4
    $PALETTE[$x] = "0x" & Hex($B, 2) & Hex($G, 2) & Hex($R, 2)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func SIGN($x)
    Return ( ($x > 0) - ($x < 0) )
    EndFunc ; by Mars

    [/autoit] [autoit][/autoit] [autoit]

    Func GetDC($hWnd, $user32="user32.dll")
    $DC = DllCall($user32, "int", "GetDC", "hwnd", $hWnd)
    If @error Then Return SetError(@error, 0, -1)
    Return $DC[0]
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func SetPixel($hdc, $X, $Y, $Color, $gdi32="gdi32.dll")
    DllCall($gdi32, "long", "SetPixel", "long", $hdc, "long", $X, "long", $Y, "long", $Color)
    EndFunc

    [/autoit]

    Da wir uns beide zufällig Hopalong ausgesucht haben nehme ich das hier auch gleich als Beispiel:

    Zitat von http://www.fraktalwelt.de/myhome/simpiter2.htm
    Code
    INPUT num
    INPUT a, b, c
    x = 0
    y = 0
    PLOT(x, y)
    FOR i = 1 TO num
    xx = y - SIGN(x) * [ABS(b*x - c)]^0.5
    yy = a - x
    x = xx
    y = yy


    In AutoIt sieht das dann so aus:

    [autoit]

    #NoTrayIcon
    #include "BASIC.au3"

    [/autoit][autoit][/autoit][autoit]

    Local $num, $a, $v, $c, $x, $y, $xx, $yy

    [/autoit][autoit][/autoit][autoit]

    $num = InputBox("", "num", 50000)
    $a = InputBox("", "a", 94)
    $b = InputBox("", "b", 58)
    $c = InputBox("", "c", -99)
    $x = 0
    $y = 0

    [/autoit][autoit][/autoit][autoit]

    For $i = 1 To $num
    $xx = $y - SIGN($x) * Sqrt(Abs($b*$x - $c))
    $yy = $a - $x
    $x = $xx
    $y = $yy
    PLOT($x, $y, 0xFF*$i)
    Next
    IDLE()

    [/autoit]


    Wie man sieht, besteht da kein großer Unterschied.
    - aus Variable wurde $Variable (was man sich in der neuen Beta auch sparen kann)
    - aus INPUT wurde InputBox und die Syntax ist leicht anders
    - aus ^0.5 wurde Sqrt(), was aber eigentlich das Gleiche ist
    - PLOT() steht jetzt in der For-Schleife
    - damit sich das Fenster nicht sofort wieder schließt, wurde IDLE() eingefügt
    Man könnte jetzt natürlich noch Farben einbauen (siehe 3. Parameter von PLOT, wobei die Farbe das Format 0xBBGGRR hat) und gdi32.dll mit DllOpen/DllClose verwenden (siehe 4. Parameter von PLOT), aber so im Großen und Ganzen funktioniert es ja.

    MfG, James

    PS: Fall jemand noch Funktionen hat (oder kennt), die in diese UDF passen würden, hinterlasst einfach einen Kommentar. :)

  • Wei Mars schon gesagt hat, kannst du SGN durch einfach: (($x>0)-($x<0)) ersetzen ;)

    Als Standerd für die PLOT / PIXEL Funktion, die desweiteren auch auch PSET heißt würde ich schon einen kleinen GDI Befehl einbauen.


    //EDIT:

    Du solltest unbedingt die BASIC Version angeben. Ganz witzig wäre nämlich mal ein kleiner Assistent für das Übersetzen von Microsofts QBASIC, weil man damit sehr sehr einfach sehr komplexe Grafiken erzeugen kann, und warum das Rad neu erfinden :whistling:

    //EDIT2:

    Ich hab vergessen zu erwähnen das du bei der GDI.dll auch vorher das Handle abfragen musst.
    Ich würde auch IDLE() durch

    Do
    until guigetmsg()=-3

    ersetzen oder OnEvents verwenden.

    Einmal editiert, zuletzt von minx (25. August 2012 um 12:50)

  • Wei Mars schon gesagt hat, kannst du SGN durch einfach: (($x>0)-($x<0)) ersetzen ;)

    Kann ich ja noch machen, ist aber auch ziemlich egal im Moment.

    Als Standerd für die PLOT / PIXEL Funktion, die desweiteren auch auch PSET heißt würde ich schon einen kleinen GDI Befehl einbauen.

    SetPixel ist doch GDI, oder was meinst du?

    Du solltest unbedingt die BASIC Version angeben. Ganz witzig wäre nämlich mal ein kleiner Assistent für das Übersetzen von Microsofts QBASIC, weil man damit sehr sehr einfach sehr komplexe Grafiken erzeugen kann, und warum das Rad neu erfinden :whistling:

    Ich weis nicht, welche BASIC Version die auf der Seite verwenden, es ging mir aber auch nur darum, so einfach wie möglich in AutoIt Fraktale erstellen zu können. Um QBASIC zu ersetzen müsste man ja wahrscheinlich auch alle in AutoIt fehlenden Funktionen nachbauen, also mache ich doch momentan nichts anderes, oder?

    Ich hab vergessen zu erwähnen das du bei der GDI.dll auch vorher das Handle abfragen musst.

    ?

    Zitat

    Ich würde auch IDLE() durch

    Do
    until guigetmsg()=-3

    ersetzen oder OnEvents verwenden.

    Und weiter? Genau das macht IDLE doch?!

  • Ja :rolleyes:

    Hatte mir die UDF nicht runtergeladen. Also das von Marsi bringt einen ziemlichen Geschwindigkeitsboost.

    Ne, für mich ging aus dem AutoIt Beispiel nicht hervor, was die Funktionen beinhalten :D

    MfG

  • Hatte mir die UDF nicht runtergeladen. Also das von Marsi bringt einen ziemlichen Geschwindigkeitsboost.

    Ne, für mich ging aus dem AutoIt Beispiel nicht hervor, was die Funktionen beinhalten :D


    Na dann ist ja gut, obwohl du schon mal auf den Spoiler hättest klicken können. ;)

  • Die UDF in Post #1 wurde überarbeitet und wir können mit dem 2. Beispiel beginnen.
    Vorher aber noch ein Dank an Jojo aus dem deutschen QBasic- und FreeBASIC-Forum, der wesentlich zu der Portierung einzelner BASIC-Befehle beigetragen hat.

    Dieses Fraktal sieht in QBASIC (Code von minx) so aus:


    Meine Umsetzung in AutoIt sieht so aus:

    [autoit]

    #NoTrayIcon
    #include "BASIC.au3"

    [/autoit][autoit][/autoit][autoit]

    Local $sins[3601]
    Local $rands[18001]

    [/autoit][autoit][/autoit][autoit]

    Local $n
    For $n = 0 To 21856
    If ($n < 18001) Then
    $rands[$n] = Random() * 32
    ElseIf ($n < 21601) Then
    $sins[$n - 18000] = 256 * Sin( ($n - 18000) * .0349 )
    ElseIf ($n < 21729) Then
    PALETTE( ($n - 21601), ( ($n - 21601) / 2 ) * 65793 )
    Else
    PALETTE( (255 - ($n - 21729)), ( ($n - 21729) / 2 ) * 65793 )
    EndIf
    Next

    [/autoit][autoit][/autoit][autoit]

    Local $f = 0, $f2 = 0, $p, $r, $c
    Do
    $f = Mod( ($f + 2), 180)
    $f2 = Mod( ($f2 + 2), 50)
    $p = 0
    For $r = 0 To 199
    For $c = 0 To 319
    PLOT($c-160, $r-100, ($sins[$r + $f] + $sins[$c + $f] + $sins[$r + $c] + $rands[$f2*$r + $c]) )
    Next
    Next
    Until False

    [/autoit]


    An dieser Stelle wieder ein paar Anmerkungen:
    - da in BASIC Arrays bei 1 beginnen, wurde aus "sins(3600)" einfach "$sins[3601]", das Gleiche gilt natürlich auch für "rands"
    - es werden zwar Daten in die Palette geschrieben, aber nie welche ausgelesen
    -> das Skript funktioniert trotzdem
    - POKE habe ich zwar implementiert, im Beispiel aber durch PLOT ersetzt
    - die Überprüfung mit GUIGetMsg() sollte eigentlich in die innere For-Schleife, da man das Skript sonst nicht mehr beenden kann
    -> dadurch würde das Skript aber noch viel langsamer werden, als es sowieso schon ist (es ist auch überhaupt nicht optimiert)

    So, das war's auch schon wieder. Ich bitte weiterhin um Kommentare.

    MfG, James

  • Moin,

    Ihr seid ja voller Elan was Fraktale angeht.

    Schaut euch doch bitte mal die "Basics" von AutoIt an.
    Da gibt es viele Tricks und Kniffe.

    Ein Beispiel dafür ist der OnEventMode.
    Damit kannst du das Skript jederzeit beenden, ohne irgendwelche Ispressed Abfragen oder GuiGetMsg.

    Dann ist es unglaublich langsam per GDI und SetPx einen Pixel zu färben.
    Per Bitmap und DllstructSetData geht das locker 10x so schnell. (Allerdings habe ich beim Lava Beispiel festgestellt, dass dann die Farben teils misinterpretiert werden. einfach mal mit Bitshift rumspielen und das Problem ist erledigt).

  • Ein Beispiel dafür ist der OnEventMode.
    Damit kannst du das Skript jederzeit beenden, ohne irgendwelche Ispressed Abfragen oder GuiGetMsg.

    Kenne ich, könnte ich auch noch einbauen (habe ich eingebaut), aber dann könnte ich ja auch gleich das ganze Skript optimieren, und ich wollte ja, dass zwischen den beiden Skripten so wenig Unterschied wie möglich ist. ;)

    Dann ist es unglaublich langsam per GDI und SetPx einen Pixel zu färben.
    Per Bitmap und DllstructSetData geht das locker 10x so schnell. (Allerdings habe ich beim Lava Beispiel festgestellt, dass dann die Farben teils misinterpretiert werden. einfach mal mit Bitshift rumspielen und das Problem ist erledigt).

    Das hat zwar nichts mehr mit "AutoIt Basics" zu tun, sondern eher mit dem Aufbau einer Bitmap, aber trotzdem danke für den Tipp. Das Problem mit den Farben, dass du bemerkt hast, könnte damit zu tun haben, das bei SetPixel Farben anscheinend im BGR-Format verarbeitet werden (oder, dass die Bytes beim DllCall vertauscht werden, da bin ich mir noch nicht sicher).

    MfG, James

  • Hi,
    SetPixel() ist die denkbar langsamste Lösung, um komplette Grafiken zu zeichnen.
    Wenn diese Fraktale so langsam sind, wieso dann die wirklich einfachen Formeln nicht inline per Assembler, als DLL per FreeBasic, als 5-Zeiler in einem OpenCl-Kerneloder wie Marsi vorgeschlagen hat, per Schreiben in eine Bitmap-DllStruct beschleunigen.
    Wenn ich das so sehe, denke ich an einen "wie geht es am langsamsten"-Contest^^
    Wobei ich zugebe, ab und zu testweise auch mal "auf die schnelle" mit Setpixel() was zu machen, aber wenn das Script dann läuft, muss "was gehen" 8o

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (26. August 2012 um 20:08)

  • Hi,
    SetPixel() ist die denkbar langsamste Lösung, um komplette Grafiken zu zeichnen.
    Wenn diese Fraktale so langsam sind, wieso dann die wirklich einfachen Formeln nicht inline per Assembler, als DLL per FreeBasic, als 5-Zeiler in einem OpenCl-Kernel oder wie Marsi vorgeschlagen hat, per Schreiben in eine Bitmap-DllStruct beschleunigen.
    Wenn ich das so sehe, denke ich an einen "wie geht es am langsamsten"-Contest^^

    So könnte man das auch sehen. Ich werde es schon noch schaffen, die von minx gemessenen 0.5 FPS zu unterbieten... :thumbup:

    Aber mal ernsthaft: Die Geschwindigkeit von Hopalong reicht meiner Meinung nach, wenn es schneller wäre, würde man die "Animation" nicht mehr so wahrnehmen. Das Lava Beispiel finde ich zwar nicht so gut, aber spätestens bei dem nächsten guten Fraktal werde ich mal versuchen, eure Performance-Tipps umzusetzen.

    MfG, James

    Edit: OnEventMode eingebaut.
    Andy Du weist doch sicherlich, warum SetPixel laut MSDN RGB verwendet, wir hier unsere Farben in BGR angeben müssen, oder? Kann es sein, dass wie in Structs die Bytes "vertauscht" werden (wenn ich z.B. 0xCAFE in "byte[2]" schreibe und dann wieder auslese kommt ja 0xFECA raus)?

  • James,
    schau dir mal die "Endianess" (Little Endian und Big Endian) an, z.B. bei wikipedia, da ist es gut erklärt.

    Das Problem ist, wie du erkannt hast, die Art der Schreibweise. Schreibst du BYTE oder CHAR, dann werden diese im Speicher nacheinander angeordnet, schreibst du WORD, DWORD, INT, FLOAT usw, dann schlägt die Endianess erbarmungslos zu 8)
    Beim Lesen genauso, letztendlich ist das aber egel, wenn man begriffen hat, wie die Daten im Speicher stehen.

  • "Endianess" - Ja, schon mal gehört. ;)
    Du sagst, dass dieses "Problem" bei BYTE und CHAR nicht auftritt, wenn ich das richtig verstehe? Genau das benutze ich aber in dem Beispiel in Post #11.

    Zu SetPixel: Ich habe kein Problem damit, bei den Farben umzudenken, weshalb ich das (aus Faulheit) noch nicht geändert habe. Aber wahrscheinlich baue ich das die nächsten Tage mal ein, dass die anderen (falls es bei der UDF überhaupt andere Benutzer gibt) wie gewohnt weiter arbeiten können. ^^

  • /EDIT/ Aquivalent zur Setpixel() übergebenen Farbe in RGB. Im Speicher einer 32Bit-Bitmap stehen die Bytes in der Reihenfolge BGRA (A=Alphakanal), in einer 24Bit-Bitmap ohne Alphakanal BGR.
    Für die Umsetzung RGB -> BGR sorgt der Code hinter der Funktion Setpixel() 8o

    Ich hab da mal was vorbereitet^^

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>
    #include <String.au3>
    #include <WinAPI.au3>
    #include <StructureConstants.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;##########################

    [/autoit] [autoit][/autoit] [autoit]

    Local $w = 256, $h = 256
    _GDIPlus_Startup()

    [/autoit] [autoit][/autoit] [autoit]

    $hgui = GUICreate("", $w, $h) ;GUI
    $DC_gui = _WinAPI_GetDC($hgui) ;DC holen
    GUISetState() ;GUI einschalten

    [/autoit] [autoit][/autoit] [autoit]

    Global $ptr, $hbmp
    $DC_bitmap = _CreateNewBmp32($w, $h, $ptr, $hbmp);Bitmap erstellen, $PTR ist danach der Pointer auf die Bitmap

    [/autoit] [autoit][/autoit] [autoit]

    ;Struct um R,G, und B- Farben einzeln zu zeichnen
    $struct1 = DllStructCreate("byte[" & $w * $h * 4 & "]", $ptr);struct an Position der Bitmap erstellen

    [/autoit] [autoit][/autoit] [autoit]

    ;ARGB auf einmal zeichnen
    $struct2 = DllStructCreate("dword[" & $w * $h & "]", $ptr);struct an Position der Bitmap erstellen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;"pixel" malen
    For $i = 1 To 25000 Step 4 ;jedes 4. Byte ist dieselbe Farbe
    DllStructSetData($struct1, 1, 0xFF, $i + 00000);BLAU
    DllStructSetData($struct1, 1, 0xFF, $i + 25001);GRÜN
    DllStructSetData($struct1, 1, 0xFF, $i + 50002);ROT
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;bissl bunt
    For $i = 75000 To 100000 ;zufällige R,G, und B- Werte
    DllStructSetData($struct1, 1, Random(1, 255, 1), $i)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;jetzt ARGB (Little Endian), 2. Struct wird benutzt
    For $i = 30000 To 40000 ;DWORD ist 4 Byte lang!
    DllStructSetData($struct2, 1, 0x000000FF, $i); 0xAARRGGBB, also Blau
    Next
    For $i = 40000 To 50000 ;DWORD ist 4 Byte lang!
    DllStructSetData($struct2, 1, 0x00FF0000, $i); 0xAARRGGBB, also Rot
    Next

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _WinAPI_BitBlt($DC_gui, 0, 0, $w, $h, $DC_bitmap, 0, 0, $srccopy); Bitmap in GUI blitten

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    While GUIGetMsg() <> -3
    WEnd

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Shutdown()
    _DeleteBitmap32($DC_bitmap, $ptr, $hbmp)
    Exit

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe $HDC und $ptr und handle auf die Bitmapdaten
    $hcdc = _WinAPI_CreateCompatibleDC(0) ;Desktop-Kompatiblen DeviceContext erstellen lassen
    $tBMI = DllStructCreate($tagBITMAPINFO) ;Struktur der Bitmapinfo erstellen und Daten eintragen
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4);Structgröße abzüglich der Daten für die Palette
    DllStructSetData($tBMI, "Width", $iwidth)
    DllStructSetData($tBMI, "Height", -$iheight) ;minus =standard = bottomup
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32) ;32 Bit = 4 Bytes => AABBGGRR
    $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hbmp = $adib[0] ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden
    $ptr = $adib[4] ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet
    _WinAPI_SelectObject($hcdc, $hbmp) ;objekt hbitmap in DC
    Return $hcdc ;DC der Bitmap zurückgeben
    EndFunc ;==>_CreateNewBmp32

    [/autoit] [autoit][/autoit] [autoit]

    Func _DeleteBitmap32($DC, $ptr, $hbmp)
    _WinAPI_DeleteDC($DC)
    _WinAPI_DeleteObject($hbmp)
    $ptr = 0
    EndFunc ;==>_DeleteBitmap32

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit]