Graphics2D - Bild bewegen (ev. mehr mathematisches Problem)



  • Guten Abend

    Ich hab grad irgendwie einen Knoten im Kopf.

    In der Schule lernte ich einst:

    x = r * cosφ
    y = r * sinφ

    Alles klar soweit.

    Nun zum Zusammenhang mit Graphics2D.

    Ich lade ein Bild (BufferedImage) (werft.png - als Beispiel) mit ImageIO.read(...), zeichne
    dieses mit g.drawImage(...). Alles kein Problem, logisch und nachvollziehbar.

    Ich fange nun mit einem KeyListener die Tasten Up, Left, Down and Right ab.

    Bei left and right rotiere ich mit

    private BufferedImage rotateImage(BufferedImage src, double degrees) {
    
                src = defaultImage;
    
                realdeg+=degrees;
    
                deg-=degrees;
    
                if(realdeg >= 360)
                    realdeg = 360-realdeg;
                else
                    if(realdeg < 0)
                        realdeg = 360 + realdeg;
    
                if(deg >= 360)
                    deg = 360 - deg;
                else
                    if(deg < 0)
                        deg = 360 + deg;
    
                degrees = deg;
    
                AffineTransform affineTransform = AffineTransform.getRotateInstance(
                        Math.toRadians(degrees),
                        src.getWidth()/2,
                        src.getHeight()/2);
                BufferedImage rotatedImage = new BufferedImage(src.getWidth(), src
                        .getHeight(), src.getType());
                Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
                g.setTransform(affineTransform);
                g.drawImage(src, 0, 0, null);
    
                return rotatedImage;
            }
    

    das Bild.

    Soweit so gut, kein Problem, etwas flakert es noch, aber das ist nicht das Problem des Threads.

    Nun soll das Bild jedoch in die Richtung in die es schaut (deg gemessen anhand der Polarachse x-positivachse)
    sich bewegen. Und zwar immer um 3.

    Nun kommt also das alte Schulwissen zu Tragen:

    x = r * cosφ
    y = r * sinφ

    r wäre in diesem Fall die Entfernung zum Pol, also 3
    φ wäre der realdeg (Affinetransform dreht irgendwie in die falsche Richtung, denn die korrekte Richtung ist ja gegen den Uhrzeigersinn, desswegen
    auch deg und realdeg).

    Ich berechne mir daher folgendermasen x und y

    public int CalcX() {
    
                int ret = 0;
    
                ret = (int)(3 * Math.cos(realdeg)); 
    
                if(realdeg == 90 || realdeg == 270)// cos 90 / cos 270 != 0
                                                   // aber IMG darf nicht Richtung
                                                   // X wandern, wenn realdeg == 270 || 90
                    ret = 0;
    
                return ret;            
            }
    
            public int CalcY() {
    
                int ret = 0;
    
                ret = (int)(3 * Math.sin(realdeg));
    
                if(realdeg == 180 || realdeg == 0) // sin 180 / sin 0 != 0
                                                   // aber IMG darf nicht Richtung
                                                   // Y wandern, wenn realdeg == 180 || 0
                    ret = 0;
    
                return ret;            
            }
    

    Das Ergebnis ist ziemlich ernüchternd, denn so bewegt sich das Bild keineswegs
    in die erforderlichen Richtungen. Also frage ich mich, was mache ich falsch?

    Ich erbitte daher höflichst um Hilfe 😃

    Gruß
    ITEDVO

    PS:

    Kleiner Log:

    CalcX() bei Realdeg 0.0 | 3
    CalcY() bei Realdeg 0.0 | 0
    deg = 0.0
    CalcX() bei Realdeg 0.0 | 3
    CalcY() bei Realdeg 0.0 | 0
    deg = 0.0
    CalcX() bei Realdeg 350.0 | 0
    CalcY() bei Realdeg 350.0 | -2
    deg = 10.0
    CalcX() bei Realdeg 350.0 | 0
    CalcY() bei Realdeg 350.0 | -2
    deg = 10.0
    CalcX() bei Realdeg 350.0 | 0
    CalcY() bei Realdeg 350.0 | -2
    deg = 10.0
    CalcX() bei Realdeg 0.0 | 3
    CalcY() bei Realdeg 0.0 | 0
    deg = 0.0
    CalcX() bei Realdeg 0.0 | 3
    CalcY() bei Realdeg 0.0 | 0
    deg = 0.0
    CalcX() bei Realdeg 0.0 | 3
    CalcY() bei Realdeg 0.0 | 0
    deg = 0.0
    CalcX() bei Realdeg 10.0 | -2
    CalcY() bei Realdeg 10.0 | -1
    deg = 350.0
    

    PS2:

    Müsste eigentlich bei folgendem nicht immer 3 herauskommen?

    3 * cos(90) = -1.3442208483875104
    3 * cos(270) = 2.9531458518975144
    3 * sin(0) = 0.0
    3 * sin(180) = -2.403457907201491
    


  • itedvo schrieb:

    Müsste eigentlich bei folgendem nicht immer 3 herauskommen?

    3 * cos(90) = -1.3442208483875104
    3 * cos(270) = 2.9531458518975144
    3 * sin(0) = 0.0
    3 * sin(180) = -2.403457907201491
    

    Die trigonometrischen Funktionen aus java.lang.Math arbeiten mit Parametern im Bogenmaß (engl. radians).

    http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Math.html#sin%28double%29

    Bei der Rotation hast du das offenbar mit Math.toRadians schon richtig gemacht.



  • Das erklärt so einiges oO

    Aber ich glaub ich habe noch immer ein gewaltiges Brett vorm Kopf, denn
    folgendes ergibt noch immer nicht 3 oO

    private void CalcStdSinCos() {
    
            System.out.println("3 * cos(90) = " + 3*Math.cos(Math.toRadians(90)));
            System.out.println("3 * cos(270) = " + 3*Math.cos(Math.toRadians(270)));
    
            System.out.println("3 * sin(0) = " + 3*Math.sin(Math.toRadians(0)));
            System.out.println("3 * sin(180) = " + 3*Math.sin(Math.toRadians(180)));
    
        }
    

    Sondern folgendes:

    3 * cos(90) = 1.8369701987210297E-16
    3 * cos(270) = -5.51091059616309E-16
    3 * sin(0) = 0.0
    3 * sin(180) = 3.6739403974420594E-16
    

    Edit:

    Bzw. 3 und -3

    Denn bei φ 90° müsste die Y-Koordinate bei 0 liegen und X bei 3
    Bei 270° hingegen müsste X = -3 sein

    Bei φ 0° müsste X-Koordinate bei 0 liegen und Y bei 3 und eben umgekehrt
    bei 180° müsste X-Koordinate ebenfalls 0 und Y bei -3 liegen.

    Oder hab ich das Grundprinzip missverstanden?



  • Die Werte sind schon richtig, denn sie sollen alle 0 ergeben - und nicht 3.

    Dank Floating-Point-Arithmetik kommt es bei deinen Testwerten zu Ungenauigkeiten. Der erste Wert 1.8369701987210297E-16 ist nämlich z. B. 0.00000000000000018369701987210297.

    Da du die Zahlen am Ende sowieso in ganze Pixel umrechnest, sollte das aber vernachlässigbar sein.



  • Interessant...

    Ich stellte mir das so vor, dass wenn ein Winkel von 90° herrscht, r = Y sei
    Aber dem wäre ja nicht so, wenn Y = 0 wäre...

    Ich gehe nämlich noch immer von der Umrechnung der polarkoord. in kartesische
    aus.

    Und da steht (ja, habe eine Schulmitschrift hervorgekramt) folgendes:

    In allen 4 Quadranten gilt:

    x = r * cosφ
    y = r * sinφ

    Da r = Abstand zum Pol ist, sollte das doch eigentlich heißen, dass Y = r ist,
    wenn φ = 90° - folglich müsste sin 90° = 1 ergeben oder?

    Nehmen wir mal an ich denke hier komplett daneben und mein Matheprof. war ein
    Schwachkopf (sorry, falls er das lesen sollte ^^) - Wie mache ich es dann richtig?

    Edit:

    Um fair zu bleiben, veränder ich die Aussage:

    Entweder denke ich komplett daneben und bin ein mathematischer Schwachkopf, oder mein Matheprof war ein Schwachkopf xD

    Fairness muss schon gelten, wenn schon der Rest der Welt nicht fair ist 😛



  • x und y sind relativ zu einem Ursprung, z. B. (0, 0). Genauso sind es r und φ.

    Das heißt, deine Formel rotiert einen Punkt (x, y) um den Ursprung (0, 0) mit dem Radius r und dem Winkel φ. Wenn du um einen anderen Punkt rotieren willst, musst du x und y als Offset zu diesem Punkt nehmen.

    Folgendes Beispiel rotiert einen Punkt um den Ursprung mit dem Radius r=3 in 90°-Schritten:

    public static void main (String[] args) {
        double r = 3;
    
        for (double a = 0; a <= 360; a += 90)
            System.out.format("%.0f deg: x=%.2f y=%.2f\n", a, x(r, a), y(r, a));
    }
    
    static double x(double r, double a) {
        return r*Math.cos(Math.toRadians(a));
    }
    
    static double y(double r, double a) {
        return r*Math.sin(Math.toRadians(a));
    }
    

    Ausgabe:

    0 deg: x=3.00 y=0.00
    90 deg: x=0.00 y=3.00
    180 deg: x=-3.00 y=0.00
    270 deg: x=-0.00 y=-3.00
    360 deg: x=3.00 y=-0.00
    


  • Sorry, ich glaube, meine letzte Antwort geht etwas am Problem vorbei, denn du willst ja nicht rotieren, sondern dein Bild in die Richtung bewegen.
    Muss ich nochmal genauer gucken...



  • Danke, ein Licht ist aufgegangen...

    Am besten ich such mir die Adresse des Matheprofs raus und bring ihm nen guten
    Wein - Ich frage mich wie er mich ausgehalten hat 😕

    Danke noch mals *virtuellen Wein überreich*

    Edit

    Dein Rotationsbeispiel hat bei mir ein Licht aufgehen lassen ^^ Sozusagen alte,
    verstaubte Erinnerungen hervorgeholt ^^

    Ich musste lediglich bei CalcY() den Returnwert invertieren. Ob dies jedoch
    notwendig ist, auf Grund von Folgefehlern in meinem weiteren Programm, oder ob
    es so korrekt ist, müsst ich mir noch ausrechnen. Aber jetzt weiß ich immerhin
    wieder wie 😛



  • Ja, passt doch:

    x' = x + r * cos(φ)
    y' = y + r * sin(φ)

    Mit (x, y) = Alte Position, (x', y') = Neue Position, r = Schrittweite und φ = Guckrichtung.

    Achte darauf, dass du die aktuellen Koordinaten intern als float oder double speicherst und erst beim Rendern in ganzzahlige Pixelkoordinaten umrechnest/rundest. Sonst kommst du bei kleinen r-Werten nicht vom Fleck, bzw. läufst nicht in die exakte Richtung.



  • Danke, werd ich mir gleich in die Notizen schreiben.

    Ich fang nämlich erst grad mit Graphics2D an, in Hinblick auf ein geplantes,
    nächstes Projekt.

    Aber bevor ich mir überhaupt großartig Gedanken über ein Projekt mache (bis auf
    die Fragen: Was? Und mit was setze ich es um?) muss ich in Graphics2D und wie
    ich merke vorallem auch Trigonometrie meine Kenntnisse auffrischen.

    Aber ich habe ja Zeit, mein derzeitiges Projekt ist erst in der Betaphase und
    da is noch einiges an Arbeit vor mir.

    Danke noch mals für die Hilfe.



  • Dazu ein Beispiel:

    Angenommen, du befindest dich bei (x, y) = (3, 2) und willst (mehrfach hintereinander) 1px in Richtung 30° gehen. Dann bekommst du für (dx, dy) die Werte (0.87, 0.50).

    Wenn du nun nach int castest, kommst du nicht vom Fleck, da die Nachkommastellen abgeschnitten werden und du dich um (0, 0) bewegst. Wenn du hingegen rundest kommt (1, 1) raus und du bewegst dich in 45°-Richtung.

    Rechne stattdessen mit float-Koordinaten, sodass sich Nachkommastellen akkumulieren und beim Rendern die Pixel-Koordinaten im richtigen "Rhythmus" weiterschalten.



  • Das hab ich so gar nicht bedacht, aber wenn ich dass so lese is es nicht nur
    einleuchtend, sondern einfach nur logisch.

    Das werd ich mir wohl rot und mit dicker Schrift hinterlegen ⚠


Anmelden zum Antworten