Winkelberechnung fehlerhaft wegen Rundungsfehler



  • Hallo zusammen,

    Ich versuche seit ein paar Tagen ein Programm zu schreiben das eine beliebige Figur auf dem Bildschirm rotieren lassen kann. (Ohne DirectX oder OpenGL).

    Funktionsweise für einen Punkt:
    Ich habe einen Ausgangspunkt x = 200 und y = 180.
    Durch drücken der Links-Taste wird der Radius und der Winkel zur Mitte des Bildschirms ermittelt. Anschließend 1 auf den Winkel addiert und die X und Y Punkte neu berechnet und gezeichnet. Der alte Punkt wird gelöscht damit es auch eine Animation ist.

    Ansich funktioniert es auch aber....
    da der Bildschirm aus rechteckigen Pixeln besteht habe ich bei jeder Neuberechnung einen klitzekleinen Versatz. Somit läuft es leider nicht ganz rund und die Figur sieht nach ein paar Umdrehungen aus wie Salat.

    Ich lasse mir den Radius im Fenstertitel anzeigen und er schwankt immer +- 2 Pixel vom Soll.

    Hat jemand einen Tipp für mich wie ich immer den Richtigen Radius erhalten kann?
    DirectX kann es ja auch berechnen, also muss es ja irgendwie gehen oder?

    Ich programmiere nicht in C++. Daher bitte keinen Code schreiben, nur ein paar Worte zur Vorgehensweise.
    Wie gesagt es kann nur daran liegen.

    Danke und Gruß, Nicky



  • Schau dir mal an was eine Rotationsmatrix tut. Die Rotation so implementieren wie du es grad machst ist jedenfalls keine gute Idee. Und speicher deine Punkte eben nicht in int- sondern in float-Koordinaten. Anstatt inkrementell zu rotieren könntest du auch einfach die Ausgangskoordinaten speichern und dann die neuen Koordinaten immer mit dem absoluten Winkel daraus berechnen.


  • Mod

    das sollte an sich so nicht passieren, kannst du mal den relevanten teil vom code posten, vielleicht hast du einen bug.



  • Hallo,

    Welchen Vorteil hat es die Bildschirmpunkte auch als Float zu speichern? Die Pixeldaten haben doch nur ganze Zahlen?

    Im großen und ganzen beziehen sich die Berechnungen auf dem rechtwinkligen Dreieck. Erst die Differenz auf der X-Achse errechnen (gk) und dann auf der Y-Achse (ak). Beides zum Quadrat, Addieren und die Wurzel ziehen. Somit hab ich den Radius meines Kreises (oder Hypothenuse). Damit kann ich den Winkel zum Mittelpunkt berechnen. Ich nutze überall Floatwerte. Bei der Rückgabe in Bildschirmkoordinaten runde ich diese auf Int-Werte (gibt ja keine 80,64 Pixel)

    Hier mal der Code dafür:

    fclex ;FPU Fehlerflag löschen
    finit ;FPU Initialisieren

    fild gk
    fild gk
    fmul
    fstp quad1

    fild ak
    fild ak
    fmul
    fstp quad2

    fld quad1
    fld quad2
    fadd
    fsqrt
    fstp p1.hl ;Das ist der Radius

    fild gk
    fld p1.hl
    fdiv
    fstp sin ;Der Sinus des Winkels

    fild ak
    fld p1.hl
    fdiv
    fstp cos ;Der Cosinus des Winkels

    fld sin
    fld cos
    fpatan
    fstp bom

    fld pi2
    fld bom
    fdiv
    fld kreis
    fxch
    fdiv
    fstp winkel ;hier der errechnete Winkel

    fld winkel
    fld winkelerhoehung ;Winkel + 1
    fadd
    fstp winkel ;neuen Winkel als Float speichern
    fwait

    fclex
    finit

    fld kreis
    fld winkel
    fdiv
    fstp quad1 ;360 / Winkel

    fld pi2 ; 6,283185 / (360/Winkel)
    fld quad1
    fdiv
    fstp bom ;Wert zwischenspeichern

    ;Sin
    fld bom ;vom zwischengespeicherten Wert den Sin berechnen
    fsin
    fld p1.hl
    fmul
    fild mpy
    fxch
    fsub
    fist p1.y ;Sinus = Y-Koordinate (Integer)

    ;Cos
    fld bom ;vom zwischengespeicherten Wert den Cos berechnen
    fcos
    fld p1.hl
    fmul
    fild mpx
    fadd
    fist p1.x ;Cosinus = X-Koordinate (Integer)
    fwait

    Ich hoffe jemand kann das lesen.
    Ich habe auch in dem Programm den Radius zwischengespeichert und weiter genutzt, damit läuft alles nach Plan. Sobald ich diesen Wert aber einmal lösche habe ich einen kleinen Versatz zum "Vorkreis".

    Hoffe jemand kann helfen 😞

    Nicky



  • Supernicky schrieb:

    Welchen Vorteil hat es die Bildschirmpunkte auch als Float zu speichern? Die Pixeldaten haben doch nur ganze Zahlen?

    Na das hast du doch selber schon gesagt. Auch wenn deine Ausgangspunkte ganzzahlige Koordinaten haben, wenn du deinen Punkt um x Grad rotierst dann hat er nichtmehr notwendigerweise ganzzahlige Koordinaten. Die Wahrscheinlichkeit ist sogar verschwindend gering. Also wird gerundet. Und dabei ein Fehler gemacht. Wenn du dann ausgehend von diesen fehlerbehafteten Koordinaten weiterrotierst kanns gut sein dass das Ganze irgendwohin abhaut. Darum entweder mit float oder Fixkomma arbeiten oder z.B. einfach nicht inkrementell rotieren.



  • Hallo dot,

    was ist "nicht inkrementell rotieren" gemeint??



  • supernicky schrieb:

    was ist "nicht inkrementell rotieren" gemeint??

    Ich würde es so deuten, daß du nicht die aktuelle Position jeweils um 1 Grad drehst, sondern dir den Rotationswinkel vom Ausgangspunkt aus merkst und jeweils die Originalpunkte um n Grad drehst.



  • exakt



  • Genau. Das ist dein Problem. Du solltest weder in float noch in sonstwas speichen, du sollst einfach _gar nicht_ speichern und nur anzeigen. Was du anzeigst, ergibt sich dann stets aus (200;180)*Rotationsmatrix.



  • Hallo zusammen,

    ich weiß was ihr meint. Das funktioniert auch, solange ich nur um eine Achse drehe.
    Sobald ich das Object auch um die Z-Achse drehen lasse, verändere ich automatisch die X- und Y Koordinaten auf unvorhersehbare Maße (man weiß ja nicht wie weit der User das Object nach hinten dreht)...

    Daher bin ich förmlich gezwungen den Winkel und den Radius aus dem IST-Zustand neu zu berechnen.

    Ich mach noch ein paar Versuche... 🙄

    Nicky


Log in to reply