[Fraktal] Mandelbrot-Menge

  • Hallo AutoIt.de Forum,
    durch einen Vortrag im Mathe-Unterricht (Thema: Komplexe Zahlen) erfuhr ich von Fraktalen.
    Das Apfelmännchen, entdeckt durch Benoît Mandelbrot, ist das wohl bekannteste Fraktal.
    Also dachte ich mir: Mensch das code ich jetzt nach.
    Es ist schwerer als man denkt, obwohl das Script relativ kurz gehalten ist.
    Das Hauptproblem war: Das ganze zu verstehen und umzusetzen.

    Lange Rede, kurzer Sinn:
    Da es mit einer "relativ niedrigen" Auflösung arbeitet ist das Ergebnis dementsprechend ziwmnlich qualitativ unwertig, ich setze mich demnächst trotzdem mal daran es in höherer Qualität rendern zu lassen (an sich kann das jeder selbst machen, er brauch halt nur die Werte ändern.)

    [Blockierte Grafik: http://img213.imageshack.us/img213/7608/mandelbrotfraktal.png]
    Hier seht ihr das Apfelmännchen :)


    [Blockierte Grafik: http://img205.imageshack.us/img205/3343/mandelbrotfraktal2.png]
    Hier noch einmal, geringere Präzision bei höherer Auflösung -> Blitz besser erkennbar


    Sourcecode

    Abschließend noch ein großes Danke an BadBunny , der mir die Theorie erklärte und dafür seinen Schlaf opferte :)

    Es gibt sehr viele Leute, die glauben. Aber aus Aberglauben.
    - Blaise Pascal

    4 Mal editiert, zuletzt von Xorianator (13. Dezember 2013 um 00:52)

  • Sehr gutes Skript, da kann ich wieder was lernen.

    Allerdings habe ich trotzdem noch einige Verbesserungsvorschläge:
    1) _Betrag() durch Abs() ersetzen
    2) man kann das Program nicht beenden und wenn das im Skript richtig erkenne, wird es sobald der Apfel komplett ist, geschlossen
    3) bei 1x1 Rechtecken könnte man auch gleich

    [autoit]

    Func _GDIPlus_BitmapSetPixel($hBitmap, $iX, $iY, $iARGB = 0xFF000000)
    Global $ghGDIPDll
    Local $aRet
    $aRet = DllCall($ghGDIPDll, "int", "GdipBitmapSetPixel", "hwnd", $hBitmap, "int", $iX, "int", $iY, "dword", $iARGB)
    Return
    EndFunc

    [/autoit]

    benutzen, wobei ich nicht glaube, dass es dann schneller wäre
    4) das wäre ein gutes Beispiel für OpenCl

    MfG, James C.
    PS: Ich bin ein Fan von Skripten mit Mathematik und/oder Physik...

  • Ich kannte es ja schon :D

    Das waren 3 Stunden meines Lebens die ich dir geschenkt hab ich hoffe du würst es nutzen ;) Weiter so, ein wenig verschnellern geht immer (Hab dir ja eben eine etwas schnellere/Präzisere Version geschickt.)

    Gruß

    mfg BB

    "IF YOU'RE GOING TO KILL IT
    OPEN SOURCE IT!"

    by Phillip Torrone

    Zitat von Shoutbox

    [Heute, 11:16] Andy: ....böseböseböseböse....da erinnere ich mich daran, dass man den Puschelschwanz eines KaRnickels auch "Blume" nennt....ob da eins zum anderen passt? :rofl: :rofl: :rofl: :rofl:

    https://autoit.de/index.php?page…leIt#post251138

    Neon Snake

  • Joa ...
    Dafür haste aber einen weiteren abschnitt deines Lebens bekommen XD

    Apropos dein Script (OT): Ich habs ma grösser gemacht, und man sieht anhand der Farben das meine "Abweichung" gar net ma so verkehrt ist ... Der Blitz ist fast nur aus Hellen Teilen, wogegen die Figur selbst aus dunklen besteht .. Naja Waaaayne :D

    James:
    Joa, an OpenCL hab ich auch gedacht, aber für mich war überhaupt erstmal das "Ja - Ivh habs geschafft" wichtig ^^
    Ich schau morgrn ma was sich da machen lässt, wär natürlich Bpmbe wenn das dann schwupps-diwups da ist, und man dann gar net mehr hinterherkommt :D

    Desweiteren habsch mir überlegt es in C++ umzusetzen, na, ma dchauen !
    G00d Night euch ersma

    Es gibt sehr viele Leute, die glauben. Aber aus Aberglauben.
    - Blaise Pascal

  • sehr schönes Projekt, habe es jetzt auch mal Versucht und mit einem eigenen Algorithmus durchlaufen lassen.

    Hier der Quellcode:

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <Array.au3>
    #include <WindowsConstants.au3>
    #include <GUIConstantsEx.au3>
    Global $hPen, $GUI, $hGraphic, $hGraphicBuff
    Global $width = 7680, $height = 4320, $Itterations = 2000
    Mandelbrotmenge()
    Func Mandelbrotmenge()
    _GDIPlus_Startup()
    $hPen = _GDIPlus_PenCreate(0xFF000000, 1, 2)
    $GUI = GUICreate("Apfel", $width, $height)
    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($GUI)
    $hBitmap = _GDIPlus_BitmapCreateFromGraphics($width, $height, $hGraphic)
    $hGraphicBuff = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    renderImage()
    $s = _GDIPlus_ImageSaveToFile($hBitmap, @DesktopDir & "\7680x4320.bmp")
    GUIDelete()
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()
    EndFunc ;==>Mandelbrotmenge
    Func renderImage()
    Local $w = $width, $h = $height
    For $i = 0 To $h
    $b = $i * 3 / $h - 1.5
    For $j = 0 To $w
    $a = $j * 3 / $w - 2
    $n = isElement($a, $b)
    If $n = -1 Then
    _GDIPlus_GraphicsDrawRect($hGraphicBuff, $j, $i, 1, 1)
    Else
    $hexstep = Hex($n, 6)
    _GDIPlus_PenSetColor($hPen, "0xFF" & $hexstep)
    _GDIPlus_GraphicsDrawRect($hGraphicBuff, $j, $i, 1, 1, $hPen)
    EndIf
    Next
    Next
    EndFunc ;==>renderImage
    Func isElement(ByRef $a, ByRef $b)
    Local $x = 0, $x2, $y = 0, $n
    For $n = 0 To $Itterations
    $x2 = $x * $x - $y * $y + $a
    $y = 2 * $x * $y + $b
    $x = $x2
    if ($x * $x + $y * $y) > 4 Then
    Return $n
    EndIf
    Next
    Return -1
    EndFunc ;==>isElement

    [/autoit]

    Wie ihr sehen könnt, relativ groß ^^, die BMP Datei ist 126 MB groß, habe sie hier mal gezipt hochgeladen.

    Hier noch der Auszug aus Scite:

    [autoit]

    +>16:07:13 AU3Check ended.rc:0
    >Running:(3.3.6.1):C:\Program Files (x86)\AutoIt3\autoit3_x64.exe "C:\Dropbox\Mandelbrotmenge.au3"
    +>00:56:05 AutoIT3.exe ended.rc:0
    >Exit code: 0 Time: 31733.303

    [/autoit]


    Vielleicht geht es ja noch was schneller als 9 Stunden ;)

    Hier noch das Bild bei RapidShare, da es hier für den Dateianhang zu groß ist:
    https://rapidshare.com/files/3872488076/7680x4320.rar

    Ich lasse es demnächst mal in einer noch höheren Auflösung durchlaufen, vielleicht 30k *30k, falls jemand Interesse hat.

  • Habe mal einen einfachen OpenCl-Kernel gebastelt....

    Gedrückte linke Maustaste zoomt rein an der Mauscursorposition
    Gedrückte rechte Maustaste zoomt raus an der Mauscursorposition

    Wer mag, kann diesen Kernel stark beschleunigen (vor allem für die CPU), wenn er die SIMD-Variante (SSE) benutzt, siehe Tunnelflug-Script
    Mit den Farben spielen ist erlaubt^^

    Da ich nur einfache Genauigkeit (float) verwende, wird es beim reinzoomen schon schnell pixelig...wird Zeit für double!

    Spoiler anzeigen
    [autoit]

    #include <opencl_easy.au3>
    #include <GUIConstantsEx.au3>
    #include <WinAPI.au3>
    #include <GDIConstants.au3>
    #include <Misc.au3>

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

    ;Apfelmännchen einfachst in OpenCl

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

    ;ich bin nicht der C-Freak, bitte also um Nachsicht mit meinen Kernels, Verbesserungsvorschläge werden selbstverständlich umgesetzt^^
    ;wer mag, kann den Kernel auch gerne als SIMD-Variante machen, das beschleunigt die Berechnung auf der CPU enorm!
    Global $KernelSource = "__kernel void tunnelflug( __global int* output, const unsigned int width,const unsigned int height,const float centerx,const float centery,const uint maxiter,const float apfelbreite)" & @CRLF & _
    "{" & @CRLF & _
    "uint iter;" & @CRLF & _;lokale variablen
    "float xtemp;" & @CRLF & _
    "uint threadid = get_global_id(0);" & @CRLF & _ ;thread-id, d.h. jedes pixel von 1 bis b*h
    "float py = threadid/height;" & @CRLF & _ ;pixelkoordinaten
    "float px = threadid%width;" & @CRLF & _ ;pixelkoordinaten
    "float x0 = (centerx-apfelbreite*0.5f)+px*apfelbreite/width;" & @CRLF & _ ;...Berechnungen
    "float y0 = (centery-apfelbreite*0.5f)+py*apfelbreite/height;" & @CRLF & _ ;...Berechnungen
    "float x = x0;" & @CRLF & _
    "float y = y0;" & @CRLF & _
    "for (iter=0;iter<maxiter;iter++)" & @CRLF & _ ;...Berechnungen
    "{" & @CRLF & _ ;...Berechnungen
    "xtemp = x*x -y*y + x0;" & @CRLF & _
    "y = 2 * x * y + y0;" & @CRLF & _
    "x = xtemp;" & @CRLF & _
    "if ((x * x + y * y) > 4) break;" & @CRLF & _
    "}" & @CRLF & _ ;...Berechnungen
    "output[threadid] = 0x0008002* native_log2(native_log2((float) iter+1));" & @CRLF & _ ;Pixel schreiben 0xFFBBGGRR, BB=GG=RR=t native_powr(iter,2)
    "}"
    ;mit den farbfunktionen kann man endlos spielen^^

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

    ;pow(iter + 1 - log2 (log2 (x*x+y*y)/log(4))/log(2),3)
    ;

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

    ;Daten
    global $width = 256 * 3 ;breite und höhe der gui/grafik, die Anzahl der "Threads" sollte ein Vielfaches von 16 sein!
    global $height = 256 * 3
    Global $dll = DllOpen("user32.dll")
    Global $mouseflag = 1
    global $fps = 0

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

    Opt("GUIOnEventMode", 1)
    Opt("MouseCoordMode", 2)

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

    $hgui = GUICreate("Apfelmännchen", $width, $height, 1, 1) ;GUI erstellen
    $hdc_gui = _WinAPI_GetDC($hgui) ;HDC holen zum blitten
    GUISetState()
    GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
    GUISetOnEvent($GUI_EVENT_PRIMARYDOWN, "_mousedown")
    GUISetOnEvent($GUI_EVENT_SECONDARYDOWN, "_mousedown")
    AdlibRegister("_fps", 1000) ;FramesPerSecond

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

    ;bitmap erzeugen, in $ptr_bitmap steht nach dem Funktionsaufruf der Pointer auf die Pixeldaten
    Local $ptr_bitmap, $hbmp_bitmap ;byref
    $hDC_bitmap = _CreateNewBmp32($width, $height, $ptr_bitmap, $hbmp_bitmap) ;DC, Pointer auf die Bitmapdaten und ein Handle für GDI+....eine eierlegende Wollmilchsau

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

    Global $DATA_SIZE = $width * $height ;bildgröße=anzahl pixel (rgba)
    Global $NULL = 0

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

    ;ab hier gehts mit OpenCl los
    ;was wird gebraucht?
    ;Buffer für Output der Daten in der Größe der Bitmap, also

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

    ;Zunächst werden die verfügbaren Geräte(Devices) gesucht.
    _CL_GetDevice("ALL") ;..oder CPU oder GPU, ermittelt das Gerät, auf dem die Berechnungen durchgeführt werden.

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

    ;Puffer für die Bitmap
    $output_buffer = DllStructCreate("dword[" & $DATA_SIZE & "]", $ptr_bitmap);halleluja! bitmaps werden immer 16byte-aligned!
    $CL_buffer_out = _CL_CreateBuffer($output_buffer)

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

    Global $maxiteration = 89 ;maximale iterationen
    Global $centerx = -.5 ;mitte Bild in x-richtung
    Global $centery = 0 ;mitte bild in y-richtung
    Global $apfelbreite = 3 ;breite des Bildinhalts x-richtung

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

    ;Parameter an den Kernel übergeben
    _CL_SetArg(0, "ptr*", $CL_buffer_out)
    _CL_SetArg(1, "uint*", $width)
    _CL_SetArg(2, "uint*", $height)
    _CL_SetArg(3, "float*", $centerx)
    _CL_SetArg(4, "float*", $centery)
    _CL_SetArg(5, "uint*", $maxiteration)
    _CL_SetArg(6, "float*", $apfelbreite)

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

    $CL_DEBUGFLAG = 0 ;Debug-Ausgabe ausschalten, um Geschwindigkeit zu erhöhen
    ;Schleife zur Anzeige für den Flug
    While 1 ;endless loop...
    _steuerung()
    _CL_SetArg(3, "float*", $centerx)
    _CL_SetArg(4, "float*", $centery)
    _CL_SetArg(5, "uint*", $maxiteration)
    _CL_SetArg(6, "float*", $apfelbreite)
    _CL_RunKernel($DATA_SIZE, 0) ;Kernel ausführen, da im Kernel SIMD verwendet wird, nur 1/4 der Threads nötig!
    _CL_ReadBuffer($CL_buffer_out, $output_buffer);Puffer(alle Pixel) lesen
    _WinAPI_BitBlt($hdc_gui, 0, 0, $width, $height, $hDC_bitmap, 0, 0, $srccopy) ;Bitmap in die GUI blitten
    $fps += 1 ;Frames pro Sekunde zählen
    WEnd

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

    ;...das wars schon^^

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

    Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe DC und ptr und handle auf die Bitmapdaten
    ;by Andy
    Local $hcdc = _WinAPI_CreateCompatibleDC(0) ;Desktop-Kompatiblen DeviceContext erstellen lassen
    Local $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
    Local $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', $DIB_RGB_COLORS, '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][/autoit] [autoit]

    Func _fps()
    WinSetTitle($hgui, "", "Apfelmännchen " & $fps & " FPS")
    $fps = 0
    EndFunc ;==>_fps

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

    Func _Exit()
    Exit
    EndFunc ;==>_Exit

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

    Func _mousedown()
    $mouseflag = 1
    EndFunc ;==>_mousedown

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

    Func _steuerung()
    If WinActive("Apfelmännchen") Then
    If _IsPressed("01", $dll) Then _leftmouse()
    If _IsPressed("02", $dll) Then _rightmouse()
    EndIf
    EndFunc ;==>_steuerung

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

    Func _leftmouse() ;zoom in
    If $mouseflag Then ;neues center
    $posx = MouseGetPos(0)
    $posy = MouseGetPos(1)
    $centerx = ($centerx - $apfelbreite / 2) + $posx * $apfelbreite / $width
    $centery = ($centery - $apfelbreite / 2) + $posy * $apfelbreite / $height
    $mouseflag = 0
    EndIf
    $maxiteration /= 0.99785 ;Iterationen erhöhen
    $apfelbreite *= 0.985
    EndFunc ;==>_leftmouse

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

    Func _rightmouse() ;zoom out
    If $mouseflag Then ;neues center
    $posx = MouseGetPos(0)
    $posy = MouseGetPos(1)
    $centerx = ($centerx - $apfelbreite / 2) + $posx * $apfelbreite / $width
    $centery = ($centery - $apfelbreite / 2) + $posy * $apfelbreite / $height
    $mouseflag = 0
    EndIf
    $maxiteration *= 0.99785 ;Iterationen erhöhen
    $apfelbreite /= 0.985
    EndFunc ;==>_rightmouse

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