Hopalong - Fraktal

    • VB.NET

    Es gibt 84 Antworten in diesem Thema. Der letzte Beitrag () ist von zn-gong.

      Zunächst einmal: NumericUpDown hat eine Property Value.

      RodFromGermany schrieb:

      Vielleicht ist es sinnvoll, Du lässt zunächst die Namen vom Designer stehen.
      Da muss ich nämlich nur 5 NUD raufziehen und fertig.
      Es ist etwas mühselig, den Controls immer noch Deine Namen geben zu müssen.
      Scheib wenigstens hin, was alles für Controls erforderlich sind, besser noch: Solange Du den Algorithmus als solchen bearbeitest, gib ihm Default-Werte, da ist nur ein Button erforderlich.
      Und die Color-Tabelle / Auswahl mach ganz zum Schluss, wenn alles läuft.
      Malen kannst Du auch auf der Form selbst, ohne PB.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Jop, das ist ein Fail von mir, ich hab einfach so Find&Replace-like gemacht... ^^

      Für den Algorithmus sind sechs Werte erforderlich, bspw. mit folgenden Werten:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Dim num As Integer = 100000 'Control: nud_iter --> Anzahl der Iterationen
      2. Dim a As Double = 0.4 'Control: nud_a --> Parameter 1
      3. Dim b As Double = 1 'Control: nud_b --> Parameter 2
      4. Dim c As Double = 0 'Control: nud_c --> Parameter 3
      5. Dim zoom As Double = 50 'Control: nud_zoom --> Vergrößerung (Faktor)
      6. Dim colorjump As Integer = 70 'Control: nud_colorjump --> Parameter 4

      Und der Algorithmus dazu (ohne extra Formeln für die Farbe):

      VB.NET-Quellcode

      1. For i As Integer = 1 To num
      2. If colorcounter > 255 Then colorcounter = 1
      3. Dim sb As New SolidBrush(Color.FromArgb(255 - colorcounter, colorcounter, CInt(127 + colorcounter / 2)))
      4. Dim ptx As Single = CSng(x * zoom + dist_hor) 'dist_hor = DrawingField.Width / 2
      5. Dim pty As Single = CSng(y * zoom + dist_ver) 'dist_ver = DrawingField.Height / 2
      6. graph.FillRectangle(sb, ptx, pty, 1, 1) 'Punkt malen
      7. Dim xx As Double = y - Math.Sign(x) * Math.Sqrt(Math.Abs(b * x - c))
      8. Dim yy As Double = a - x
      9. If Math.Abs(x - xx) < colorjump Then colorcounter += 1
      10. x = xx
      11. y = yy
      12. Next



      @RodFromGermany:
      Warum soll ich direkt auf die Form malen ? Was ist der Vorteil gegenüber einer PB / eines Panels ? Ich hab doch dann bei der PB direkt den Punkt 'um den herum gemalt wird' :)
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais

      ThePlexian schrieb:

      Warum soll ich direkt auf die Form malen ?
      Weil Du in der XXX.Paint malen sollst, die PB kannst Du nicht drehen, sondern nur ein Bild, und dem ist es egal, ob es in einer PB oder einer Form angezeigt wird.
      Momentan sehe ich gar nix. Wie kommt denn die Bitmap in die PB?
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Man das ist echt kompliziert :D
      Also auch wenn ich auf eine Bitmap zeichne mit Graphics.FromImage() logischerweise, soll ich das im Paint-Event machen ?

      RodFromGermany schrieb:

      Momentan sehe ich gar nix. Wie kommt denn die Bitmap in die PB?

      WTF :cursing: ich bin so dämlich, hab ich glatt mal vergessen die Bitmap auch zu Zeichnen :D

      Heißt das jetzt, ich soll im Paint-Event
      PictureBox1.Image = fractal
      schreiben oder
      e.Graphics.DrawImageUnscaledAndClipped(fractal, New Rectangle(0, 0, PictureBox1.Width, PictureBox1.Height))
      ?

      Und brauche ich noch ein Flag damit mit das nicht immer gezeichnet wird ?
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      Ich hab mal was versucht, so funktioniert die Live-Generierung bestens (ich generiere hier zwar kein Fraktal, der Effekt lässt sich aber nachempfinden):
      Code für FractalGenerator-Klasse

      VB.NET-Quellcode

      1. Imports System.Threading
      2. Public Class FractalGenerator
      3. Private _Image As Bitmap = Nothing
      4. Public Property Image As Bitmap
      5. Get
      6. Return _Image
      7. End Get
      8. Set(value As Bitmap)
      9. _Image = value
      10. End Set
      11. End Property
      12. Public Event FractalGenerationComplete()
      13. Public Function GenerateFractal(iterations As Integer, size As Size) As Bitmap
      14. 'Überprüfen, ob die übergebene Anzahl der Zyklen überhaupt möglich ist
      15. If Not iterations > 0 Then
      16. 'Ausnahme werfen
      17. Throw New ArgumentOutOfRangeException("cycles")
      18. End If
      19. 'Zufallsgenerator mit Seed, produziert immer dasselbe Bild bei gleicher Anzahl an Zyklen
      20. Dim RandomGenerator As New Random(iterations)
      21. 'Hier wird nachher reingerendert
      22. Dim RenderTarget As New Bitmap(size.Width, size.Height)
      23. 'Die Durchgänge durchlaufen
      24. For Counter As Integer = 1 To iterations
      25. 'Die Generierung des Fraktals in der aktuellen Iterationsstufe
      26. 'Ich habe sie Einfachheitshalber mal durch die Generierung eines Rechtecks ausgetauscht
      27. Dim Location As New Point(RandomGenerator.Next(0, size.Width - (size.Width \ 10)) - 5, RandomGenerator.Next(0, size.Height - (size.Height \ 10)) - 5)
      28. Dim Shape As New Size(RandomGenerator.Next(5, size.Width - Location.X), RandomGenerator.Next(5, size.Width - Location.X))
      29. Dim Rect As New Rectangle(Location, Shape)
      30. Dim LineColor As Color = Color.FromArgb(255, RandomGenerator.Next(64, 255), RandomGenerator.Next(64, 255), RandomGenerator.Next(64, 255))
      31. 'Rendervorgang
      32. Using g As Graphics = Graphics.FromImage(RenderTarget)
      33. 'Hier wird die momentane Iterationsstufe reingerendert
      34. g.DrawRectangle(New Pen(LineColor), Rect)
      35. End Using
      36. 'Bild wieder abspeichern
      37. _Image = CType(RenderTarget.Clone(), Bitmap)
      38. 'Etwas warten
      39. Thread.Sleep(3)
      40. Next
      41. 'Mitteilen, dass die Generation abgeschlossen wurde
      42. RaiseEvent FractalGenerationComplete()
      43. Return RenderTarget
      44. End Function
      45. End Class

      Code für Form

      VB.NET-Quellcode

      1. Imports System.Threading
      2. Public Class Form1
      3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      4. Dim RenderThread As New Thread(AddressOf StartFractalGeneration)
      5. RenderThread.Start()
      6. End Sub
      7. Private fc As New FractalGenerator
      8. Private Sub StartFractalGeneration()
      9. Me.Invoke(Sub() Timer1.Start())
      10. Dim Fractal As Bitmap = fc.GenerateFractal(CInt(Me.NumericUpDown1.Value), Me.PictureBox1.Size)
      11. Me.Invoke(Sub() PictureBox1.Image = Fractal)
      12. End Sub
      13. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
      14. If fc.Image IsNot Nothing Then
      15. PictureBox1.Image = fc.Image
      16. End If
      17. End Sub
      18. End Class

      Auf der Form befinden sich ein Button zum Starten der Generierung, eine Picturebox zum Anzeigen des Bildes und ein Timer für die Aktualisierung, dieser hat ein Intervall von 50. Der Code ist noch nicht fertig, er funktioniert zwar, jedoch stürzt das Programm in allen nicht standardmäßigen, durch den User hervorgerufenen Situationen ab. Dazu zählen beispielsweise das erneute Klicken des Buttons und das vorzeitige Beenden des Programmes. Außerdem wird der Timer nicht gestoppt. Trotztem denke ich, dass er dir, wenn du ein paar Anpassungen machst und die Bugs behebst, sicher weiterhilft. Außerdem verwende ich Threading, dadurch friert also die Form nicht ein.
      Hoffe, dass das dir etwas weiterhilft :).

      MfG Stefan
      Love U nafets :D

      Ich werde das mal einbauen und versuchen nachzuvollziehen :)
      Ich denke mal in der Form-Class deklarierst du Timer1 global ?
      Auf der Form, my fault :)
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      Allein diese Rechtecke sind epic :)

      Das behalte ich, danke ^^ :D


      @nafets3646:

      Ich habe jetzt mein Algorithmus eingebaut ein zwei Sachen geändert:

      Neue Property:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Private _iter As Integer
      2. Public ReadOnly Property iter As Integer
      3. Get
      4. Return _iter
      5. End Get
      6. End Property


      GenerateFractal Funktion:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Function GenerateFractal(iterations As Integer, size As Size) As Bitmap
      2. 'Überprüfen, ob die übergebene Anzahl der Zyklen überhaupt möglich ist
      3. If Not iterations > 0 Then
      4. 'Ausnahme werfen
      5. Throw New ArgumentOutOfRangeException("cycles")
      6. End If
      7. 'Hier wird nachher reingerendert
      8. Dim RenderTarget As New Bitmap(size.Width, size.Height)
      9. Using graph As Graphics = Graphics.FromImage(RenderTarget)
      10. graph.Clear(Color.Black)
      11. End Using
      12. Dim num As Integer = iterations 'Control: nud_iter --> Anzahl der Iterationen
      13. Dim a As Double = 0.4 'Control: nud_a --> Parameter 1
      14. Dim b As Double = 1 'Control: nud_b --> Parameter 2
      15. Dim c As Double = 0 'Control: nud_c --> Parameter 3
      16. Dim zoom As Double = 150 'Control: nud_zoom --> Vergrößerung (Faktor)
      17. Dim colorjump As Integer = 70 'Control: nud_colorjump --> Parameter 4
      18. Dim colorcounter As Integer = 0
      19. Dim dist_hor As Integer = Convert.ToInt32(size.Width / 2)
      20. Dim dist_ver As Integer = Convert.ToInt32(size.Height / 2)
      21. Dim x, y As Double
      22. 'Die Durchgänge durchlaufen
      23. For Counter As Integer = 1 To iterations
      24. _iter = Counter '!!!!!!
      25. If colorcounter > 255 Then colorcounter = 1
      26. Dim sb As New SolidBrush(Color.FromArgb(255 - colorcounter, colorcounter, CInt(127 + colorcounter / 2)))
      27. Dim ptx As Single = CSng(x * zoom + dist_hor) 'dist_hor = DrawingField.Width / 2
      28. Dim pty As Single = CSng(y * zoom + dist_ver) 'dist_ver = DrawingField.Height / 2
      29. 'Rendervorgang
      30. Using graph As Graphics = Graphics.FromImage(RenderTarget)
      31. graph.FillRectangle(sb, ptx, pty, 1, 1) 'Punkt malen
      32. End Using
      33. Dim xx As Double = y - Math.Sign(x) * Math.Sqrt(Math.Abs(b * x - c))
      34. Dim yy As Double = a - x
      35. If Math.Abs(x - xx) < colorjump Then colorcounter += 1
      36. x = xx
      37. y = yy
      38. 'Bild wieder abspeichern
      39. _Image = CType(RenderTarget.Clone(), Bitmap)
      40. 'Etwas warten
      41. 'Thread.Sleep(1)
      42. Next
      43. 'Mitteilen, dass die Generation abgeschlossen wurde
      44. RaiseEvent FractalGenerationComplete()
      45. Return RenderTarget
      46. End Function


      Und in der Form-Class:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
      2. If fc.Image IsNot Nothing Then
      3. PictureBox1.Image = fc.Image
      4. Label1.Text = fc.iter.ToString
      5. End If
      6. End Sub


      Natürlich muss jetzt auf der Form ein Label1 vorhanden sein.

      Bei mir stockt der Count etwas, aber bei iwelchen Randomwerten für _iter :/
      Habe ich iwas übersehen ?
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThePlexian“ ()

      @nafets3646:

      Vielen vielen Dank, ich habe das jetzt alles verstanden und einbauen können, aber es bleiben zwei Sachen:

      1. siehe Post #28
      2. Wozu ist das Event ? Es wird nicht genutzt, also gehe ich davon aus, dass es nur eingebaut wurde, um es vielleicht mal nutzen zu können oder ?
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      1. Für was ist die Variable _iter gut?
      2. Das Event war dazu gedacht, bei rechnerisch aufwändigeren Generationen eine direkte Weitergabe des Umstandes, dass das Bild sich geändert hat, weiterzugeben. Willst du diese Funktion nutzen, solltest du einfach in die Property Image anstatt in die Variable _Image schreiben. Dann wird das Event automatisch ausgelöst. Jedoch musst du dann darauf achten, dass du wenn nötig invokest.
      @nafets3646:

      Die Variable habe ich eingebaut, bzw. die Property, damit man auf der Form nicht nur die Entwicklung der Bitmap sehen kann, sondern auch die Anzahl an Iterationen.
      Also Property Image --> PictureBox - Bei jedem step der For aktualisiert
      Property Iter --> Label - Bei jedem step der For aktualisiert
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      @ThePlexian:: Vielleicht postest Du noch mal Deinen aktuellen Code, am besten die FormX.vb und die FormX.Designer.vb, da sind die Controls drin definiert.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      @RodFromGermany:
      Okay, hier sind die drei Dateien, ich denke mal die Namen verraten schon was was ist :)

      @nafets3646:
      Ja, denke schon, sind zumindest keine aufgetreten :)
      Dateien
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      Ja, die Dateien sind gut. :thumbup:
      Aber
      Kaum gestartet, schon abgestürzt.
      X und Y liegen im guten Bereich, steps ~ 1144 (schwankt bei verschiedenen Zoom-Werten).
      Hab da momentan keinen Plan.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      @RodFromGermany:

      Meinst du damit, dass das einfach die richtigen Dateien waren oder guter Code ist ?
      Vielleicht funktioniert das ja mit Graphics.FromImage(TargetBitmap).DrawRectangle(ptx, pty, 1, 1) besser ?

      Ich werde nachher mal die beiden Codes (meiner und nafets') vergleichen, es kann ja nicht sein, dass 100000 Rechtecke flüssiger gezeichnet werden als 100000 Punkte :)
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      @nafets3646:
      Okay danke schonmal :)

      Ich habe jetzt folgendes geändert:

      TargetBitmap.SetPixel(ptx, pty, col) durch setPixel(TargetBitmap, ptx, pty, col) erstezt, und folgende Sub hinzugefügt:

      VB.NET-Quellcode

      1. Private Sub setPixel(ByRef bmp As Bitmap, x As Integer, y As Integer, col As Color)
      2. ' Lock the bitmap's bits.
      3. Dim lockdata As Imaging.BitmapData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
      4. ' Declare an array to hold the bytes of the bitmap.
      5. ' This code is specific to a bitmap with 24 bits per pixels.
      6. Dim bytes As Integer = Math.Abs(lockdata.Stride) * bmp.Height
      7. Dim argbValues(Math.Abs(lockdata.Stride) * bmp.Height - 1) As Byte
      8. ' Copy the ARGB values into the array.
      9. System.Runtime.InteropServices.Marshal.Copy(lockdata.Scan0, argbValues, 0, bytes)
      10. Dim counter As Integer = (y * bmp.Width * 4) + (x * 4) '32bpp-Bitmap --> 4-Farbwerte
      11. argbValues(counter) = col.A
      12. argbValues(counter + 1) = col.R
      13. argbValues(counter + 2) = col.G
      14. argbValues(counter + 3) = col.B
      15. ' Copy the ARGB values back to the bitmap
      16. System.Runtime.InteropServices.Marshal.Copy(argbValues, 0, lockdata.Scan0, bytes)
      17. ' Unlock the bits.
      18. bmp.UnlockBits(lockdata)
      19. End Sub


      Funktioniert, ist aber leider nicht schneller :/
      Ich konnte aber feststellen (über ein Label, dass die aktuelle Iterationszahl anzeigt), dass es gleichmäßig verläuft :)) :thumbup:

      EDIT: Ich bezweifle aber auch das der Code gut ist, da ich noch nie mit LockBits gearbeitet habe, und wahrscheinlich die BitmapData falsch deklariert (mit falschen Argumenten) wird.
      »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
      ich muss ma dumm dazwischenfragen: Was versprecht ihr euch davon, das Hoppalong in eine Bitmap zu malen?
      Auffm Bildschirm sieht man nur, was inne Graphics eines Controls gemalt ist.

      Einzig vlt. schneller sind iwelche Apis, die direkt auffn Screen blitten, oder iwas mit DirectX oder sowas.

      ich weiß nicht - ein Hoppalong, wassich angeguckt hab, hat 100000 Punkte in glaub 4 Sekunden gemalt. Dassis doch schon recht enorm, oder?
      Also ich kann nicht so schnell gucken - muss ich auch garnet, weil mein Bildschirm auch nicht so schnell anzeigen kann.

      Sinnvoll zu zeigen sind vlt. 30 Bildwechsel/s oder sowas. Also man könnte das Gehoppel berechnen lassen, alle 20ms ein Standbild schießen, und anschließend als Film vorführen.
      Aber die Darstellung des einzelnen Punktes zu beschleunigen - seh ich nicht so viel Sinn drin - oder binnich auffm falschen Dampfer?