Berechnung Winkel in gleichschenkligem Dreieck

  • VB.NET
  • .NET (FX) 3.0–3.5

Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Berechnung Winkel in gleichschenkligem Dreieck

    Hallo liebe Community,
    ich stehe erneut vor, möglicherweise einem Denkfehler.

    Es geht mir diesmal um die Bereichnung eines Winkels in einem gleichschenkligem Dreick.
    Grundsätzlich sehr einfach: γ = arccos( ( 2 * a² - c² ) / (2a²) )
    Wieso bekomme ich nun aber mit folgender Berechnung keinen "wahren Winkel":

    VB.NET-Quellcode

    1. Dim dist As Integer = CInt(MeasurePointDistance(center, pt))
    2. Math.Acos((2 * dist ^ 2 - MeasurePointDistance(New Point(center.X, center.Y + ellipseWidth), pt) ^ 2) / (2 * dist ^ 2))
    3. Public Shared Function MeasurePointDistance(ByVal p1 As Point, ByVal p2 As Point) As Double
    4. Return Math.Round(Math.Sqrt(Math.Pow((p2.X - p1.X), 2) + Math.Pow((p2.Y - p1.Y), 2)), 1)
    5. End Function



    Edit://
    Wobei center, logischerweise der Mittelpunkt des Kreises ist, und
    pt der Schnittpunkt mit dem Kreis.

    Danke im Vorraus.
    Bilder
    • winkel.PNG

      15,25 kB, 449×468, 159 mal angesehen
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    Hi
    was verstehst du unter einem "wahren Winkel"? Du bekommst den Radianten, d.h. du musst noch mit 180/Pi multiplizieren. Den Sinn der Verwendung von Math.Round verstehe ich übrigens nicht.

    Quadrat ist übrigens besser über x * x zu lösen, als über x ^ 2. Das ist effizienter und vermutlich auch akkurater. Ggf. schreibst du dir dafür eine eigene statische Methode, die das erledigt.

    Viele Grüße
    ~blaze~
    Okey, das ist mir jetzt peinlich. Ja es ging einfach um den Radianten.
    Habe nun auch deine Verbesserungsvorschläge eingebaut:

    VB.NET-Quellcode

    1. Private Function GetLineAngle(center As Point, pt As Point) As Single
    2. Dim dist As Double = MeasurePointDistance(center, pt)
    3. Dim x As Double = MeasurePointDistance(New Point(center.X, center.Y + dist), pt)
    4. Return Math.Acos((2 * (dist * dist) - x * x) / (2 * (dist * dist))) * (180 / Math.PI)
    5. End Function

    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


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

    @Gather Iwo fehlt mir da was.
    3 Punkte: Kreismittelpunkt und zwei Peripheriepunkte, von denen einer fest sein kann, der andere ist an die Peripherie gebunden, ich nehme mal an, dass der Abstand der beiden Peripheriepunkte dist heißt, dann wäre c der Radius. Was ist dann ellipseWidth?
    Kannst Du mal Deine Variablennamen in Dein Bild reinmalen, und die Zuordnung zu Deiner grundsätzlichen Formel markieren?
    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: Ich habe den 1. Post editiert und die Variabelbeschreibungen schon hinzugefügt.
    center ist der Mittelpunkt des Kreises,
    pt der Schnittpunkt

    Edit:// Radius is irrelevant danke. Da es sich ja nur um die Distanz zwischen center und pt handelt.
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    Gather schrieb:

    Da es sich ja nur um die Distanz zwischen center und pt handelt.
    Das wäre bei mir radius. :S
    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!
    Das stimmt. Aber der Radius ist in diesem Fall die bereits berechnete Strecke x, also irrelevant.
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    Ganz fertig bin ich noch nicht, es geht nur um die Berechnung des Value im Bezug auf den Winkel.

    Der Kreis ist unterteilt in zwei Halbkreise, mit Hilfe dieser wird, wie oben schon beschrieben der Winkel zum Schnittpunkt mit dem Kreis berechnet.
    Der Schwarze Bereich im Bild ist Blockiert, hier kann man den Regler nicht hindrehen.
    Minimum und Maximum können Variabel geändert werden.

    Grundsätzlich dachte ich das Ganze funktioniert wie folgt:

    VB.NET-Quellcode

    1. _Value = Convert.ToInt32((angle / (360 - _BlockedAngle)) * (_Maximum - _Minimum) + _Minimum)

    Das stimmt jedoch nicht. Wenn der Regler ganz nach links gedreht ist, ist mein Wert nun 7, anstatt des tatsächlichen Minimums, bei 30 blockierten Grad. (ganz rechtes 93, statt 100)

    Wisst ihr wo der Fehler liegt?
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


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

    Wie berechnest xdu angle? Sollte irgendwie so sein, wobei (x, y) die Position der Maus ist und center das Zentrum des Kreises:

    VB.NET-Quellcode

    1. angle = Math.Max(_BlockedAngle / 2, Math.Min(360 - BlockedAngle / 2, (360 + 180 / Math.Pi * Math.Atan2(y - center.Y, x - center.X)) Mod 360)


    Ggf. musst du es noch so modifizieren, dass der Winkel relativ zu einem anderen Winkel ist, so rotiert das ganze um den Winkel 0°. Das wäre dann eine Anpassung der jeweils ersten Min/Max-Parameter.

    Viele Grüße
    ~blaze~

    Gather schrieb:

    Wisst ihr wo der Fehler liegt?
    Das sieht mir nach einem Pixeleffekt aus. Welche Werte gehen denn für angle da ein? Mach da doch mal einen Dump.
    Isses nicht einfacher, Du malst Dein Control, gibst ihm die beiden Winkel (von Graphics.DrawPie) und begrenzt diese einfach bei der grafischen Darstellung selbst?
    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!
    @~blaze~:
    Angle wird wie gesagt mit der oberen Funktion berechnet:

    VB.NET-Quellcode

    1. Private Function GetLineAngle(center As Point, pt As Point) As Single
    2. 'center, ist der Mittelpunkt des Kreises,
    3. 'pt, der Schnittpunkt der Geraden mit dem Kreis zwischen dem Mittelpunkt und der Mausposition
    4. Dim dist As Double = MeasurePointDistance(center, pt)
    5. Dim x As Double = MeasurePointDistance(New Point(center.X, center.Y + dist), pt)
    6. Return Math.Acos((2 * (dist * dist) - x * x) / (2 * (dist * dist))) * (180 / Math.PI)
    7. End Function



    Edit:// Deine Methode sieht interessant aus. Ist es möglich das ganze um 90° im Uhrzeigersinn zu drehen?
    @RodFromGermany:
    Ich denke auch, dass es eine Abweichung der Pixel auf Grund der Berechnung gibt.
    Aber, dass dies so hoch ist sollte man doch umgehen können..

    Das mit DrawPie hatte ich mir auch schon überlegt, allerdings weiß ich nicht ob es für dieses Control sinnvoll wäre.
    Es soll ein Drehknopf werden.
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


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

    Gather schrieb:

    Es soll ein Drehknopf werden.
    Einer mit zwei Anschlägen.
    Eigentlich brauchst Du den Winkel zur Senkrechten oben, da ist es egal, ob die Maus drinne oder draußen ist.
    Normiertes Skalarprodukt zweier xy-Vektoren und feddich.
    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!

    VB.NET-Quellcode

    1. angle = Math.Max(BaseAngle + _BlockedAngle / 2, Math.Min(BaseAngle + 360 - BlockedAngle / 2, (360 + 180 / Math.Pi * Math.Atan2(y - center.Y, x - center.X))) Mod 360


    sollte das dann sein. BaseAngle gibt dann die Mitte des Blocks an. Du hast dann quasi einen Pie von (BaseAngle + _BlockedAngle / 2) bis (BaseAngle + 360° - _BlockAngle / 2).

    Ich würde den Atan2 verwenden, der ist für solche Sachen gedacht und bei RodFromGermanys Vorschlag hättest du immer noch einen Acos drin, der aber das Ganze etwas umständlich macht, da es mehrere Werte gibt, für die der Cosinus die gleichen Werte ausspuckt (also cos(x1) = cos(x2) mit x1 != k*2pi + x2).

    Viele Grüße
    ~blaze~

    ~blaze~ schrieb:

    Atan2
    Jou, genau der macht das, zwei Koordinaten, zwei Strecken, feddich.
    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!

    ~blaze~ schrieb:

    BaseAngle gibt dann die Mitte des Blocks an.

    Inwiefern? bzw. in welcher Einheit
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    Dann versteh ich es leider nicht ganz..

    ohne gesetzten BaseAngle, werden die Jeweiligen Grad nur im Gelben bereich zugeordnet. (also 180°)
    Dieser Bereich soll aber bis zum Blockierten Bereich gehen (BlockedAngle, im Screenshot 30°)
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


    @Gather Warum so kompliziert?
    PictureBox, center ist die Mitte des Anzeigegeräts. Oben ist 0°, der Bereich { -135°, +135° } wird angezeigt.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub PictureBox1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
    2. If e.Button = System.Windows.Forms.MouseButtons.Left Then
    3. Dim pt = e.Location
    4. angle = Math.Atan2(pt.Y - center.Y, pt.X - center.X) * 180 / Math.PI + 90 ' oben ist 0°
    5. If angle > 180 Then
    6. ' unten ist Vorzeichenumschlag
    7. angle -= 360
    8. End If
    9. ' Bereichsbegrenzung
    10. If angle > 135 Then
    11. angle = 135
    12. ElseIf angle < -135 Then
    13. angle = -135
    14. End If
    15. ' Anzeige
    16. Me.Label1.Text = angle.ToString("0.0")
    17. End If
    18. End Sub
    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!
    Dann halt so:

    VB.NET-Quellcode

    1. Dim angle As Single = Math.Min(360.0F - blockAngle / 2, Math.Max(blockAngle / 2, (CSng(Math.Atan2(y - center.X, x - center.Y) * 180.0 / Math.PI) - baseAngle + 720.0F) Mod 360.0F))


    Aber ggf. wäre es sinnvoll, wenn du verstündest, warum das so ist.

    Hier noch der hässliche Testcode, ich war jetzt nicht wirklich motiviert, da mehr zu machen:
    Spoiler anzeigen

    C#-Quellcode

    1. private float percentage;
    2. private float baseAngle = 90;
    3. private float blockAngle = 30;
    4. public Form1()
    5. {
    6. InitializeComponent();
    7. }
    8. private void Form1_Load(object sender, EventArgs e)
    9. {
    10. DoubleBuffered = true;
    11. }
    12. private void Form1_MouseDown(object sender, MouseEventArgs e)
    13. {
    14. if (e.Button == MouseButtons.Left)
    15. UpdateAngle(e.X, e.Y);
    16. }
    17. private void Form1_MouseMove(object sender, MouseEventArgs e)
    18. {
    19. if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
    20. UpdateAngle(e.X, e.Y);
    21. }
    22. private void UpdateAngle(int x, int y)
    23. {
    24. float angle = Math.Min(360f - blockAngle / 2, Math.Max(blockAngle / 2, ((float)(Math.Atan2(y - ClientSize.Height / 2.0, x - ClientSize.Width / 2.0) * 180.0 / Math.PI) - baseAngle + 720f) % 360f));
    25. percentage = (angle - blockAngle / 2) / (360f - blockAngle);
    26. Invalidate();
    27. }
    28. private void Form1_Paint(object sender, PaintEventArgs e)
    29. {
    30. float angle = baseAngle + blockAngle / 2 + percentage * (360f - blockAngle);
    31. e.Graphics.DrawArc(Pens.Black, new Rectangle(0, 0, ClientSize.Width - 1, ClientSize.Height), baseAngle + blockAngle / 2, 360 - blockAngle);
    32. e.Graphics.DrawLine(Pens.Black, ClientSize.Width * 0.5f, ClientSize.Height * 0.5f,
    33. ClientSize.Width * 0.5f * ((float)Math.Cos(angle * Math.PI / 180) + 1),
    34. ClientSize.Height * 0.5f * ((float)Math.Sin(angle * Math.PI / 180) + 1));
    35. }


    Ich würde mich übrigens sehr freuen, wenn du zukünftig schreibst, was du schon alles mit dem gelieferten Ansatz versucht hast. Es motiviert mich dann doch zu längeren Erklärungen, wenn ich weiß, dass es überhaupt auf Interesse stößt.

    Edit: RodFromGermanys Vorschlag war meine erste Idee, ist aber nicht ganz narrensicher: Bei meinem Code werden zumindest negative Winkel zwischen 0 und 360° bei baseAngle berücksichtigt (wenn du Mod 360 einbaust auch sonstige).
    Edit2: Beachte übrigens auch diese Zeile:

    C#-Quellcode

    1. percentage = (angle - blockAngle / 2) / (360f - blockAngle);

    Du willst normalerweise keinen Winkel, sondern einen relativen Wert, sodass sich das bei Änderung des Block-Winkels anpasst.

    Viele Grüße
    ~blaze~
    Gut das sieht gar nicht schlecht aus. Damit lässt sich die Winkelberechnung ersetzen.
    Das heißt, mit folgender Methode sollte ich nun jedem Angle einen Wert zuordnen können, wenn ich mich nicht irre:

    VB.NET-Quellcode

    1. _Value = Convert.ToInt32((angle / (360 - _BlockedAngle)) * (_Maximum - _Minimum) + _Minimum)

    Wobei das Vorzeichen noch für die Linke (negative) Seite des Kreises Angepasst werden sollte.

    Edit:// @~blaze~:
    Sehe mir deinen Post gerade an.

    ~blaze~ schrieb:

    Aber ggf. wäre es sinnvoll, wenn du verstündest, warum das so ist.


    Das versuche ich, tu mir aber zugegebenermaßen etwas schwer.

    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


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