Punktewolke darstellen

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Punktewolke darstellen

    Hallo zusammen!

    Ich habe mal eine grundsätzliche Frage zum grafischen Darstellen von Punkten. Ich habe eine Ascii-Datei mit bis zu über 1 Mio. Punkte mit jeweils x,y,z-Koordinaten und einen Grauwert. Diese Punkte möchte ich gern - zumindest in 2D - grafisch in einer Form darstellen, natürlich mit den Grauwerten, so dass ein "buntes" Bild entsteht.

    Weiß jemand von euch ob es in VB.NET überhaupt möglich ist und wenn ja mit welchem Tool?

    Vielen Dank für eure Hilfe!

    Gruß
    Poste doch bitte mal den Inhalt der Datei für die ersten 20 Punkte.
    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!
    hier ein Auszug aus der Ascii-Datei mit der Reihenfolge x,y,z und Grauwert. Die letzten beiden Spalten sind weitere Werte, die ich für spätere Berechnungen brauche. Getrennt sind die Spalten durch Leerzeichen.


    -0.000 0.280 -0.769 101 19.989 0.818
    -0.000 0.284 -0.765 105 20.349 0.816
    -0.000 0.288 -0.761 88 20.709 0.813
    -0.000 0.285 -0.739 68 21.069 0.792
    -0.000 0.287 -0.732 89 21.429 0.787
    -0.000 0.291 -0.728 100 21.790 0.784
    -0.000 0.295 -0.725 152 22.150 0.782
    -0.000 0.302 -0.729 118 22.510 0.789
    -0.000 0.311 -0.737 106 22.870 0.800
    -0.000 0.318 -0.742 107 23.230 0.807
    -0.000 0.325 -0.743 124 23.590 0.811
    -0.000 0.329 -0.740 155 23.950 0.810
    -0.000 0.333 -0.737 191 24.310 0.809
    -0.000 0.339 -0.737 153 24.670 0.811
    -0.000 0.344 -0.737 125 25.030 0.813
    Sieh Dir mal das an:
    Form mit Button1 und PictureBox1
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Structure MyPoints
    3. Dim x As Single
    4. Dim y As Single
    5. Dim z As Single
    6. Dim col As Color
    7. End Structure
    8. Private AllPoints As New List(Of MyPoints)
    9. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    10. Dim pt As MyPoints
    11. Dim lines() As String = System.IO.File.ReadAllLines("C:\Temp\test\new.txt") ' Dein Pfad
    12. Dim c As Integer
    13. For Each line In lines
    14. line = line.Replace(".", ",")
    15. Dim parts() As String = line.Split(" "c)
    16. pt.x = Single.Parse(parts(0))
    17. pt.y = Single.Parse(parts(1))
    18. pt.z = Single.Parse(parts(2))
    19. c = Integer.Parse(parts(3))
    20. pt.col = Color.FromArgb(c, c, c)
    21. AllPoints.Add(pt)
    22. Next
    23. PictureBox1.Invalidate()
    24. End Sub
    25. Private Sub PictureBox1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    26. Dim g As Graphics = e.Graphics
    27. Dim x As Integer
    28. Dim y As Integer
    29. For Each pt In AllPoints
    30. x = CInt(pt.y * 300) ' Strewckung anpassen
    31. y = CInt((pt.z + 1) * 300) ' Offset anpassen
    32. g.DrawRectangle(New Pen(pt.col), New Rectangle(x, y, 1, 1))
    33. Next
    34. End Sub
    35. End Class
    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!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ () aus folgendem Grund: Fehler korrigiert @FreakJNS:

    Danke für die Tipps!

    @RodFromGermany: Das sieht immerhin schon "bunt" aus. Punkte kann man zufälligerweiße nicht zeichnen?
    Ich tüftel mal etwas damit rum. Vielleicht bekomm ich das noch in eine entsprechende Form - mit x-,y-Achse zur Beschriftung und anständige Punktgrößen.

    @ErfinderdesRades: Die Software sieht recht vielversprechend aus. Mal schaun was sich damit anfangen lässt.
    @RodFromGermany
    New Rectangle(x, y, x + 1, y + 1)
    x+1 und y+1 ist hier glaubich nicht gewollt^^ nur 1 bei beiden einsetzten:

    New Rectangle(x, y, 1, 1)

    ansonsten werden die "Punkte" immer größer je weiter sie vom Ursprung wegsind xD (C&P-Bremse detektiert?)
    Ich hätt's so gemacht: (Button und Panel der Größe 400x400)

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim Points As New List(Of Point3D)
    3. Private Structure Point3D
    4. Dim X, Y, Z As Single
    5. Dim Color As Color
    6. End Structure
    7. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    8. Using sr As New StreamReader("C:\test.txt")
    9. Do While sr.Peek <> -1
    10. Dim t As String = sr.ReadLine.Replace(".", ",")
    11. Points.Add(
    12. New Point3D With {
    13. .X = CSng(t.Split(" "c)(0)),
    14. .Y = CSng(t.Split(" "c)(1)),
    15. .Z = CSng(t.Split(" "c)(2)),
    16. .Color = Color.FromArgb(255, CInt(t.Split(" "c)(3)), CInt(t.Split(" "c)(3)), CInt(t.Split(" "c)(3)))
    17. })
    18. Loop
    19. End Using
    20. Panel1.Refresh()
    21. End Sub
    22. Private Sub Panel1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
    23. If Points.Count > 0 Then
    24. Dim scaling As Integer = 400 'Eine Einheit sind 400 Pixel
    25. Dim center As New Point(Panel1.Width \ 2 - 150, Panel1.Height \ 2 + 150) 'Koordinatenursprung, bisschen nach links unten verschoben
    26. 'Achsen zeichnen
    27. e.Graphics.DrawLine(Pens.Green, center, New Point(0, Panel1.Height)) 'X-Achse
    28. e.Graphics.DrawLine(Pens.Green, center, New Point(Panel1.Width, center.Y)) 'Y-Achse
    29. e.Graphics.DrawLine(Pens.Green, center, New Point(center.X, 0)) 'Z-Achse
    30. 'Punkte zeichnen
    31. For Each p As Point3D In Points
    32. Dim x As Integer = center.X + CInt((p.Y - (p.X * scaling / 2)) * scaling)
    33. Dim y As Integer = center.Y + CInt((p.Z + (p.X * scaling / 2)) * scaling)
    34. e.Graphics.DrawRectangle(New Pen(p.Color), New Rectangle(x, y, 1, 1))
    35. Next
    36. End If
    37. End Sub
    38. End Class


    Wobei du halt experimentieren musst, wo du den Koordinatenursprung/Nullpunkt setzt, und wie du die Skalierung machst. Das hängt ja vor allem vom Wertebereich der Punkte ab. Übrigens sind die Achsen nur schnell "reingehackt". Vor allem bei der X-Achse musst du dir noch etwas überlegen, die wird nicht immer richtig gezeichnet

    Skybird schrieb:

    Das sind ja Ubisoftmethoden hier !

    tina83 schrieb:

    Punkte kann man zufälligerweiße nicht zeichnen?

    GDI ist Vektorgrafik, da gibt es keine Punkte. Wenn Du einen hättest und vergrößertest das Bild um den Faktor 10 in x und y, würde der Punbkt nicht mit vergfrößert werden. Deswegen ein kleines Rechteck.
    Wenn Du Punkte haben willst, musst Du Dich mit Pixelgrafik beschäftigen, in der Bitmap-Klasse gibt es ein SetPixel(x, y, color)
    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!
    gute Idee, in diesem Falle die Zeichnung in einer Bitmap auszuführen.
    Weil bei 1 Mio Punkte wird normales OwnerDrawing zu langsam werden. Da müsste man vor dem Zeichnungsvorgang die PunkteWolke analysieren und alle verdeckten Punkte vom Zeichnen ausnehmen, und dann ist immer noch zu lahm.
    Eine Bitmap ist zunächst auch grauenhaft lahm, aber ist sie erstmal fertiggestellt macht die Anzeige keine Probleme mehr.
    Weiters kann man die Erstellgeschwindigkeit einer Bitmap exorbitant optimieren - aber bitte erst im 2. Schritt.
    Hi
    es gibt auch noch BufferedGraphics. Diese verwende ich meistens, wenns darum geht schneller zu zeichnen:

    VB.NET-Quellcode

    1. Dim bg As BufferedGraphics = BufferedGraphicsManager.Current.Allocate(myGraphics, someBounds)
    2. 'auf bg.Graphics zeichnen
    3. bg.Render()
    4. bg.Dispose()


    Wäre es eigentlich nicht FillRectangle, statt DrawRectangle?

    Gruß
    ~blaze~
    BufferedGraphics bringt imo niemals ieinen Vorteil gegenüber richtig angewandtem OwnerDrawing.
    Ob man nu seine Zeichnungen erst in eine BufferedGraphics ausführt, und die dann mittels .Render() auffordert, in die richtige Graphics zu zeichnen, oder ob man gleich in die richtige Graphics zeichnet....

    Übrigens, wenn beim normalem OwnerDrawing Doppelpufferung aktiviert ist, dann zeichnet dieselbe Zeichen-Routine ebenfalls in ein BufferedGraphics-Objekt, statt in die richtigen Graphics.
    Da ist im Hintergrund für gesorgt.
    Habe sowas mal ausprobiert, bei sovielen Punkten dauert das aufbauen des Bildes mehrere Sekunden (die Punkte müssen ja auch noch auf das Skalierte Raster gerechner werden, etc). Da würde ich auch auf eine Bitmap zurückgreifen und das Zeichnen in einem Thread/Backgroundworker auslagern.
    Alternativ eben auf Lockbits zurückgreifen - das dürfte auch so noch schnell genug sein, sodass man keinen Thread/BGW benötigt.

    Das einzelne Hinzufügen von Punkten kann man dann immer auf der gleichen Bitmap machen - nur zum entfernen muss man komplett neuzeichnen^^
    LockBits ist in diesem Fall vmtl. nicht angebracht. Es gäbe auch noch eine Möglichkeit, per hdc darauf zuzugreifen und anschließend die SetPixel für Gerätekontexte zu verwenden.

    @ErfinderDesRades: Probier das einfach mal aus. OptimizedDoubleBuffer auf True und anschließend noch ein weiteres BufferedGraphics oder OptimizedDoubleBuffer auf False. Bei mir kam ein überraschendes Ergebnis raus (ODP hat länger gebraucht, als BG). BufferedGraphics führt die Zeichnung afaik innerhalb bereits aus, nur wird der Präsentationsvorgang verlagert, ähnlich wie bei der Bitmap. Nur ist eine Bitmap halt kein Gerätekontext, sondern kann einen haben, ein BufferedGraphics ist eben schon ein Gerätekontext. Ich denke zumindest, dass es so intern gehandhabt wird.

    Gruß
    ~blaze~
    Ich hab mit so Sachen rumprobiert, ziemlich ausführlich.

    Doppelpufferung ist immer langsamer als ungepuffert. Einfach weil dasselbe 2 mal gezeichnet wird: erstmal in den Puffer, und dann nochmal ins eigentliche Ziel.
    Doppelpufferung verhindert flackern, wenn ein angefangener Zeichenvorgang abgebrochen wird, weil schon der nächste ZeichenVorgang anfängt.

    Deinen Versuchsaufbau verstehe ich nicht.
    Aber hiermit kann man die Zeichnungsgeschwindigkeit schön visualisieren (ckAutoRun checken).

    Also da könntest du jetzt was mit BufferedGraphics basteln, was dieselbe Zeichnung schneller vollführt.
    Bei meinen erneuten Tests eben hat BufferedGraphics besser abgeschnitten, soweit sich das sagen lässt. Verglichen habe ich dabei
    - OptimizedDoubleBuffer = True, Zeichnen auf e.Graphics
    - OptimizedDoubleBuffer = False, Zeichnen auf bg.Graphics + Render()
    jeweils UserPaint, AllPaintingInWmPaint und halt ResizeRedraw auf True.
    Gezeichnet wurden insgesamt ClientSize.Width * ClientSize.Height / 450 gefüllte Rechtecke, nachdem der Hintergrund auf die Form-Hintergrundfarbe gecleart wurde.

    Gruß
    ~blaze~
    Ok, ich habe das Programm noch mal umstrukturiert. Offenbar lag's vorhin an meiner idiotischen Art des Tests. Jetzt macht's auch wieder mehr Sinn, da sonst MS nen ziemlichen Mist zusammenprogrammiert hätte. Habe das Testprojekt trotzdem mal angehängt.

    Gruß
    ~blaze~
    Dateien
    naja, im Bereich von 0.001s kann man mit Stopwatch nicht mehr sinnvoll messen.
    Auch die Ausgabe in den TraceListener mag das Ergebnis schon wesentlich verfälschen.

    Also ich hab BufferedGraphics-Painting mal in mein Sample eingebaut:

    VB.NET-Quellcode

    1. Private WithEvents _Timer As New System.Timers.Timer(2000)
    2. Private _PaintCounter As Integer
    3. Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    4. _PaintCounter += 1
    5. MyBase.OnPaint(e)
    6. If ckShowClip.Checked Then
    7. ' ggfs. ClipBereich visualisieren
    8. ' e.Graphics.Clear() färbt nur den Clipbereich neu
    9. e.Graphics.Clear(ColorGenerator.GetNextColor)
    10. End If
    11. e.Graphics.SmoothingMode = SmoothingMode.HighQuality
    12. If ckBufferedGrahics.Checked Then
    13. Using bg As BufferedGraphics = BufferedGraphicsManager.Current.Allocate(e.Graphics, e.ClipRectangle)
    14. bg.Graphics.Clear(Me.BackColor)
    15. _Arrow.Draw(bg.Graphics)
    16. bg.Render()
    17. End Using
    18. Else
    19. _Arrow.Draw(e.Graphics)
    20. End If
    21. If ckAutoRun.Checked Then
    22. 'Ereigniskette: ApplyChanges() löst das nächste Paint-Ereignis aus
    23. 'So zeigt sich die maximale Zeichnungs-Geschwindigkeit
    24. With _Arrow
    25. .Rotation += 1
    26. .ApplyChanges()
    27. End With
    28. End If
    29. End Sub
    30. Private Sub _Timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles _Timer.Elapsed
    31. Console.WriteLine(_PaintCounter.ToString)
    32. _PaintCounter = 0
    33. End Sub
    34. Private Sub ckBufferedGrahics_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) Handles ckBufferedGrahics.CheckedChanged
    35. Console.WriteLine(String.Concat("BufferedGraphics=" & ckBufferedGrahics.Checked, ", countingTime=", _Timer.Interval, "ms"))
    36. End Sub
    Mit ckBufferedGrahics kann ich nun einstellen, ob normales OwnerDrawing oder mit BufferedGrahics

    VB.NET-Quellcode

    1. If ckBufferedGrahics.Checked Then
    2. Using bg As BufferedGraphics = BufferedGraphicsManager.Current.Allocate(e.Graphics, e.ClipRectangle)
    3. bg.Graphics.Clear(Me.BackColor)
    4. _Arrow.Draw(bg.Graphics)
    5. bg.Render()
    6. End Using
    7. Else
    8. _Arrow.Draw(e.Graphics)
    9. End If

    Die Ausgabe gibt alle 2 Sekunden aus, wie oft gepaintet wurde.

    Ausgabe schrieb:

    BufferedGraphics=False, countingTime=2000ms
    1
    0
    732
    BufferedGraphics=True, countingTime=2000ms
    1751
    1839
    1833
    1844
    1849
    BufferedGraphics=False, countingTime=2000ms
    1808
    1507
    1699
    1730
    1688
    1610
    1432
    1620
    1698
    BufferedGraphics=True, countingTime=2000ms
    1790
    1833
    1839
    1855
    1851
    BufferedGraphics=False, countingTime=2000ms
    1798
    1741
    1746
    1735
    1739
    1748
    Die erste Zahl jeweils nach einem Umschalten ist vmtl. fehlerbehaftet, aber die anneren sollten korrekt sein. Es zeigt sich, dass mit BufferedGraphics doch ein klein bischen schneller ist - 5% - 8% - hätte ich nicht gedacht.
    Aber nix, wofür ich mir ein Bein ausreißen würde ;)