n-body Simulation

  • Hi zusammen,

    die n-body Simulation hat mich schon immer interessiert, zufällig hatte die c´t-Redaktion in einer der letzten Ausgaben einen Bericht zum Thema.
    Allerdings haben die Jungs dort nicht in AutoIt auf einem Mono-Core getestet, sondern sind mit diversen Compileroptionen einem Xeon E5 2690, einem Opteron 6380 und einer Tesla K20X / Xeon Phi auf die Pelle gerückt 8o, die Dinger haben über den dicken Daumen 60 Prozessoren ;(
    Der Bericht ist jedenfalls lesenswert, gerade die C/C++-Fraktion bekommt wieder Prügel....(wem bspw. der Unterschied eines Array of Structures AoS zu einer Structure of Arrays SoA etwas sagt, der weiss auch warum...dafür gibts nämlich keine C/C++-interne Umorganisierung, welche mal eben den Faktor 2 in der Geschwindigkeit ausmacht, egal auf welchem Prozessor)

    Genug geschwafelt, zum Script:
    Da AutoIt alles andere als schnell ist, habe ich das Script schon für diverse Optimierungen vorgesehen 8o
    So werden keine Arrays verwendet, um die Koordinaten und die Geschwindigkeiten der Körper zu speichern, sondern Structs. Auf die kann man dann ohne das Schript umzubauen, bspw mit diversen Dll´s (C/C++ und Basic-Freaks, ich zähl auf euch!!!) oder mit asm-Code oder per OpenCL zugreifen.

    Der Code ist recht einfach, es wird die Anziehungskraft (Gravitation) zwischen allen beteiligten Körpern berechnet und daraus für jeden Körper die resultierende Bewegung ermittelt.
    Ich habe im Code überall die 3D-Darstellung mitgezogen, für die "Optik" aber nur 2D angezeigt, die z-Ebene ist also 0. Wer das ändern möchte, kommentiert einfach die entsprechende Zeile in der inneren Schleife aus, dann ist es "realistischer" :rofl:

    Da ich die GGEngine-UDF vom Kollegen TheShadowAE viel zu schade finde um sie versauern zu lassen, habe ich damit die "Grafik" erstellt.
    Natürlich wäre "richtiges" GDI+ auch gegangen, aber spätestens, wenn jemand per Shaderprogramm und OpenGL loslegen will, ist damit sowieso Schluss^^

    Los gehts mit 4 Körpern, bis zu 50 kann man noch mit AutoIt verarbeiten (Variable $particles), sollen es 10000 sein, ist Gehirnschmalz gefragt :thumbup:
    Je nach Geschwindigkeit und Ballung der Körper kann es sein, dass einer der Kandidaten aus dem Universum geschossen wird...c´est la vie... 8) bei mehr als 30 Körpern ist das aber auch unerheblich, ggf. einfach nochmal neu starten.
    Um nur die Körper, ohne den Kometenschweif, anzeigen zu lassen, muss der Parameter $cleanflag auf 1 gesetzt werden.

    Spoiler anzeigen
    [autoit]

    ;Formeln
    ;Kraft = Masse * Beschleunigung
    ; F = m * a ; Masse m=1 für alle Partikel gleich
    ;(1) F = a
    ;
    ;Beschleunigung = Geschwindigkeit / Zeit
    ; a = V / t ;Zeit t = 1 sec Intervall
    ; a = V
    ;
    ;Geschwindigkeit = Weg / Zeit
    ; V = s / t ;Zeit t = 1 sec Intervall
    ; V = s
    ;
    ; => wenn alle Massen = 1 und das Intervall t=1 dann
    ; ist der zurückgelegte Weg s so groß wie die
    ; resultierende aller Anziehungskräfte F

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

    #include <GGEngine.au3>
    ;#include <assembleit.au3>

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

    Global $w = 800 ;Fenstergröße
    Global $h = $w
    Global $ausgleich = 1E-3 ;falls mehrere partikel dieselben parameter haben
    Global $t = 1 ;Zeit
    Global $max = 0

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

    ;***********************************************************************
    Global $particles = 4 ;anzahl partikel
    Global $cleanflag = 0 ;0=weg zeigen, 1= nur pixel
    ;***********************************************************************

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

    _GGinit()
    $gui = _GGGUIcreate("n-body", $w, $h)
    $ptr1 = _GGGUIgetIMGptr($gui)

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

    $bmp_struct = DllStructCreate("dword[" & $w * $h & "]", $ptr1) ;bilddaten
    $leer_struct = DllStructCreate("char[" & $w * $w * 4 & "]", $ptr1) ;"doublebuffer"
    $leerstring = ""
    For $i = 1 To $w * $w * 4 ;
    $leerstring &= Chr(0)
    Next

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

    $col_struct = DllStructCreate("int[" & $particles & "]") ;Farben

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

    ;struct of arrays ist wesentlich schneller als array of structs (später mit asm-SIMD oder c++)
    $x_struct = DllStructCreate("float [" & $particles & "]") ;Koordinaten
    $y_struct = DllStructCreate("float [" & $particles & "]") ;
    $z_struct = DllStructCreate("float [" & $particles & "]") ;
    $vx_struct = DllStructCreate("float [" & $particles & "]") ;Geschwindigkeiten
    $vy_struct = DllStructCreate("float [" & $particles & "]") ;
    $vz_struct = DllStructCreate("float [" & $particles & "]") ;

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

    For $i = 1 To $particles
    DllStructSetData($x_struct, 1, Random($w / 4, $w * 3 / 4), $i) ;koordinaten mit zufälligen floats füllen
    DllStructSetData($y_struct, 1, Random($w / 4, $w * 3 / 4), $i) ;
    DllStructSetData($z_struct, 1, Random($w / 4, $w * 3 / 4), $i) ;
    ;~ DllStructSetData($vx_struct, 1, Random(-.051, .051), $i) ;geschwindigkeitsvektoren mit zufälligen floats füllen
    ;~ DllStructSetData($vy_struct, 1, Random(-.051, .051), $i) ;
    ;~ DllStructSetData($vz_struct, 1, Random(-.051, .051), $i) ;

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

    DllStructSetData($col_struct, 1, 0xFF000000 + Random(0, 0xFFFFFF, 1), $i) ;zufällige Farben
    Next

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

    ;vorbelegung als Demo für 2 Partikel
    DllStructSetData($vx_struct, 1, 0, 1) ;geschwindigkeitsvektoren mit zufälligen floats füllen
    DllStructSetData($vy_struct, 1, -.05, 1) ;

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

    DllStructSetData($vx_struct, 1, 0, 2) ;geschwindigkeitsvektoren mit zufälligen floats füllen
    DllStructSetData($vy_struct, 1, .05, 2) ;

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

    While Not _GGGUIclosed($gui)
    _nbody($particles) ;koordinaten berechnen
    _pic() ;pixel anzeigen
    WEnd

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

    _GGGUIdelete($gui)
    _GGfree()

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

    Func _nBody($particles)

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

    For $i = 1 To $particles ;jeder partikel
    $x = DllStructGetData($x_struct, 1, $i) ;x-koordinate
    $y = DllStructGetData($y_struct, 1, $i) ;y-koordinate
    $z = DllStructGetData($z_struct, 1, $i) ;z-koordinate
    $Fx = 0
    $Fy = 0
    $Fz = 0
    $vxi = 0
    $vyi = 0
    $vzi = 0
    For $j = 1 To $particles ;hat wechselwirkung mit jedem anderen Partikel
    $xj = DllStructGetData($x_struct, 1, $j) ;x-koordinate
    $yj = DllStructGetData($y_struct, 1, $j) ;y-koordinate
    $zj = DllStructGetData($z_struct, 1, $j) ;z-koordinate
    $dx = $xj - $x ;abstand zw. den einzelnen Partikeln
    $dy = $yj - $y
    $dz = $zj - $z
    ;$sqr = $dx * $dx + $dy * $dy + $dz * $dz + $ausgleich ;ausgleich, um 0 zu vermeiden 3D
    $sqr = $dx * $dx + $dy * $dy + $ausgleich ;ausgleich, um 0 zu vermeiden 2D
    $1sqr = 1 / ($sqr * Sqrt($sqr))
    If $1sqr > .01 Then $1sqr = .01 ;Anziehungskräfte begrenzen

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

    $Fx += $dx * $1sqr ;Gravitationskräfte addieren
    $Fy += $dy * $1sqr
    $Fz += $dz * $1sqr
    Next

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

    $vxi += $Fx * $t ;Geschwindigkeiten addieren
    $vyi += $Fy * $t
    $vzi += $Fz * $t

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

    $vx = DllStructGetData($vx_struct, 1, $i) ;V=s => s=V
    $vy = DllStructGetData($vy_struct, 1, $i) ;
    $vz = DllStructGetData($vz_struct, 1, $i) ;

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

    $v1 = $vx + $vxi ;neue Koordinate= alte Koordinate + Weg
    $v2 = $vy + $vyi
    $v3 = $vz + $vzi

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

    ;~ If $v1 > $max Then
    ;~ ConsoleWrite($v1 & @CRLF)
    ;~ $max = $v1
    ;~ EndIf

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

    ;~ if abs($v1)>.4 then $v1=$vx
    ;~ if abs($v2)>.4 then $v2=$vy
    ;~ if abs($v3)>.4 then $v3=$vz

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

    DllStructSetData($vx_struct, 1, $v1, $i) ;koordinaten eintragen
    DllStructSetData($vy_struct, 1, $v2, $i) ;
    DllStructSetData($vz_struct, 1, $v3, $i) ;

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

    Next

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

    EndFunc ;==>_nBody

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

    Func _pic() ;schreibt die koordinaten in die Bitmap
    If $cleanflag Then DllStructSetData($leer_struct, 1, $leerstring) ;hintergrund leeren

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

    For $i = 1 To $particles
    $x = DllStructGetData($x_struct, 1, $i) ;x-koordinate
    $y = DllStructGetData($y_struct, 1, $i) ;y-koordinate
    $z = DllStructGetData($z_struct, 1, $i) ;z-koordinate

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

    $vx = DllStructGetData($vx_struct, 1, $i) ;Geschwindigkeit = zurückgelegter Weg
    $vy = DllStructGetData($vy_struct, 1, $i) ;vy
    $vz = DllStructGetData($vz_struct, 1, $i) ;vz

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

    DllStructSetData($x_struct, 1, $x + $vx, $i);neue Koordinaten
    DllStructSetData($y_struct, 1, $y + $vy, $i)
    DllStructSetData($z_struct, 1, $z + $vz, $i);

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

    $col = DllStructGetData($col_struct, 1, $i) ;Farbe holen
    DllStructSetData($bmp_struct, 1, $col, Int($y) * $w + Int($x)) ;pixel setzen
    Next

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

    _GGGUIupdate($gui)
    EndFunc ;==>_pic

    [/autoit]

    TheShadowAE´s UDF
    autoit.de/wcf/attachment/21537/

  • Zitat

    Nicht nur mathematisch sondern auch ästhetisch ansprechend .

    naja, für die ansprechende Ästhetik musste ich mathematisch ein wenig schummeln^^
    die Zeile

    [autoit]

    If $1sqr > .01 Then $1sqr = .01 ;Anziehungskräfte begrenzen

    [/autoit]

    gehört da eigentlich nicht rein, aber es entsteht folgendes Problem. Wenn die Körper sehr dicht aneinander sind, beschleunigen sie sich gegenseitig so stark, dass die Geschwindigkeit so hoch wird, dass sie sich gegenseitig "wegschiessen". D.h. die Geschwindigkeit ist so hoch, dass sie auch nicht von der Gravitation der anderen Körper beeinflusst wird.

    Der andere Fall tritt auf, wenn die Gravitation zu stark wird. Dann bilden sich schnell Gruppen, welche sich mit anderen Gruppen durch die gegenseitige Anziehung verbinden, bis schlußendlich nur noch ein Punkt übrig bleibt.

  • Buphx: sieht aus wie die klassische Ansicht von Elektronen, die um den Atomkern fliegen.


    Andy: klasse Ding! Ein Antialiasing würde die Ästhetik aufpeppen! ;)

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Interessant ist auf jeden Fall, wie sich nur wenige, d.h. 3-6 Körper, abhängig voneinander verhalten.

    Btw hätte ich nie gedacht, dass es sich jemand antut 400 Körper mit diesem langsamen Script aufeinander loszulassen. :D

    Zitat von name22

    Jetzt bekomme ich Lust das Ganze mit 10.000+ Partikeln auf meiner GraKa zu simulieren . Ich muss mich wirklich mal mit OpenCL auseinandersetzen, das könnte dabei ganz nützlich sein.

    na dann los^^
    Als Vorlage sollten die Beispiele im OpenCl-Tut ja ausreichen. Das Grundgerüst ist ja identisch, lediglich die Parameter und der Kernel müssen angepasst werden.
    Und wenn jemand schon den Kernel erstellt, dann ist es ein Leichtes, den C/C++-Code in eine Dll zu überführen, so könnte man auch div. Compiler und Compilerschalter vergleichen!

    Und dann fängt das Optimieren an, sicher lässt sich einiges mit Hilfe des superschnellen lokalen Speichers auf der Graka beschleunigen.

    Ich werde mich mal an asm-SIMD mittels SSE versuchen, wer Lust hat, kann ja jeweils 16er-Gruppen von Körpern per einem AVX-Register gleichzeitig berechnen!
    Und da ja Floats berechnet werden, könnte jemand die immer noch im Prozessor vorhandene FPU verwenden :rolleyes:
    Möglichkeiten gibts viele..... ;)

  • Spoiler anzeigen
    [autoit]


    ;--Einstellungen-------------------------------------
    $Durchgaenge = 200
    $Berechnungen_pro_Durchgang = 10000
    $RandomVON = 1
    $RandomBIS = 1
    ;----------------------------------------------------
    Dim $aA[$Berechnungen_pro_Durchgang]
    Dim $aB[$Berechnungen_pro_Durchgang]
    Dim $aC[$Berechnungen_pro_Durchgang]
    Global $DiffC = 0, $DiffB = 0

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

    For $o = 1 To $Durchgaenge
    For $i = 0 To UBound($aA) - 1
    $aA[$i] = Random($RandomVON , $RandomBIS, 1)
    Next

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

    $timerB = TimerInit()
    For $i = 0 To UBound($aA) - 1
    $aB[$i] = $aA[$i] * $aA[$i]
    Next
    $DiffB += TimerDiff($timerB)

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

    $timerC = TimerInit()
    For $i = 0 To UBound($aA) - 1
    $aC[$i] = $aA[$i] ^ 2
    Next
    $DiffC += TimerDiff($timerC)
    Next

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

    ConsoleWrite("DIFF_B(Mal sich selber) = " & $DiffB/$Durchgaenge & " ; DIFF_C(Hoch 2) = " & $DiffC/$Durchgaenge & " ; ")

    [/autoit]

    Ich kann bei OpenCL und ASm nicht viel beitragen aber bei mir ist
    ^2 schneller als mal sich selbst, wie du es teilweise im Script verwendest.

    DIFF_B = 7.44313371159946 ; DIFF_C = 6.05605812516962

  • Hier mal meine Version von dem Ganzen in GDI+
    (Hab nicht bei Andy abgeschaut, sondern mich nur inspirieren lassen :P)
    Ist aber komplett in AutoIt. mit ASM geht da also nix

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <Array.au3>
    Opt('GUIOnEventMode', 1)
    Opt('MustDeclareVars', 1)

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

    ; p = Dichte
    ; PI = 3.1415926536
    ; r = Radius
    ; m = Masse = 4/3*PI*r³*p
    ; d(m) = Durchmesser(Masse) = 2*(3/(4*PI)*(m/p))^(1/3)
    ; G = Gravitationskonstante = 6.67384 * 10^(-11)
    ; F(r) = Anziehungskraft(Abstand) = G*m1*m2/r²

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

    Global Const $VierDrittelPi = ATan(1) * 16 / 3
    Global Const $DreiDurchVierPi = 3 / (ATan(1) * 16)
    Global Const $Gravi = 6.67384 * 10 ^ (-11)

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

    Global $Dichte = 10^10 ; Dichte der Materie (Größer -> Kleinere Partikel) in Kilogramm / m³
    Global $GesamtMasse = 10^13 ; Gesamte Masse in unserem Universum in Kilogramm
    Global $AnzahlPartikel = 25 ; Anzahl Brocken auf die die Masse aufgeteilt wird
    Global $ZentralMasse = 10^10 ; Masse im Zentrum
    Global $iW = 768, $iH = 768, $iD = ($iH ^ 2 + $iW ^ 2) ^ 0.5 ; Größe des Fensters
    Global $aPartikel[$AnzahlPartikel][7]

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

    ; [0] - x Koord
    ; [1] - y Koord
    ; [2] - z Koord
    ; [3] - vX
    ; [4] - vY
    ; [5] - vZ
    ; [6] - Masse

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

    _GDIPlus_Startup()

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

    Global $hGUI, $hDC, $hGFX, $hIMG, $hBUF, $hBRU, $hBRU2
    $hGUI = GUICreate('Test', $iW, $iH)
    $hDC = _WinAPI_GetDC($hGUI)
    $hIMG = _Image_Create($iW, $iH)
    $hBUF = DllStructGetData($hIMG, 1, 1)
    $hGFX = _GDIPlus_GraphicsCreateFromHDC($hBUF)
    $hBRU = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
    $hBRU2 = _GDIPlus_BrushCreateSolid(0x20000000)
    _GDIPlus_GraphicsSetSmoothingMode($hGFX, 4)

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

    _Load()

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

    GUISetOnEvent(-3, 'EVENT', $hGUI)
    OnAutoItExitRegister('_Freigeben')
    GUIRegisterMsg(0xF, 'WM_PAINT')
    GUISetState(@SW_SHOW, $hGUI)

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

    While Sleep(10)
    _Run()
    WEnd

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

    Func _Run()
    Local Static $c = 0
    $c += 1
    _Calc()
    If IsInt($c/2) Then
    _Draw()
    WM_PAINT()
    EndIf
    EndFunc ;==>_Run

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

    Func _Calc()
    Local $aTMP
    For $i = $AnzahlPartikel - 1 To 0 Step - 1
    For $e = 0 To $AnzahlPartikel - 1 Step 1
    _GetVector($i, 0, 0, 0)
    If $i = $e Then ContinueLoop
    _GetVector($i, $e)
    Next
    Next
    For $i = $AnzahlPartikel - 1 To 0 Step - 1
    If $aPartikel[$i][6] = 1 Then
    _ArrayDelete($aPartikel, $i)
    $AnzahlPartikel -= 1
    ContinueLoop
    EndIf
    $aPartikel[$i][0] += $aPartikel[$i][3]
    $aPartikel[$i][1] += $aPartikel[$i][4]
    $aPartikel[$i][2] += $aPartikel[$i][5]
    Next
    EndFunc ;==>_Calc

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

    Func _Draw()
    Local $d
    _GDIPlus_GraphicsFillRect($hGFX, 0, 0, $iW, $iH, $hBRU2)
    For $i = 0 To $AnzahlPartikel - 1 Step 1
    $d = 2 * ($DreiDurchVierPi * $aPartikel[$i][6] / $Dichte)^(1/3)
    DllCall($ghGDIPDll, "int", "GdipFillEllipse", "handle", $hGFX, "handle", $hBRU, "float", $aPartikel[$i][0]-$d/2 + $iW/2, "float", $aPartikel[$i][1]-$d/2 + $iH/2, "float", $d, "float", $d)
    Next

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

    $d = 2 * ($DreiDurchVierPi * $ZentralMasse / $Dichte)^(1/3)
    DllCall($ghGDIPDll, "int", "GdipFillEllipse", "handle", $hGFX, "handle", $hBRU, "float", $iW/2-$d/2, "float", $iH/2-$d/2, "float", $d, "float", $d)
    EndFunc ;==>_Draw

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

    Func _GetVector($i, $e, $y = 1, $z = 1)
    Local $a[3], $L, $F, $d, $mGes
    If $e = 0 And $y = 0 And $z = 0 Then ; Unbewegliches Massezentrum
    $a[0] = $e - $aPartikel[$i][0]
    $a[1] = $y - $aPartikel[$i][1]
    $a[2] = $z - $aPartikel[$i][2]
    $L = ($a[0] ^ 2 + $a[1] ^ 2 + $a[2] ^ 2) ^ 0.5 ; Abstand der Partikel (Betrag des Vektors)
    $F = $Gravi * $aPartikel[$i][6] * $ZentralMasse / $L ^ 3 ; Direkt um L vermindert
    $d = ($DreiDurchVierPi * $aPartikel[$i][6] / $Dichte)^(1/3) + ($DreiDurchVierPi * $ZentralMasse / $Dichte)^(1/3); Durchmesser
    If $L > $iD/32 Then
    For $o = 0 To 2 Step 1
    $aPartikel[$i][$o + 3] += $a[$o] * $F / $aPartikel[$i][6]
    Next
    EndIf
    Else
    For $o = 0 To 2 Step 1
    $a[$o] = $aPartikel[$e][$o] - $aPartikel[$i][$o]
    Next
    $L = ($a[0] ^ 2 + $a[1] ^ 2 + $a[2] ^ 2) ^ 0.5 ; Abstand der Partikel (Betrag des Vektors)
    $F = $Gravi * $aPartikel[$i][6] * $aPartikel[$e][6] / $L ^ 3 ; Direkt um L vermindert
    $d = ($DreiDurchVierPi * $aPartikel[$i][6] / $Dichte)^(1/3) + ($DreiDurchVierPi * $aPartikel[$e][6] / $Dichte)^(1/3); Durchmesser
    If $d > $L Then
    $mGes = $aPartikel[$i][6] + $aPartikel[$e][6]
    For $o = 0 To 2 Step 1
    $aPartikel[$i][$o] = ($aPartikel[$i][$o]*$aPartikel[$i][6] + $aPartikel[$e][$o]*$aPartikel[$e][6])/$mGes
    $aPartikel[$i][$o+3] = ($aPartikel[$i][$o+3]*$aPartikel[$i][6] + $aPartikel[$e][$o+3]*$aPartikel[$e][6])/$mGes
    Next
    $aPartikel[$i][6] = $mGes
    $aPartikel[$e][6] = 1
    Else
    For $o = 0 To 2 Step 1
    $aPartikel[$i][$o + 3] += $a[$o] * $F / $aPartikel[$i][6]
    Next
    EndIf
    EndIf
    EndFunc ;==>_GetVector

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

    Func _Load()
    Local $aTMP
    For $i = 0 To $AnzahlPartikel - 1 Step 1
    $aTMP = _3DRandom(-$iD / 4, $iD / 4)
    $aPartikel[$i][0] = $aTMP[0]
    $aPartikel[$i][1] = $aTMP[1]
    $aPartikel[$i][2] = $aTMP[2]
    $aPartikel[$i][3] = Random(-1, 1)
    $aPartikel[$i][4] = Random(-1, 1)
    $aPartikel[$i][5] = Random(-1, 1)
    $aPartikel[$i][6] = $GesamtMasse / $AnzahlPartikel
    Next
    EndFunc ;==>_Load

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

    Func _3DRandom($Min, $Max)
    Local $x, $y, $z
    Do
    $x = Random($Min, $Max)
    $y = Random($Min, $Max)
    $z = Random($Min, $Max)
    Until ($x ^ 2 + $y ^ 2 + $z ^ 2) ^ 0.5 < $Max ; Liegt der Vektor in der Sphere ?
    Local $a[3] = [$x, $y, $z]
    Return $a
    EndFunc ;==>_3DRandom

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

    Func WM_PAINT()
    _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hBUF, 0, 0, 0xCC0020)
    EndFunc ;==>WM_PAINT

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

    Func EVENT()
    Switch @GUI_CtrlId
    Case -3
    Exit
    EndSwitch
    EndFunc ;==>EVENT

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

    Func _Freigeben()
    _GDIPlus_BrushDispose($hBRU2)
    _GDIPlus_BrushDispose($hBRU)
    _GDIPlus_GraphicsDispose($hGFX)
    _WinAPI_ReleaseDC($hGUI, $hDC)
    _Image_Delete($hIMG)
    _GDIPlus_Shutdown()
    EndFunc ;==>_Freigeben

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

    Func _Image_Create($iW, $iH)
    Local $Ptr, $hDC, $hBmp, $tBMI, $aDIB, $vStruct
    $hDC = _WinAPI_CreateCompatibleDC(0)
    $tBMI = DllStructCreate($tagBITMAPINFO)
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4)
    DllStructSetData($tBMI, "Width", $iW)
    DllStructSetData($tBMI, "Height", -$iH)
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)
    $aDIB = DllCall('GDI32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hBmp = $aDIB[0]
    $Ptr = $aDIB[4]
    _WinAPI_SelectObject($hDC, $hBmp)
    $vStruct = DllStructCreate('int[5]')
    DllStructSetData($vStruct, 1, $hDC, 1)
    DllStructSetData($vStruct, 1, $iW, 2)
    DllStructSetData($vStruct, 1, $iH, 3)
    DllStructSetData($vStruct, 1, $Ptr, 4)
    DllStructSetData($vStruct, 1, $hBmp, 5)
    Return $vStruct
    EndFunc ;==>_Image_Create

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

    Func _Image_Delete(ByRef $vStruct)
    _WinAPI_DeleteObject(DllStructGetData($vStruct, 1, 5))
    _WinAPI_DeleteDC(DllStructGetData($vStruct, 1, 1))
    $vStruct = 0
    EndFunc ;==>_Image_Delete

    [/autoit]
  • :thumbup:
    Made my day 8)

    [autoit]

    Global $GesamtMasse = 10^13 ; Gesamte Masse in unserem Universum in Kilogramm
    Global $AnzahlPartikel = 25 ; Anzahl Brocken auf die die Masse aufgeteilt wird
    Global $ZentralMasse = 10^10 ; Masse im Zentrum

    [/autoit]

    :rock:

  • Ich lasse gerade Andy´s Script mit 1000 und 2000 laufen ;) nachher gibt es die Screens :thumbup:

    Edit: Auffallend ist, das erst alle Bewegungen in die Mitte gehen und nach ungefähr der gleichen Zeit, alle wieder nach außen gehen... egal wie viele Partikel ;)

    autoit.de/wcf/attachment/21557/
    autoit.de/wcf/attachment/21562/
    autoit.de/wcf/attachment/21567/
    autoit.de/wcf/attachment/21572/

  • Super Skript mal wieder Andy :thumbup: Ich werd mich auch mal an ein von denen setzen (ASM, C++, CL). Vermutlich C++.
    Aber ein Mangel hab ich dann doch ^^ es crasht bei cleanflag=1 (einfach Absturz)
    Und ein langsames ausfaden oder ein größeren Punkt wäre grafisch noch sehr nice gewesen ^^ Da ist GDI+ dann teilweise doch besser und schneller.. ^^

    Kommt meinem KometPlanet ja fast ähnlich (hust, bei mir sind die Planeten festgewachsen)

  • Die GDI+ Version mit aufpolierten Farben und ohne Clearscreen:

    Skript
    [autoit]

    #include <GDIPlus.au3>
    #include <Array.au3>
    Opt('GUIOnEventMode', 1)
    Opt('MustDeclareVars', 1)

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

    ; p = Dichte
    ; PI = 3.1415926536
    ; r = Radius
    ; m = Masse = 4/3*PI*r³*p
    ; d(m) = Durchmesser(Masse) = 2*(3/(4*PI)*(m/p))^(1/3)
    ; G = Gravitationskonstante = 6.67384 * 10^(-11)
    ; F(r) = Anziehungskraft(Abstand) = G*m1*m2/r²

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

    Global Const $VierDrittelPi = ATan(1) * 16 / 3
    Global Const $DreiDurchVierPi = 3 / (ATan(1) * 16)
    Global Const $Gravi = 6.67384 * 10 ^ (-11)

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

    Global $Dichte = 10^10 ; Dichte der Materie (Größer -> Kleinere Partikel) in Kilogramm / m³
    Global $GesamtMasse = 10^13 ; Gesamte Masse in unserem Universum in Kilogramm
    Global $AnzahlPartikel = 20 ; Anzahl Brocken auf die die Masse aufgeteilt wird
    Global $ZentralMasse = 10^9 ; Masse im Zentrum
    Global $iW = 768, $iH = 768, $iD = ($iH ^ 2 + $iW ^ 2) ^ 0.5 ; Größe des Fensters
    Global $aPartikel[$AnzahlPartikel][7], $aBRU[$AnzahlPartikel]

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

    ; [0] - x Koord
    ; [1] - y Koord
    ; [2] - z Koord
    ; [3] - vX
    ; [4] - vY
    ; [5] - vZ
    ; [6] - Masse

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

    _GDIPlus_Startup()

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

    Global $hGUI, $hDC, $hGFX, $hIMG, $hBUF, $hBRU, $hBRU2
    $hGUI = GUICreate('Test', $iW, $iH)
    $hDC = _WinAPI_GetDC($hGUI)
    $hIMG = _Image_Create($iW, $iH)
    $hBUF = DllStructGetData($hIMG, 1, 1)
    $hGFX = _GDIPlus_GraphicsCreateFromHDC($hBUF)
    $hBRU = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
    $hBRU2 = _GDIPlus_BrushCreateSolid(0x01000000)
    _GDIPlus_GraphicsSetSmoothingMode($hGFX, 4)

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

    _Load()

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

    GUISetOnEvent(-3, 'EVENT', $hGUI)
    OnAutoItExitRegister('_Freigeben')
    GUIRegisterMsg(0xF, 'WM_PAINT')
    GUISetState(@SW_SHOW, $hGUI)

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

    While Sleep(10)
    _Run()
    WEnd

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

    Func _Run()
    Local Static $c = 0
    $c += 1
    _Calc()
    ;~ If IsInt($c/5) Then
    _Draw()
    WM_PAINT()
    ;~ EndIf
    ToolTip('Runde: ' & $c & @CRLF & 'Partikel: ' & $AnzahlPartikel)
    EndFunc ;==>_Run

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

    Func _Calc()
    Local $aTMP
    For $i = $AnzahlPartikel - 1 To 0 Step - 1
    ;~ Sleep(20)
    For $e = 0 To $AnzahlPartikel - 1 Step 1
    _GetVector($i, 0, 0, 0)
    If $i = $e Then ContinueLoop
    _GetVector($i, $e)
    Next
    Next
    For $i = $AnzahlPartikel - 1 To 0 Step - 1
    If $aPartikel[$i][6] = 1 Then
    _ArrayDelete($aPartikel, $i)
    _GDIPlus_BrushDispose($aBRU[$i])
    _ArrayDelete($aBRU, $i)
    $AnzahlPartikel -= 1
    ContinueLoop
    EndIf
    $aPartikel[$i][0] += $aPartikel[$i][3]
    $aPartikel[$i][1] += $aPartikel[$i][4]
    $aPartikel[$i][2] += $aPartikel[$i][5]
    Next
    EndFunc ;==>_Calc

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

    Func _Draw()
    Local $d
    ;~ _GDIPlus_GraphicsFillRect($hGFX, 0, 0, $iW, $iH, $hBRU2)
    For $i = 0 To $AnzahlPartikel - 1 Step 1
    $d = 2 * ($DreiDurchVierPi * $aPartikel[$i][6] / $Dichte)^(1/3)
    DllCall($ghGDIPDll, "int", "GdipFillEllipse", "handle", $hGFX, "handle", $aBRU[$i], "float", $aPartikel[$i][0]-$d/2 + $iW/2, "float", $aPartikel[$i][1]-$d/2 + $iH/2, "float", $d, "float", $d)
    Next
    EndFunc ;==>_Draw

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

    Func _GetVector($i, $e, $y = 1, $z = 1)
    Local $a[3], $L, $F, $d, $mGes
    If $e = 0 And $y = 0 And $z = 0 Then ; Unbewegliches Massezentrum
    $a[0] = $e - $aPartikel[$i][0]
    $a[1] = $y - $aPartikel[$i][1]
    $a[2] = $z - $aPartikel[$i][2]
    $L = ($a[0] ^ 2 + $a[1] ^ 2 + $a[2] ^ 2) ^ 0.5 ; Abstand der Partikel (Betrag des Vektors)
    $F = $Gravi * $aPartikel[$i][6] * $ZentralMasse / $L ^ 3 ; Direkt um L vermindert
    $d = ($DreiDurchVierPi * $aPartikel[$i][6] / $Dichte)^(1/3) + ($DreiDurchVierPi * $ZentralMasse / $Dichte)^(1/3); Durchmesser
    If $L > $iD/32 Then
    For $o = 0 To 2 Step 1
    $aPartikel[$i][$o + 3] += $a[$o] * $F / $aPartikel[$i][6]
    Next
    EndIf
    Else
    For $o = 0 To 2 Step 1
    $a[$o] = $aPartikel[$e][$o] - $aPartikel[$i][$o]
    Next
    $L = ($a[0] ^ 2 + $a[1] ^ 2 + $a[2] ^ 2) ^ 0.5 ; Abstand der Partikel (Betrag des Vektors)
    $F = $Gravi * $aPartikel[$i][6] * $aPartikel[$e][6] / $L ^ 3 ; Direkt um L vermindert
    $d = ($DreiDurchVierPi * $aPartikel[$i][6] / $Dichte)^(1/3) + ($DreiDurchVierPi * $aPartikel[$e][6] / $Dichte)^(1/3); Durchmesser
    If $d > $L Then
    $mGes = $aPartikel[$i][6] + $aPartikel[$e][6]
    For $o = 0 To 2 Step 1
    $aPartikel[$i][$o] = ($aPartikel[$i][$o]*$aPartikel[$i][6] + $aPartikel[$e][$o]*$aPartikel[$e][6])/$mGes
    $aPartikel[$i][$o+3] = ($aPartikel[$i][$o+3]*$aPartikel[$i][6] + $aPartikel[$e][$o+3]*$aPartikel[$e][6])/$mGes
    Next
    $aPartikel[$i][6] = $mGes
    $aPartikel[$e][6] = 1
    Else
    For $o = 0 To 2 Step 1
    $aPartikel[$i][$o + 3] += $a[$o] * $F / $aPartikel[$i][6]
    Next
    EndIf
    EndIf
    EndFunc ;==>_GetVector

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

    Func _Load()
    Local $aTMP
    For $i = 0 To $AnzahlPartikel - 1 Step 1
    $aTMP = _3DRandom(-$iD / 4, $iD / 4)
    $aPartikel[$i][0] = $aTMP[0]
    $aPartikel[$i][1] = $aTMP[1]
    $aPartikel[$i][2] = $aTMP[2]
    $aPartikel[$i][3] = Random(-1, 1)
    $aPartikel[$i][4] = Random(-1, 1)
    $aPartikel[$i][5] = Random(-1, 1)
    $aPartikel[$i][6] = $GesamtMasse / $AnzahlPartikel
    $aTMP = _3DRandom(0, 255) ; Auch für Farben wunderbar geeignet.
    $aBRU[$i] = _GDIPlus_BrushCreateSolid('0xFF' & Hex(Int(Round($aTMP[0],0)),2) & Hex(Int(Round($aTMP[1],0)),2) & Hex(Int(Round($aTMP[2],0)),2))
    Next
    Local $d = 2 * ($DreiDurchVierPi * $ZentralMasse / $Dichte)^(1/3)
    DllCall($ghGDIPDll, "int", "GdipFillEllipse", "handle", $hGFX, "handle", $hBRU, "float", $iW/2-$d/2, "float", $iH/2-$d/2, "float", $d, "float", $d)

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

    EndFunc ;==>_Load

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

    Func _3DRandom($Min, $Max)
    Local $x, $y, $z
    Do
    $x = Random($Min, $Max)
    $y = Random($Min, $Max)
    $z = Random($Min, $Max)
    Until ($x ^ 2 + $y ^ 2 + $z ^ 2) ^ 0.5 < $Max ; Liegt der Vektor in der Sphere ?
    Local $a[3] = [$x, $y, $z]
    Return $a
    EndFunc ;==>_3DRandom

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

    Func WM_PAINT()
    _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hBUF, 0, 0, 0xCC0020)
    EndFunc ;==>WM_PAINT

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

    Func EVENT()
    Switch @GUI_CtrlId
    Case -3
    Exit
    EndSwitch
    EndFunc ;==>EVENT

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

    Func _Freigeben()
    _GDIPlus_BrushDispose($hBRU2)
    For $i = 0 To $AnzahlPartikel - 1 Step 1
    _GDIPlus_BrushDispose($aBRU[$i])
    Next
    _GDIPlus_BrushDispose($hBRU)
    _GDIPlus_GraphicsDispose($hGFX)
    _WinAPI_ReleaseDC($hGUI, $hDC)
    _Image_Delete($hIMG)
    _GDIPlus_Shutdown()
    EndFunc ;==>_Freigeben

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

    Func _Image_Create($iW, $iH)
    Local $Ptr, $hDC, $hBmp, $tBMI, $aDIB, $vStruct
    $hDC = _WinAPI_CreateCompatibleDC(0)
    $tBMI = DllStructCreate($tagBITMAPINFO)
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4)
    DllStructSetData($tBMI, "Width", $iW)
    DllStructSetData($tBMI, "Height", -$iH)
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)
    $aDIB = DllCall('GDI32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hBmp = $aDIB[0]
    $Ptr = $aDIB[4]
    _WinAPI_SelectObject($hDC, $hBmp)
    $vStruct = DllStructCreate('int[5]')
    DllStructSetData($vStruct, 1, $hDC, 1)
    DllStructSetData($vStruct, 1, $iW, 2)
    DllStructSetData($vStruct, 1, $iH, 3)
    DllStructSetData($vStruct, 1, $Ptr, 4)
    DllStructSetData($vStruct, 1, $hBmp, 5)
    Return $vStruct
    EndFunc ;==>_Image_Create

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

    Func _Image_Delete(ByRef $vStruct)
    _WinAPI_DeleteObject(DllStructGetData($vStruct, 1, 5))
    _WinAPI_DeleteDC(DllStructGetData($vStruct, 1, 1))
    $vStruct = 0
    EndFunc ;==>_Image_Delete

    [/autoit]

    (Nicht dass man denkt, das könnte meine Version nicht :P)

  • Hey,
    habe heute Morgen in Informatik mal angefangen eine FreeBasic Dll dafür zu schreiben ;)
    Da ich nicht fertig geworden bin, hab ich das natürlich mit nach Haus genommen und eben (trotz super Wetter) das Ganze endlich halbwegs fertig gemacht.
    Ich räum das Script noch mal ein wenig auf und stell es dann hier rein ;)

    Lg Ali
    Edit: Anhang hinzugefügt 8)
    Wenn ihr Parameter ändern möchtet, müsst ihr die auch im Dll-Code ändern und die Dll dann neu compilieren.
    Neue Version, jetzt muss man nurnoch bei änderung der Auflösung die Dll bearbeiten (Typen können nicht Dynamisch sein).
    Bild ist nach etwa 1 Minute.

    Code:

    AutoIt Code
    [autoit]

    #include "GGEngine.au3"
    OnAutoItExitRegister("_Exit")
    Global $w = 800 ;Fenstergröße
    Global $h = $w ;Werte müssen in Dll angepasst werden!
    ;***********************************************************************
    Global $ausgleich = 1E-3 ;falls mehrere partikel dieselben parameter haben
    Global $t = 1 ;Zeit
    Global $max = .01 ;Geschwindigkeits multiplikator
    Global $particles = 40 ;anzahl partikel
    ;Global $cleanflag = 0 ;0=weg zeigen, 1= nur pixel
    ;***********************************************************************

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

    _GGinit()
    $gui = _GGGUIcreate("n-body", $w, $h)
    $hgui = WinGetHandle("n-body")
    $ptr1 = _GGGUIgetIMGptr($gui)
    $bmp_struct = DllStructCreate("dword b[" & $w * $h & "]", $ptr1) ;bilddaten

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

    $hDll = DllOpen("N-Body.dll")
    DllCall($hDll, "none", "Create", "uint", $particles, "double", $t, "double", $ausgleich, "double", $max)

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

    While Not _GGGUIclosed($gui)
    $hT1 = TimerInit()
    DllCall($hDll, "none", "NBody")
    $T1 = TimerDiff($hT1)
    $hT2 = TimerInit()
    DllCall($hDll, "none", "Pic", "ptr", DllStructGetPtr($bmp_struct))
    $T2 = TimerDiff($hT2)
    $hT3 = TimerInit()
    _GGGUIupdate($gui)
    $T3 = TimerDiff($hT3)

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

    WinSetTitle($hgui, "", "N-Body:" & Round($T1, 2) & "ms" & " " & "Pic:" & Round($T2, 2) & "ms" & " " & "Render:" & Round($T3, 2) & "ms")
    ;~ Sleep(1)
    WEnd

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

    Func _Exit()
    DllClose($hDll)
    _GGGUIdelete($gui)
    _GGfree()
    EndFunc

    [/autoit]
    Freebasic Code