Via Trigonometrie Punkte auf Kreis malen

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Lingo.

    Via Trigonometrie Punkte auf Kreis malen

    Hey Communtiy.

    Ich habe eigentlich eine ganz einfache Frage, aber es funktoniert nicht -.-
    Ich möchte einen Kreis zeichnen, und auf diesen in gleichmäßigen Abständen 5 Punkte (kleine Kreise) zeichnen (--> 360° / 5 = 72°).

    VB.NET-Quellcode

    1. Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    2. Dim margin As Integer = 10 'Damit ein 10px Abstand zur Formborder besteht
    3. Dim rect As Rectangle = New Rectangle(margin, margin, Me.ClientSize.Width - 2 * margin, Me.ClientSize.Height - 2 * margin)
    4. Dim radius As Integer = CInt(rect.Width / 2)
    5. Using g As Graphics = e.Graphics
    6. g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    7. g.DrawEllipse(New Pen(Brushes.DarkGray, 3), rect) 'Circle
    8. For angle As Integer = 0 To 360 Step 72
    9. Dim a As Double = ToRadians(angle + 270) 'Damit der erste Punkt oben in der Mitte ist
    10. Dim center As New PointF(CSng(rect.Width / 2), CSng(rect.Height / 2))
    11. Dim pt As New PointF(center.X + radius * CSng(Math.Cos(a)), _
    12. center.Y + radius * CSng(Math.Sin(a)))
    13. g.FillEllipse(Brushes.Red, New RectangleF(pt.X, pt.Y, 5, 5))
    14. Next
    15. End Using
    16. End Sub
    17. Function ToRadians(deg As Double) As Double
    18. Return (2 * Math.PI) / 360 * deg
    19. End Function

    Leider sind die Punkte nicht auf dem Kreis -.-


    Btw verstehe ich nicht warum ich den Winkel in Bogenmaß umwandeln muss für die trig. Funktionen, aber ohne sieht es noch 'schlimmer' aus
    Bilder
    • Circle.JPG

      35,73 kB, 648×660, 294 mal angesehen
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    Mir ist jetzt kein wirklicher Fehler aufgefallen, sondern was anderes:
    Warum multiplizierst du bei deiner Funktion ToRadians Pi erst mir 2 und teilst dann durch 360? Es wäre warscheinlich performanter, einfach nur durch 180 zu teilen. Außerdem hast du dann ein minimal genaueres Ergebnis. Der Rest sieht so aus, als ob du alle Punkte ein paar Pixel zu weit oben links generierst. Versuch mal, sie jeweils ein paar Pixel nach rechts und ein paar Pixel nach unten zu verschieben (es müssten ca. 5 Pixel in beide Richtungen sein).
    Setze mal einen Haltepunkt und schau dir die Variable rect an. Du wirst feststellen, dass du garnicht mit einem Kreis arbeitest, sonder mit einer Ellipse. Punkte auf einer Ellipse berechnet man anders als die auf einem Kreis^^

    Edit: Du musst die Winkel ins Bogenmaß umwandeln, da die Methoden der Math-Klasse Winkel in diesem Format erwarten. Ist wie wenn du eine Geschwindigkeit ausrechnen willst (=> km/h), da musst du Kilometer und Stunden angeben und nicht Meilen pro Schaltjahr oder sowas^^

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

    @ThePlexian:: Nicht Mittelpunkt und Radius, sondern oben-links und Breite-Höhe.
    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!
    erstell doch einfach eine neue point-variable die als offset dient... wenn zb die dicke der punkte fehler macht, setzt du offset dementsprechend, oder wenn du was anderes debuggen willst, etc

    edit:

    VB.NET-Quellcode

    1. '0.0174532925199433
    2. private PI_180 as double = Math.PI / 180
    3. public function deg2rad(deg as double) as double
    4. return deg * PI_180
    5. end function
    6. public functiion rad2deg(rad as double) as double
    7. return rad / PI_180
    8. end function

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Agita“ ()

    Der (Haupt-)Fehler ist, dass er keinen Kreis zeichnet, sondern eine Ellipse (Haltepunkt auf die rect-Variable). Zwei Möglichkeiten den Fehler auszubessern:
    1) dafür sorgen, dass man nur Kreise zeichnet
    2) die Berechnung für die Punkte so machen, dass sie Punkte auf einer Ellipse berechnet (und nicht auf einem Kreis)

    @Agita
    geschickt gelöst xD
    stimmt, jetz fällts mir auch auf, dass die 5 punkte selbst mit nem offset nich gleichmäßig liegen würden, selbst ohne vb anzuschmeißen :D

    deg2rad2deg benutze ich auch sehr oft, und das in sachen wo es in kruerz zeit sehr oft aufgerufen wird... da jedesmal die komplette formel zunehmen verlangsamt ungemein :D

    Lingo schrieb:

    Ich glaube es liegt an der dicke der Punkte.
    Da diese ja nicht genau 1 Pixel sind kannst du sie nicht präzise platzieren.
    Deswegen musst du den Durchmesser der kleinen Punkte beachten.
    Nope, da sonst wenigstens jeder Punkt den Kreis schneiden müsste.

    @nafets3646::
    Naja, das mit dem 2 / 360 oder 1 / 180 war mir in dem Moment egal ^^
    Also wenn ich beim Zeichnen der Punkte einen Offset von 6px hinzufüge (--> g.FillEllipse(Brushes.Red, New RectangleF(pt.X + 6, pt.Y + 6, 8, 8)), dann gehts... und warum bitte ? :D

    @FreakJNS::
    Das stimmt nicht, da ich im Form.Load-Event die Clientsize auf 600x600 setze, und somit die Werte von rect so sind:
    x = 10 ; y = 10 ; Width = 580 ; Height = 580

    @RodFromGermany::
    Stimmt schon, aber siehe Kommentar zu Lingo oben,

    @Agita::
    Ja aber dann sind das mehr oder weniger random-Werte, und ich wills ja auch verstehen :D
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    Nagut, gesetz dem Falle, dass es ein Kreis ist hast du aber noch einen Fehler gemacht: Das Rectangle ist verschoben, das hast du nicht berücksichtigt (rect.x und rect.y dazurechnen):

    VB.NET-Quellcode

    1. Dim center As New PointF(rect.X + CSng(rect.Width / 2), rect.Y + CSng(rect.Height / 2))


    alternativ meine Lösung, die auch Ellipsen beherrscht:

    VB.NET-Quellcode

    1. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint
    2. Dim margin As Integer = 10 'Damit ein 10px Abstand zur Formborder besteht
    3. Dim rect As Rectangle = New Rectangle(margin, margin, Me.ClientSize.Width - 2 * margin, Me.ClientSize.Height - 2 * margin)
    4. Dim radiusX As Single = (rect.Width / 2.0F)
    5. Dim radiusY As Single = (rect.Height / 2.0F)
    6. 'Me.Text = rect.ToString
    7. Using g As Graphics = e.Graphics
    8. g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    9. 'g.DrawRectangle(Pens.Black, rect)
    10. g.DrawEllipse(New Pen(Brushes.DarkGray, 3), rect) 'Circle
    11. For angle As Integer = 0 To 360 Step 72
    12. Dim ang As Double = ToRadians(angle + 270) 'Damit der erste Punkt oben in der Mitte ist
    13. Dim centerX As Single = rect.X + rect.Width / 2.0F
    14. Dim centerY As Single = rect.Y + rect.Height / 2.0F
    15. Dim px As Single = centerX + CSng(Math.Cos(ang)) * radiusX
    16. Dim py As Single = centerY + CSng(Math.Sin(ang)) * radiusY
    17. g.FillEllipse(Brushes.Red, New RectangleF(px - 5, py - 5, 10, 10))
    18. Next
    19. End Using
    20. End Sub

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

    ThePlexian schrieb:

    dann gehts... und warum bitte ? :D
    Magic...
    Ne, im Ernst: Schau einfach mal, wo die 6px an Offset herkommen könnten :D. In der Zeile 18 entsteht beispielsweise ein kleiner Offset und in Zeile 13 hast du vergessen, den Rand mit einzubeziehen. Die Zeilen müssten in etwa so aussehen:

    VB.NET-Quellcode

    1. Dim Center As New PointF(CSng(rect.Width / 2) + margin, CSng(rect.Height / 2) + margin)
    und

    VB.NET-Quellcode

    1. g.FillEllipse(Brushes.Red, New RectangleF(pt.X - 2.5, pt.Y - 2.5, 5, 5))

    //EDIT:
    Wenn ich mir das so anschaue, müssten es eigentlich sogar 7,5 Pixel Offset sein :D.
    und ich wills ja auch verstehen


    in sachen verständnis und mal abgesehen von der kreis/ellipse-sache:

    es ist immer das gleiche spiel :) 1. rotation 2. skallieren 3. translation(offset)
    1) du rotierst in deiner sache um Z, 5 einzelabschnitte, vollkreis/5 = 1 abschnitt, jeder abschnitt hat nen anderen cos/sin wert
    2) den radius kann man auch als akllierung ansehen
    3) und center.x/y ist dein offset, das heisst, diesen debugwert von 6 den du hast kannst du auch zb da hinzufügen

    Quellcode

    1. ...
    2. ' steht am besten ausserhalb der schleife
    3. Dim center As New PointF( _
    4. CSng(rect.Width / 2) + ... rand miteinbeziehen ... debug wert einbeziehen ... etc etc ... , _
    5. CSng(rect.Height / 2) + ... rand miteinbeziehen ... debug wert einbeziehen ... etc etc ... _
    6. )
    7. ...
    8. pt = New PointF( _
    9. _ ' rota, scale, offset
    10. Math.Cos(a) * radius + center.X, _
    11. Math.Sin(a) * radius + center.Y _
    12. )


    sry, hab langeweile xD
    Ich habe mal den kleinen Fehler ausgebaut (thx nafets3646) den Abstand zum Rand gleich null gesetzt und die Liniendicke minimiert, folgendes Ergebnis:

    VB.NET-Quellcode

    1. Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    2. Dim margin As Integer = 0
    3. Dim rect As Rectangle = New Rectangle(margin, margin, Me.ClientSize.Width - 2 * margin, Me.ClientSize.Height - 2 * margin)
    4. Dim radius As Integer = CInt(rect.Width / 2)
    5. Dim center As New PointF(CSng(rect.Width / 2) + margin, CSng(rect.Height / 2) + margin)
    6. Using g As Graphics = e.Graphics
    7. g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    8. g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    9. g.DrawEllipse(New Pen(Brushes.DarkGray, 1), rect) 'Circle
    10. For angle As Integer = 0 To 360 Step 72
    11. Dim a As Double = ToRadians(angle + 270)
    12. Dim pt As New PointF(center.X + radius * CSng(Math.Cos(a)), _
    13. center.Y + radius * CSng(Math.Sin(a)))
    14. g.FillEllipse(Brushes.Red, New RectangleF(pt.X, pt.Y, 2, 2))
    15. Next
    16. 'Richtlinie
    17. g.DrawLine(Pens.Black, New Point(CInt(center.X), rect.Y), New Point(CInt(center.Y), rect.Height))
    18. End Using
    19. End Sub
    20. Function ToRadians(deg As Double) As Double
    21. Return Math.PI / 180 * deg
    22. End Function


    EDIT: Die schwarze Linie ist nur eine Richtlinie, dort sollte der obere Punkt liegen ^^
    Bilder
    • Circle.JPG

      29,98 kB, 632×649, 176 mal angesehen
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    Bau die Schleife mal anders auf:

    VB.NET-Quellcode

    1. Public Property PointCount As Integer = 5 'Es sollen 5 Punkte gleichmäßig verteilt auf den Kreis gezeichnet werden
    2. For i As Integer = 0 To PointCount - 1
    3. Dim ang = (360 / PointCount) * i - 90
    4. Dim a As Double = ToRadians(ang)
    5. '...
    6. Next
    Vom Aufbau her angenehmer. Ich würde die schleife auch mittels der gewünschten (Eck)Punkte laufen lassen, anstatt komplette 360° und dem step-parameter.
    Warum? weil ein Kreis IMMER 360° sind. willst du später was ändern brauchst du nur PointCount ändern
    Genau das. Wenn du z.B. mal 50 Punkte haben möchtest lässt sich das ganz einfach einrichten. Ansonsten müsstest du u.U. eine Kommazahl als Schleifenvariable hochzählen lassen (360/50=7.2) - und das sollte man vermeiden. Grundsatz: Mit Schleifen nur Ganzzahlige Zahlentypen zählen. Grund: Gleitkommazahlen werden nur ungenau dargestellt. So kann es unter ungünstigen Umständen dazu kommen, dass ein Wert zuviel oder zu wenig iteriert wird - den Fehler wirst du niemals ausfindig machen.

    Hier mal ein Beispiel für einen WorstCase - bei sehr vielen Nachkommastellen tritt solches Verhalten aber auch schon bei kleineren Zahlen auf.

    VB.NET-Quellcode

    1. Dim zahlA As Single = 5000 'eine kleine Zahl
    2. Dim zahlB As Single = Long.MaxValue 'ein große Zahl
    3. Dim zahlC As Single = (zahlA + zahlB) - zahlB '(klein + groß - groß) = klein??
    4. Me.Text = zahlC.ToString' = 4096


    Ist deine Frage damit gelöst oder hängt es noch iwo?
    lg
    @FreakJNS: (Bezieht sich auf Post #10) Vorsicht, da stimmt der Winkel dann nicht. Solange es ein Kreis ist, passt es, aber bei einer Ellipse würden die Punkte einfach zusammengestaucht werden.

    VB.NET-Quellcode

    1. Dim Angle As Double = ...
    2. Dim Angle2 = Math.Atan2(Math.Sin(Angle), Math.Cos(Angle) * (RadiusY / RadiusX))
    3. Dim PointX = Math.Cos(Angle2) * RadiusX
    4. Dim PointY = Math.Sin(Angle2) * RadiusY


    Links (rot) ist mit dieser Formel berechnet, rechts (grün) ist mit der von Dir gezeigten.
    Die roten und grünen Linien treffen sich nicht. Die rechte Ellipse ist einfach in Y zusammengestaucht.
    Deutlicher wird es, wenn der Unterschied zwischen den Radien groß ist:

    Noch deutlicher sieht man den Unterschied, wenn man außen um die Mitte herum die Ellipse wegnimmt. Dann gibt's keine optische Täuschung und dann fällt auch auf, dass links die Winkel zwischen den Linien konstant sind und rechts werden die Winkel kleiner, je näher sie an der X-Achse liegen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils