Lissajous-Figuren Zeichnen

  • C#

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

    Ich habs schon hinbekommen, ganz hilflos bin ich nicht. ;)
    Hier der Code, ich musste einfach bei höherer Frequenz mehr Punkte berechnen:
    Spoiler anzeigen

    C#-Quellcode

    1. public class LissajousPlotter : Control
    2. {
    3. private struct PlottedPoint
    4. {
    5. public PointF Point;
    6. public double Time;
    7. }
    8. readonly List<PlottedPoint> plottedPoints;
    9. readonly object locker;
    10. Thread updateThread;
    11. Thread drawingThread;
    12. bool loopRunning;
    13. public double Frequency1 { get; set; }
    14. public double Frequency2 { get; set; }
    15. public double Phi { get; set; }
    16. public LissajousPlotter()
    17. {
    18. SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
    19. SetStyle(ControlStyles.ResizeRedraw, true);
    20. UpdateStyles();
    21. plottedPoints = new List<PlottedPoint>();
    22. loopRunning = false;
    23. locker = new object();
    24. }
    25. public void Start()
    26. {
    27. if (!loopRunning)
    28. {
    29. loopRunning = true;
    30. updateThread = new Thread(UpdateLoop);
    31. updateThread.IsBackground = true;
    32. updateThread.Start();
    33. drawingThread = new Thread(DrawingLoop);
    34. drawingThread.IsBackground = true;
    35. drawingThread.Start();
    36. }
    37. }
    38. public void Stop()
    39. {
    40. if (loopRunning)
    41. {
    42. loopRunning = false;
    43. updateThread.Join();
    44. drawingThread.Join();
    45. plottedPoints.Clear();
    46. }
    47. }
    48. private static PlottedPoint Plot(double frequency1, double frequency2, double phi, double t)
    49. {
    50. float x = (float)Math.Sin(2 * Math.PI * frequency1 * t + phi);
    51. float y = (float)Math.Sin(2 * Math.PI * frequency2 * t);
    52. return new PlottedPoint() { Point = new PointF(x, y), Time = t };
    53. }
    54. private static int CalculateInterpolationPointCount(double frequency1, double frequency2)
    55. {
    56. double frequency = Math.Max(frequency1, frequency2);
    57. return Math.Max(1, (int)(frequency / 10));
    58. }
    59. private void UpdateLoop()
    60. {
    61. double oldTime = 0;
    62. var sw = new Stopwatch();
    63. sw.Start();
    64. while (loopRunning)
    65. {
    66. double time = sw.Elapsed.TotalSeconds;
    67. int pointCount = CalculateInterpolationPointCount(Frequency1, Frequency2);
    68. for (int i = 0; i < pointCount; i++)
    69. {
    70. PlottedPoint p = Plot(Frequency1, Frequency2, Phi, oldTime + i * (time - oldTime) / pointCount);
    71. lock (locker) plottedPoints.Add(p);
    72. }
    73. lock (locker)
    74. {
    75. for (int i = 0; i < plottedPoints.Count; i++)
    76. {
    77. if (plottedPoints[i].Time >= time - 0.1)
    78. {
    79. plottedPoints.RemoveRange(0, i);
    80. break;
    81. }
    82. }
    83. }
    84. oldTime = time;
    85. Thread.Sleep(1);
    86. }
    87. sw.Stop();
    88. }
    89. private void DrawingLoop()
    90. {
    91. var invalidateAction = new Action(Invalidate);
    92. while (loopRunning)
    93. {
    94. BeginInvoke(invalidateAction);
    95. Thread.Sleep(1);
    96. }
    97. }
    98. protected override void OnPaint(PaintEventArgs e)
    99. {
    100. var g = e.Graphics;
    101. g.SmoothingMode = SmoothingMode.AntiAlias;
    102. g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    103. PointF[] points;
    104. lock (locker) points = plottedPoints.Select(p => p.Point).ToArray();
    105. if (points.Length < 2)
    106. return;
    107. using (var matrix = new Matrix())
    108. {
    109. matrix.Translate(Width / 2.0f, Height / 2.0f);
    110. float scale = Math.Min(Width / 4.0f, Height / 4.0f);
    111. matrix.Scale(scale, -scale);
    112. g.Transform = matrix;
    113. using (var pen = new Pen(Color.Lime, 0.02f))
    114. g.DrawLines(pen, points);
    115. g.ResetTransform();
    116. }
    117. }
    118. }

    Passt das jetzt alles so von der Mathematik her? Wenn ja, wie kommen dann jetzt die bewegten Figuren zustande?
    Ich merke gerade, auf Wikipedia steht unter dem Bild "bei einem Frequenz-Verhältnis von fast genau 2:3" und tatsächlich, wenn ich 200,1 zu 300 nehme, dann siehts zwar von der Form her wie 200 zu 300 aus, aber es bewegt sich. Es reichen also minimale Abweichungen aus, um den Effekt zu erzeugen, und in der echten Welt kann man unmöglich 100% exakt sein. Das wäre also geklärt.

    Artentus schrieb:

    kann man unmöglich 100% exakt sein

    Wie der Wikipedia-Artikel schon andeutet: Genau dieser Effekt wurde in Vorzeiten bei Fernsehern und noch heute bei anderen Röhrengeräten eingesetzt, um die Frequenz eines Schwingungskreises GENAU auf die einer Referenzquelle abzustimmen: Stehendes Bild bedeutet erfolgreichen Abgleich.

    @Artentus
    Starkes Control, die erreichbaren Formen sind einfach schön, vielen Dank.
    @Artentus:: Vielleicht machst Du Dir eine List<Point> oder so, in der immer eine gewisse Anzahl von Punkten drin sind, wenn Du dann hinten einen neuen Punkt dranhängst, kannst Du den ersten löschen (Memory-Effekt).
    So stellst Du sicher, dann nur n Punkte gemalt werden und Dir die Kurve nicht den Screen zumalt.
    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!
    @us4711
    Danke.
    Die Figuren sind in der Tat sehr schön, deswegen hab ichs ja auch programmiert (der praktische Nutzen geht ja eigentlich gegen 0).

    @RodFromGermany
    So mache ich es schon. Die Schwierigkeit besteht nur darin, die korrekte "Nachleuchtzeit" zu finden, dass es so aussieht wie auf dem Oszilloskop. Im Moment hab ichs auf 20ms gesetzt, was ganz gut funktioniert (fällt mir auch gerade auf, hier im Code sinds noch 100ms, wers benutzen will, der sollte in Zeile 95 0.1 durch 0.02 ersetzen).