Koordinatensystem drehen und neue Punkte berechnen


#1

Hab sogar schon überlegt das in irgendein Mathe-Forum zu posten, aber versuchs erstmal hier…

Habe nen Programm, das mir Bilder anzeigt. Wollte nun drehen einbauen und hab jedes Bild erstmal hardcodiert auf 90° Drehung. Das zeichnen funktioniert auch problemlos, nur… Die Berechnungen danach, mit denen man das bild umherschieben kann stimmen nun ja nicht mehr, da das ganze Koordinatensystem gedreht wurde. Nun heißts da also den Winkel in die Berechnungen einzubeziehen und da haperts ganz gehörig… Meine Winkelberechnungszeiten im koordinatensystem sind schon was her, hab mir daher die Formeln ausem Netz gezogen:

Hab dann diese versucht einzubauen und scheitere komplett…

Hier die Code-Ausschnitte:

private int computeCoordinate(boolean newX, int x, int y, int degree) {
                return newX ? (int) Math.round(x * Math.cos(degree) - y * Math.sin(degree)) : (int) Math.round(x * Math.sin(degree) + y * Math.cos(degree));
//                    return newX ? x : y;
            }

bzw der Mouselistener komplett mit Aufruf der Methode:

//Framelistener
    MouseAdapter adapter = new MouseAdapter() {
        private boolean firstPress = false;
        private Point loc;
        private int pressedButton = MouseEvent.NOBUTTON;

        @Override
        public void mousePressed(MouseEvent e) {
            switch (pressedButton = e.getButton()) {
                case MouseEvent.BUTTON1:
                    if (!firstPress) {
                        firstPress = true;
                        int mouseX = e.getX(), mouseY = e.getY();

//                            mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
//                            mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());
                            loc = new Point(mouseX, mouseY);
                            System.out.println(loc);
                        }
                        break;
                    case 4:
                        prevButton.doClick();
                        break;
                    case 5:
                        nextButton.doClick();
                        break;
                    default:
                        break;
                }
            }

        @Override
        public void mouseReleased(MouseEvent e) {
            firstPress = false;
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (loc != null && pressedButton == MouseEvent.BUTTON1) {
                int mouseX = e.getX(), mouseY = e.getY();

                mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
                mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());

                int movedX = loc.x - mouseX;
                int movedY = loc.y - mouseY;

                int newCurrX = imgCmp.getCurrX() - movedX;
                int newCurrY = imgCmp.getCurrY() - movedY;

                System.out.println("NewX1: " + newCurrX + "; NewY1: " + newCurrY);

                newCurrX = computeCoordinate(true, newCurrX, newCurrY, imgCmp.getRotationDegree());
                newCurrY = computeCoordinate(false, newCurrX, newCurrY, imgCmp.getRotationDegree());

                System.out.println("NewX2: " + newCurrX + "; NewY2: " + newCurrY);
                
                System.out.println(imgCmp.getRotationDegree());

                imgCmp.setCurrX(newCurrX);
                imgCmp.setCurrY(newCurrY);
                imgCmp.repaint();

                loc.x = e.getX();
                loc.y = e.getY();
            }
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            int hBef = imgCmp.getCurrH();
            int wBef = imgCmp.getCurrW();
            imgCmp.setScaleFactor(imgCmp.getScaleFactor() - (e.getUnitsToScroll() / 100.0));
            imgCmp.repaint();
            imgCmp.computeCurrWAndH();
            imgCmp.setCurrX(imgCmp.getCurrX() - (imgCmp.getCurrW() - wBef) / 2);
            imgCmp.setCurrY(imgCmp.getCurrY() - (imgCmp.getCurrH() - hBef) / 2);
            scaleTextField.setText("" + Math.round(imgCmp.getScaleFactor() * 100.0));
        }
        
        private int computeCoordinate(boolean newX, int x, int y, int degree) {
                return newX ? (int) Math.round(x * Math.cos(degree) - y * Math.sin(degree)) : (int) Math.round(x * Math.sin(degree) + y * Math.cos(degree));
//                    return newX ? x : y;
            }
        };

Wenn ich die Berechnungen so lasse, schauen die Ergebnisse ganz schnell so aus:

java.awt.Point[x=966,y=556]
NewX1: -1288; NewY1: -1599
NewX2: 2007; NewY2: 2511
90
NewX1: 115; NewY1: 876
NewX2: -835; NewY2: -1139
90
NewX1: -2722; NewY1: -2772
NewX2: 3698; NewY2: 4548
90
NewX1: 1815; NewY1: 2916
NewX2: -3420; NewY2: -4364
etc

Hab schon mehrmals versucht mit den Vorzeichen rumzuspielen, aber… Es geht einfach ncih. Wer von euch mit ner Lösung parat? Versuchs solange weiter bzw. suche nach anderen Formeln


#2

Javas Winkelfunktionen arbeiten mit Radiant und nicht mit Grad.


#3

Hab ich beim Drehen bedacht, aber nich in den Rechnungen… Kommt aber trz noch größerer murks raus (Beweg die Maus nen millimeter und koordinaten wandern jenseits der Milliarden)

private int computeCoordinate(boolean newX, int x, int y, int degree) {
//                degree *= -1;
                degree = (int) Math.round(Math.toRadians(-degree));
                return newX ? (int) Math.round(x * Math.cos(degree) + y * Math.sin(degree)) : (int) Math.round(-x * Math.sin(degree) + y * Math.cos(degree));
//                return (int) (newX ? Math.round(Math.cos(degree) + Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * x) : Math.round(Math.cos(degree) - Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * y));
//                    return newX ? x : y;
            }

bzw.

MouseAdapter adapter = new MouseAdapter() {
        private boolean firstPress = false;
        private Point loc;
        private int pressedButton = MouseEvent.NOBUTTON;

        @Override
        public void mousePressed(MouseEvent e) {
            switch (pressedButton = e.getButton()) {
                case MouseEvent.BUTTON1:
                    if (!firstPress) {
                        firstPress = true;
                        int mouseX = e.getX(), mouseY = e.getY();

//                            mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
//                            mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());
                            loc = new Point(mouseX, mouseY);
                            System.out.println(loc);
                        }
                        break;
                    case 4:
                        prevButton.doClick();
                        break;
                    case 5:
                        nextButton.doClick();
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                firstPress = false;
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                if (loc != null && pressedButton == MouseEvent.BUTTON1) {
                    int mouseX = e.getX(), mouseY = e.getY();

                    System.out.println("MouseX1: " + mouseX + "; MouseY1: " + mouseY);

                    mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
                    mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());

                    System.out.println("MouseX2: " + mouseX + "; MouseY2: " + mouseY);

                    int movedX = loc.x - mouseX;
                    int movedY = loc.y - mouseY;

                    int newCurrX = imgCmp.getCurrX() - movedX;
                    int newCurrY = imgCmp.getCurrY() - movedY;

                    System.out.println("NewX1: " + newCurrX + "; NewY1: " + newCurrY);

                    newCurrX = computeCoordinate(true, newCurrX, newCurrY, imgCmp.getRotationDegree());
                    newCurrY = computeCoordinate(false, newCurrX, newCurrY, imgCmp.getRotationDegree());

                    System.out.println("NewX2: " + newCurrX + "; NewY2: " + newCurrY);

                    System.out.println(imgCmp.getRotationDegree());

                    imgCmp.setCurrX(newCurrX);
                    imgCmp.setCurrY(newCurrY);
                    imgCmp.repaint();

                    loc.x = e.getX();
                    loc.y = e.getY();
                }
            }

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                int hBef = imgCmp.getCurrH();
                int wBef = imgCmp.getCurrW();
                imgCmp.setScaleFactor(imgCmp.getScaleFactor() - (e.getUnitsToScroll() / 100.0));
                imgCmp.repaint();
                imgCmp.computeCurrWAndH();
                imgCmp.setCurrX(imgCmp.getCurrX() - (imgCmp.getCurrW() - wBef) / 2);
                imgCmp.setCurrY(imgCmp.getCurrY() - (imgCmp.getCurrH() - hBef) / 2);
                scaleTextField.setText("" + Math.round(imgCmp.getScaleFactor() * 100.0));
            }

            private int computeCoordinate(boolean newX, int x, int y, int degree) {
//                degree *= -1;
                degree = (int) Math.round(Math.toRadians(-degree));
                return newX ? (int) Math.round(x * Math.cos(degree) + y * Math.sin(degree)) : (int) Math.round(-x * Math.sin(degree) + y * Math.cos(degree));
//                return (int) (newX ? Math.round(Math.cos(degree) + Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * x) : Math.round(Math.cos(degree) - Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * y));
//                    return newX ? x : y;
            }
        };

DIe 2. Formel hab ich von http://www.onlinemathe.de/forum/Punkt-um-den-Ursprung-rotieren da kommt aber das gleiche raus, wie wenn ich einfach nichts an den koordinaten verändere…


#4

Der Unterschied zwischen dem, was eine Antwort auf diese Frage sein könnte, und dem, was ich kürzlich erst in Image eines ImageIcons rotieren geschrieben hatte, erschließt sich mir nicht ganz. (Dort gibt es eine component, mit der man ein Bild völlig frei verschieben, drehen und skalieren kann…). Aber vielleicht muss ich den Thread und den Code noch genauer lesen, um die eigentliche Frage richtig zu erkennen…


#5

Das Problem mit deiner Klasse war, das sie bei mir nich so funktioniert hat wie ich es wollte und es daher auch nicht geklappt hat. Hab die gerade nochmal ausgekramt und entweder ich seh einfach nix, oder es geht trz nich. Also ich kann das Bild weder verschieben richtig und es hängt bei mir nur klein sichtbar in ner Ecke… Mein obiges funktioniert ja auch problemlos, nur die BErechnung der neuen Koordinaten sitmmt nich ganz.

Also der Winkel muss in die BErechnung der neuen koordinaten mit einfließen und da stimmt es ncoh nciht ganz


#6

Nun, ohne dir zu nahe treten oder dich diskreditieren oder demotivieren zu wollen - das klingt für mich (und ohne, dass ich das unterstellen will) als hättest du planlos irgendwelchen Code hingeschrieben, der dann eben nicht funktioniert, und jetzt erwartest, dass man deinen Code(schnipsel - d.h. in einer Form, die man nicht testen kann!) liest, nachvollzieht (newX?!) und dir erklärt, was daran falsch ist. (“Der Code!!!” :stuck_out_tongue_winking_eye: ). Kannst du die Frage eingrenzen, oder wenigstens ein Beispiel posten, das man compilieren und starten kann?


#7

Der Code ansich funktioniert ja problemlos, nur will ich in die Berechnungen jetz noch nen Drehwinkel mit einbauen, mehr is es ja nich und da haperts, da er die Koordinaten nicht mehr ordentlich verschiebt. Mache schnell nen kskb fertig.

Hier das Kompilierbare KSKB, nurnoch der Pfad zu nem Beispielbild muss geändert werden:

package bilder;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;

/**
 *
 * @author Andy
 */
public class BilderForum {

    private static class ImageComponent extends JComponent {

        private ImageIcon imageIcon;
        private double scaleFactor = 1.0;
        private boolean firstDraw = true;
        private int currX = 0;
        private int currY = 0;
        private int currH = 0;
        private int currW = 0;
        private int rotationDegree = 90;

        public ImageComponent() {
            super();
        }

        public void setImage(ImageIcon icon) {
            if (this.imageIcon != null) {
                this.imageIcon.getImage().flush();
            }
            this.imageIcon = icon;
            firstDraw = true;
            repaint();
        }

        public ImageIcon getImage() {
            return imageIcon;
        }

        public double getScaleFactor() {
            return scaleFactor;
        }

        public void setScaleFactor(double scaleFactor) {
            if (scaleFactor <= 0.01) {
                scaleFactor = 0.01;
            }
            this.scaleFactor = scaleFactor;
        }

        public int getCurrX() {
            return currX;
        }

        public void setCurrX(int currX) {
            this.currX = currX;
        }

        public int getCurrY() {
            return currY;
        }

        public void setCurrY(int currY) {
            this.currY = currY;
        }

        public int getCurrH() {
            return currH;
        }

        public int getCurrW() {
            return currW;
        }

        public int getRotationDegree() {
            return rotationDegree;
        }

        public void setRotationDegree(int degree) {
            this.rotationDegree = degree;
        }

        @Override
        @SuppressWarnings("empty-statement")
        protected void paintComponent(Graphics gr) {
            super.paintComponent(gr);
            if (imageIcon != null) {
                computeCurrWAndH();
                Container parent = this;
                while ((parent = parent.getParent()).getClass() != JFrame.class);

                if (firstDraw) {
                    currX = (parent.getWidth() - currW) / 2;
                }

                if (firstDraw) {
                    currY = (parent.getHeight() - currH) / 2;
                }
                setPreferredSize(new Dimension(currW, currH));
                Graphics2D g = (Graphics2D) gr;
//                g.rotate(Math.toRadians(rotationDegree), parent.getWidth() / 2, parent.getHeight() / 2);
System.out.println("Draw");
                g.drawImage(imageIcon.getImage(), currX, currY, currW, currH, this);
                firstDraw = false;
            }
        }

        private void computeCurrWAndH() {
            if (imageIcon != null) {
                int iw = imageIcon.getIconWidth();
                int ih = imageIcon.getIconHeight();
                double factorW = (double) getParent().getWidth() / iw;
                double factorH = (double) getHeight() / ih;
                double factor = Math.min(factorW, factorH);
                currW = (int) (factor * iw * scaleFactor);
                currH = (int) (factor * ih * scaleFactor);
            }
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> createAndShowGUI());
    }

    private static void createAndShowGUI() {
        JFrame f = new JFrame();
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().setBackground(Color.BLACK);

        ImageComponent imgCmp = new ImageComponent();
        imgCmp.setImage(new ImageIcon("C:\\Users\\Andy\\Downloads\\FC5_ANNOUNCEMENT_FANKIT\\1_IMAGES\\FC5_Vista.jpg"));
        f.getContentPane().add(imgCmp, BorderLayout.CENTER);

        f.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                imgCmp.computeCurrWAndH();
                imgCmp.setCurrX((f.getWidth() - imgCmp.getCurrW()) / 2);
                imgCmp.setCurrY((f.getHeight() - imgCmp.getCurrH()) / 2);
                imgCmp.repaint();
            }
        });
        
        //Framelistener
        MouseAdapter adapter = new MouseAdapter() {
            private boolean firstPress = false;
            private Point loc;
            private int pressedButton = MouseEvent.NOBUTTON;

            @Override
            public void mousePressed(MouseEvent e) {
                switch (pressedButton = e.getButton()) {
                    case MouseEvent.BUTTON1:
                        if (!firstPress) {
                            firstPress = true;
                            int mouseX = e.getX(), mouseY = e.getY();
//                            mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
//                            mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());
                            loc = new Point(mouseX, mouseY);
                            System.out.println(loc);
                        }
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                firstPress = false;
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                if (loc != null && pressedButton == MouseEvent.BUTTON1) {
                    int mouseX = e.getX(), mouseY = e.getY();

                    System.out.println("MouseX1: " + mouseX + "; MouseY1: " + mouseY);

//                    mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
//                    mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());

                    System.out.println("MouseX2: " + mouseX + "; MouseY2: " + mouseY);

                    int movedX = loc.x - mouseX;
                    int movedY = loc.y - mouseY;

                    int newCurrX = imgCmp.getCurrX() - movedX;
                    int newCurrY = imgCmp.getCurrY() - movedY;

                    System.out.println("NewX1: " + newCurrX + "; NewY1: " + newCurrY);

//                    newCurrX = computeCoordinate(true, newCurrX, newCurrY, imgCmp.getRotationDegree());
//                    newCurrY = computeCoordinate(false, newCurrX, newCurrY, imgCmp.getRotationDegree());

                    System.out.println("NewX2: " + newCurrX + "; NewY2: " + newCurrY);

                    System.out.println(imgCmp.getRotationDegree());

                    imgCmp.setCurrX(newCurrX);
                    imgCmp.setCurrY(newCurrY);
                    imgCmp.repaint();

                    loc.x = e.getX();
                    loc.y = e.getY();
                }
            }

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                int hBef = imgCmp.getCurrH();
                int wBef = imgCmp.getCurrW();
                imgCmp.setScaleFactor(imgCmp.getScaleFactor() - (e.getUnitsToScroll() / 100.0));
                imgCmp.repaint();
                imgCmp.computeCurrWAndH();
                imgCmp.setCurrX(imgCmp.getCurrX() - (imgCmp.getCurrW() - wBef) / 2);
                imgCmp.setCurrY(imgCmp.getCurrY() - (imgCmp.getCurrH() - hBef) / 2);
            }

            private int computeCoordinate(boolean newX, int x, int y, int degree) {
                degree *= -1;
                degree = (int) Math.round(Math.toRadians(degree));
                return (newX ? (int) Math.round(x * Math.cos(degree) + y * Math.sin(degree)) : (int) Math.round(x * Math.sin(degree) - y * Math.cos(degree)));
//                return (int) (newX ? Math.round(Math.cos(degree) + Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * x) : Math.round(Math.cos(degree) - Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * y));
//                    return newX ? x : y;
            }
        };
        f.addMouseListener(adapter);
        f.addMouseWheelListener(adapter);
        f.addMouseMotionListener(adapter);
        imgCmp.addMouseListener(adapter);
        imgCmp.addMouseWheelListener(adapter);
        imgCmp.addMouseMotionListener(adapter);

        f.pack();
        f.setMinimumSize(new Dimension(800, 600));
        f.setVisible(true);
        imgCmp.repaint();
    }

}

Da is jetz alles drin, was dafür relevant is. EIn Bild anzeigen, das man per Maus bewegen kann. Nun soll aber der Drehwinkel mit ins Spiel und da scheiterts bei mir


#8

mit System rangehen wird für größeres ohne Alternative bleiben,
für aktuell nur aus Rätselinteresse habe ich folgendes, was du im MouseListener ergänzen kannst:

int movedX = loc.x - mouseX;
int movedY = loc.y - mouseY;

double r = Math.toRadians(imgCmp.rotationDegree);
double cos = Math.cos(-r);
double sin = Math.sin(-r);
double mX = movedX * cos - movedY * sin;
double mY = movedX * sin + movedY * cos;

imgCmp.currX -= mX;
imgCmp.currY -= mY;

imgCmp.repaint();

loc.x = e.getX();
loc.y = e.getY();

currx + y habe ich auf Zugriff ohne Methoden testweise umgestellt,
und muss double werden für genaue Speicherung


vorher habe ich, kaum weniger blind als du rangegangen, erst nur beim Zeichnen den Einsatz von sin und cos versucht,
Ergebnisse auch gar nicht so übel, aber letztlich nicht ganz hilfreich, braucht dann bestimmt mehr von den afffinen Transformationen usw.

wenn es dich näher interessiert hier noch an Code fürs Zeichen:

zunächst ein kleiner Thread, der konstante Rotation bewirkt:

Runnable r = new Runnable() {
	@Override
	public void run() {
		try {
			while (true) {
				Thread.sleep(50);
				imgCmp.rotationDegree += 1;
				f.repaint();
			}
		} catch (InterruptedException e) {}
	}
};
new Thread(r).start();

in createAndShowGUI() eingefügt,

fürs Zeichen also:

Graphics2D g = (Graphics2D) gr;
double r = Math.toRadians(rotationDegree);
double mitteX = parent.getWidth() / 2;
double mitteY = parent.getHeight() / 2;

double cos = Math.cos(-r);
double sin = Math.sin(-r);
double drawX = mitteX + (currX - mitteX) * cos - (currY - mitteY) * sin;
double drawY = mitteY + (currX - mitteX) * sin + (currY - mitteY) * cos;

g.rotate(r, mitteX, mitteY);

int mx = (int) mitteX;
int my = (int) mitteY;
g.setBackground(Color.DARK_GRAY);
g.fillRect(-1000, -1000, 1000 + mx, 1000 + my);

// g.drawImage(imageIcon.getImage(), (int) drawX, (int) drawY, currW, currH, this);
g.drawImage(imageIcon.getImage(), (int) currX, (int) currY, currW, currH, this);

g.setColor(Color.YELLOW);
g.drawRect((int) drawX, (int) drawY, 5, 5);
g.setColor(Color.GREEN);
g.drawRect((int) currX, (int) currY, 5, 5);
g.setColor(Color.LIGHT_GRAY);
g.drawLine(-1000, my, 1000, my);
g.drawLine(mx, -1000, mx, 1000);

die hinteren Befehle malen ein kleines Koordinatensystem um den Mittelpunkt herum,
drawX/Y ist ein dank cos + sin konstanter (gelber) Punkt auf Bildschirm während der grüne currX/Y um den Mittelpunkt rotiert, wenn ein Thread wie genannt die Rotation ständig ändert,

mit drawImage auf drawX/Y kann man entsprechend dort Zeichnen testen,
für Verschieben aber dann wichtig, im MouseListener wieder auf normal umzustellen, nicht dort auch sin/ cos!

na besser ist eh im MouseListener sin/cos + Zeichnen normal auf currX/Y


#9

Im zuletzt geposteten Code wird “setRotationDegree” nicht mal aufgerufen…

EDIT: Insgesamt glaube ich, dass die Lösung des Problems recht einfach ist. Wenn klar genug ist, was das Problem ist. Aber das geht im dem Code etwas unter.


#10

Natürlich… Sagte ja hab die Gradzahl hardcodiert und es erstmal so zu testen. DIe Methode wird dann relevant, wenn ich ein GUI-ELement zum steuern einfüge, dafür muss aber das Grundproblem erstmal gelöst werden.

Und das Problem ist doch klar? Wie du selbst schon schriebst, wird bei “Graphics2D.rotate()” das Koordinatensystem gedreht und nicht das Bild. Also wenn ich das Bild um 90° wie im Beispiel drehe und meine Verschiebungsberechnungen so lasse, werden die Bilder weiterhin im Koordinatensystem korrekt verschoben, optisch allerdings dann in andere Richtungen, als die Maus bewegt wird. Um das wieder anzugleichen müssen die neuen X + Y Koordinaten in den Berechnungen nen evtl Drehungswinkel berücksichtigen, damit es selbst im gedrehten Koordinatensystem weiterhin nach oben wandert, wenn die Maus nach oben geht und nich sonstwohin. Allerdings scheitere ich daran das korrekt umzusetzen, da das Bild momentan entweder hin und her springt, mit 1mm Mausbewegung 1_000_000 Koordinaten zurücklegt oder sich weiterhin in falsche Richtungen bewegt. DAS ist das Problem. Führe das KSKB doch mit nem Beispielbild aus und lass die letzte return in der computeNewCoordinates drin und kommentier den rest aus. Du siehst es bewegt sich in optisch falsche Richtungen…

@SlaterB Vielen Dank für den Post, da war einiges aufschlussreiches drin. Ich werde mir das nachher genauer anschauen und weiter rumprobieren!


#11

Aaaalso. Man rotiert das Bild. Danach verschiebt man das Bild, im “gedrehten Koordinatensystem”.

Deine intention, das “erstmal mit 90° zu testen” ist an sich nicht falsch. Aber die Rotation um 90° ist so ein spezieller Fall, dass man davon ausgehen muss, dass selbst wenn es in diesem Fall funktioniert, es nicht unbedingt im allgemeinen Fall funktioniert. (Die 90° könnte man ja einfach mit x=oldY; y=-oldX abdecken…)

Wie auch immer: Wenn man mit der Maus eine Verschiebung macht, die 100 pixel nach rechts geht, und 50 pixel nach unten, dann sieht das auf dem Bildschirm so aus:

A-----------------+
                  |
                  |
                  |
                  |
                  B

Aber wenn das ganze um 45°C gedreht ist, dann muss diese Berechnung umgerechnet werden, so dass im Endeffekt das hier rauskommt:

A
 \
   \
     \
       \
         \
           \
             \
             /
           /
         /  
       B

Das ist nicht so schwierig: Man bestimmt sich den Verschiebungsvektor, der bei dir GROB mit sowas wie

int movedX = mouseX - loc.x;
int movedY = mouseY - loc.y;

berechnet wird (Vorzeichen hin oder her…)

Und dann berechnet man, wie dieser Vektor aussieht, wenn er um 45° gedreht ist. (Egal ob Celsius oder Farenheit).

double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double rotatedMoveX = ca * moveX + sa * moveY;
double rotatedMoveY = -sa * moveX + ca * moveY;

Ich habe mal versucht, das mit möglichst wenigen sonstigen Änderungen in deinen Code einzubauen. Aber…

Räum’ den Code auf!!!

Da ist im Moment schon so viel “schlecht und frickelig”, auf verschiedenSTen Ebenen, dass es nur frustrierend (oder noch frustrierender) werden kann, zu versuchen, da weiter dran rumzudengeln.

(Ja, ich weiß: “Du kritisierst nur, machst aber keine Verbesserungsvorschläge”. Üblicherweise mache ich sehr konkrete Verbesserungsvorschläge. Aber hier habe ich die schon in den anderen, verwandten Threads gemacht, und du hast sie ignoriert, deswegen liste ich sie hier nicht nochmal auf…)

Der code:

package bytewelt;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Point2D;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;

/**
 *
 * @author Andy
 */
public class BilderForum {

    private static class ImageComponent extends JComponent {

        private ImageIcon imageIcon;
        private double scaleFactor = 1.0;
        private boolean firstDraw = true;
        private int currX = 0;
        private int currY = 0;
        private int currH = 0;
        private int currW = 0;
        private int rotationDegree = 30;

        public ImageComponent() {
            super();
        }

        public void setImage(ImageIcon icon) {
            if (this.imageIcon != null) {
                this.imageIcon.getImage().flush();
            }
            this.imageIcon = icon;
            firstDraw = true;
            repaint();
        }

        public ImageIcon getImage() {
            return imageIcon;
        }

        public double getScaleFactor() {
            return scaleFactor;
        }

        public void setScaleFactor(double scaleFactor) {
            if (scaleFactor <= 0.01) {
                scaleFactor = 0.01;
            }
            this.scaleFactor = scaleFactor;
        }

        public int getCurrX() {
            return currX;
        }

        public void setCurrX(int currX) {
            this.currX = currX;
        }

        public int getCurrY() {
            return currY;
        }

        public void setCurrY(int currY) {
            this.currY = currY;
        }

        public int getCurrH() {
            return currH;
        }

        public int getCurrW() {
            return currW;
        }

        public int getRotationDegree() {
            return rotationDegree;
        }

        public void setRotationDegree(int degree) {
            this.rotationDegree = degree;
        }

        @Override
        @SuppressWarnings("empty-statement")
        protected void paintComponent(Graphics gr) {
            super.paintComponent(gr);
            if (imageIcon != null) {
                computeCurrWAndH();
                Container parent = this;
                while ((parent = parent.getParent()).getClass() != JFrame.class);

                if (firstDraw) {
                    currX = (parent.getWidth() - currW) / 2;
                }

                if (firstDraw) {
                    currY = (parent.getHeight() - currH) / 2;
                }
                setPreferredSize(new Dimension(currW, currH));
                Graphics2D g = (Graphics2D) gr;
                g.rotate(Math.toRadians(rotationDegree), parent.getWidth() / 2, parent.getHeight() / 2);
                System.out.println("Draw");
                g.drawImage(imageIcon.getImage(), currX, currY, currW, currH, this);
                firstDraw = false;
            }
        }

        private void computeCurrWAndH() {
            if (imageIcon != null) {
                int iw = imageIcon.getIconWidth();
                int ih = imageIcon.getIconHeight();
                double factorW = (double) getParent().getWidth() / iw;
                double factorH = (double) getHeight() / ih;
                double factor = Math.min(factorW, factorH);
                currW = (int) (factor * iw * scaleFactor);
                currH = (int) (factor * ih * scaleFactor);
            }
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> createAndShowGUI());
    }

    private static void createAndShowGUI() {
        JFrame f = new JFrame();
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().setBackground(Color.BLACK);

        ImageComponent imgCmp = new ImageComponent();
        imgCmp.setImage(new ImageIcon("image.png"));
        f.getContentPane().add(imgCmp, BorderLayout.CENTER);

        f.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                imgCmp.computeCurrWAndH();
                imgCmp.setCurrX((f.getWidth() - imgCmp.getCurrW()) / 2);
                imgCmp.setCurrY((f.getHeight() - imgCmp.getCurrH()) / 2);
                imgCmp.repaint();
            }
        });
        
        //Framelistener
        MouseAdapter adapter = new MouseAdapter() {
            private boolean firstPress = false;
            private Point loc;
            private int pressedButton = MouseEvent.NOBUTTON;

            @Override
            public void mousePressed(MouseEvent e) {
                switch (pressedButton = e.getButton()) {
                    case MouseEvent.BUTTON1:
                        if (!firstPress) {
                            firstPress = true;
                            int mouseX = e.getX(), mouseY = e.getY();
//                            mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
//                            mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());
                            loc = new Point(mouseX, mouseY);
                            System.out.println(loc);
                        }
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                firstPress = false;
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                if (loc != null && pressedButton == MouseEvent.BUTTON1) {
                    int mouseX = e.getX(), mouseY = e.getY();

                    System.out.println("MouseX1: " + mouseX + "; MouseY1: " + mouseY);

//                    mouseX = computeCoordinate(true, mouseX, mouseY, imgCmp.getRotationDegree());
//                    mouseY = computeCoordinate(false, mouseX, mouseY, imgCmp.getRotationDegree());

                    System.out.println("MouseX2: " + mouseX + "; MouseY2: " + mouseY);

                    int movedX = mouseX - loc.x;
                    int movedY = mouseY - loc.y;

//                    int newCurrX = imgCmp.getCurrX() - movedX;
//                    int newCurrY = imgCmp.getCurrY() - movedY;
//
//                    System.out.println("NewX1: " + newCurrX + "; NewY1: " + newCurrY);
//
                    //newCurrX = computeCoordinate(true, newCurrX, newCurrY, imgCmp.getRotationDegree());
                    //newCurrY = computeCoordinate(false, newCurrX, newCurrY, imgCmp.getRotationDegree());

//                    System.out.println("NewX2: " + newCurrX + "; NewY2: " + newCurrY);

//                    System.out.println(imgCmp.getRotationDegree());
                    
                    Point2D movement = computeMovement(movedX, movedY, Math.toRadians(imgCmp.getRotationDegree()));
                    int newCurrX = (int)(imgCmp.getCurrX() + movement.getX());
                    int newCurrY = (int)(imgCmp.getCurrY() + movement.getY());
                    
                    imgCmp.setCurrX(newCurrX);
                    imgCmp.setCurrY(newCurrY);
                    imgCmp.repaint();

                    loc.x = e.getX();
                    loc.y = e.getY();
                }
            }

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                int hBef = imgCmp.getCurrH();
                int wBef = imgCmp.getCurrW();
                imgCmp.setScaleFactor(imgCmp.getScaleFactor() - (e.getUnitsToScroll() / 100.0));
                imgCmp.repaint();
                imgCmp.computeCurrWAndH();
                imgCmp.setCurrX(imgCmp.getCurrX() - (imgCmp.getCurrW() - wBef) / 2);
                imgCmp.setCurrY(imgCmp.getCurrY() - (imgCmp.getCurrH() - hBef) / 2);
            }

            // This is crap
//            private int computeCoordinate(boolean newX, int x, int y, int degree) {
//                degree *= -1;
//                degree = (int) Math.round(Math.toRadians(degree));
//                return (newX ? (int) Math.round(x * Math.cos(degree) + y * Math.sin(degree)) : (int) Math.round(x * Math.sin(degree) - y * Math.cos(degree)));
////                return (int) (newX ? Math.round(Math.cos(degree) + Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * x) : Math.round(Math.cos(degree) - Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * y));
////                    return newX ? x : y;
//            }
            
            private Point2D computeMovement(double dx, double dy, double angleRad) {
                double ca = Math.cos(angleRad);
                double sa = Math.sin(angleRad);
                double x = ca * dx + sa * dy;
                double y = -sa * dx + ca * dy;
                return new Point2D.Double(x, y);
            }
            
        };
        f.addMouseListener(adapter);
        f.addMouseWheelListener(adapter);
        f.addMouseMotionListener(adapter);
        imgCmp.addMouseListener(adapter);
        imgCmp.addMouseWheelListener(adapter);
        imgCmp.addMouseMotionListener(adapter);

        f.pack();
        f.setMinimumSize(new Dimension(800, 600));
        f.setVisible(true);
        imgCmp.repaint();
    }

}

#12

Bei dir sieht das so einfach aus… Und ja, am “clean-code” muss ich noch viel arbeiten. Liegt aber hauptsächlich auch dran das ich “einfach los” programmiere, ohne mir vorher irgend ne Struktur zurechtzulegen oä. Da passiert das sehr schnell, weiß auch denke ich welche Punkte du teilweise meinst. Versuche da mal mit mehr Struktur dranzugehen, nervt mich ja selber.

Aber nochmal zu deinem Code: Das du nen Punkt zurückgibst, anstatt beide Berechnungen von nem boolean abhängig zu machen ist schonmal ein guter Weg, hab ich nicht daran gedacht. Als deiner funktioniert perfekt wie ich es will.
Nur hab ich nich so ganz verstanden, warum deine funktionieren und meine nicht? Die Formeln sind im Prinzip die gleichen (Mal von abgesehen, das deine Variante deutlich hübscher is), nur du hast die Ursprungskoordinaten dazuaddiert. War das schon des Rätsels Lösung? Oder überseh ich nen Entscheidenden Punkt?

Könnte das ganze natürlich dankend annehmen und so lassen, aber… Möchte das gerne verstehen, das ich in Zukunft Bescheid weiß, wie der Kram funktioniert

Und zu den Verbesserungsvorschlägen: Das da oben war schon mehr als erwartet^^ Und die anderen hab ich nie “ignoriert”, eher gescheitert an der Umsetzung bzw. in alte Muster zurückgefallen.


#13

was soll man groß sagen wenn die Lösung für Addition von a und b lautet: a+b
und jemand anders schreibt a*b*b*b/435 oder so, braucht es da noch Hinweise?

der größte und wichtigste Hinweis zu allem und jeden lautet,
Code nicht aus dem Weltraum zu betrachen, sondern mit der Lupe,
ganz genau prüfen was passiert, was da fabriziert,

das ist das schöne am Code im Gegensatz zu fertigen Auto, man kann noch jedes einzelne Detail, jeden Arbeitsschritt im Ablauf, jeden Zwischenschritt in er Rechnung immer ziemlich genau prüfen,
mit Papierrechung vergleichen,

es hilft natürlich auch, eine grundsätzliche Ahnung zu haben, was passieren soll/ wird, was etwa bei Math.toRadians(degree) der grundsätzliche Wertebereich ist,

das mit System.out.println() für Beispielwerte genau auszugeben und dann zu verstehen, dass allein schon degree = (int) Math.round(Math.toRadians(degree)); mit Rundung auf ganze int ziemlich fatal ist, tja, dafür braucht es mal wieder das gewisse Programmierer-Gen: alles sekptisch hinterfragen und nachprüfen, spätestens im Fehlerfall

zur Berechung von newX mit Math.round(Math.cos(degree) + Math.sin(degree) * Math.sin(degree) * Math.cos(degree) * x) ohne Berücksichtungen von y (!), dafür mit Addierung des Absolutwert vom cos (!) und dann noch sin^3 (!),
tja, das ist dann eben mein initiales Beispiel, wie man auch a+b verhunzen kann,

dazu fällt nun niemanden etwas ein, weil du ja auch gewiss nirgendwo (habe nicht alles gelesen) mit Worten beschreibst, wie du auf solch verrückte Sachen kommst,
die Formel hast du am Anfang mal korrekt von extern gepostet, wie zu sehen Marco13’s Variante (oder auch meine) dem relativ nahe,

man welchselt einfach nie ohne Plan zu sin*sin*sin oder meinetwegen auch sin*sin*cos, kann man das so als Grundregel an die Pinnwand schreiben und wieder was gelernt? :wink:

(edit: ich finde das sin*sin*cos gerade nur noch in auskommentierten Bereichen, habe ich mich verschaut? dann ja immerhin schon Fortschritt und vielleicht das von Marco13 weiter unten zu x + y die größere Ursache, genaue Untersuchung der Werte hilft, auch wenn es viele Mouse-Events gibt)


#14

Wie SlaterB schon in seiner bekannt liebenswürdigen Art geschrieben hat: Dein Code hat “irgendwas mit Sinus und Cosinus” ausgerechnet. Und nachzuvollziehen, was dort gerechnet wird, und wie das mit dem zusammenhängen könnte, was du berechnen wolltest (oder, ggf. unabhängig davon, mit dem was “richtig” wäre), ist schwierig…

Bei meinem Hinweis zum “Aufräumen” ging es nicht um “Clean Code”, sondern um das elementarste, worum es beim Programmieren gehen kann, nämlich eine ungefähre Ahnung zu haben, was der Code bedeutet, den man selbst hinschreibt.

Als Beispiel:

int movedX = loc.x - mouseX;
int movedY = loc.y - mouseY;

int newCurrX = imgCmp.getCurrX() - movedX;
int newCurrY = imgCmp.getCurrY() - movedY;

newCurrX = computeCoordinate(true, newCurrX, newCurrY, imgCmp.getRotationDegree());
newCurrY = computeCoordinate(false, newCurrX, newCurrY, imgCmp.getRotationDegree());

Was ist movedX und movedY? Ja, das ist die Bewegung von der aktuellen Mausposition zur vorherigen (!) Mausposition.

Was ist newCurrX und newCurrY? Das sind die verschobenen Koordinaten des Bildes (negativ, weil movedX und movedY ja “falschrum” sind…).

Aber dann werden diese verschobenen Koordianten in eine Funktion gesteckt, die … irgendwas berechnet, und neue Werte zurückgibt, wobei newCurrX wenn es an den zweiten Aufruf dieser Funktion übergeben wird, ja schon einen neuen Wert hat (!!!). Betrachte analog dazu diesen Code

int x = 4;
int y = 5;
x = add(x, y);
y = add(x, y);

und überleg’ dir, warum danach sicher nicht y==9 gilt…


#15

Das macht durchaus Sinn und wenn man das so sieht ist einem das immer direkt klar… Denke das Problem ansich kam ja überhaupt erst auf, weil ich nicht auf den Punkt-Rückgabewert kam und das durch nen boolean regeln wollte. Dein letztes Beispiel hätte da schon gereicht, da das natürlich so nicht funktionieren kann^^.

Aber vielen Dank nochmal dafür!

@Slater: Die andere Formel hab ich weiter oben gepostet, die ich im Netz noch gefunden habe und ausprobiert. Natürlich löst sich das wieder auf, aber wollte die mal ausprobieren, evtl hätte ich auch nurn Denkfehler gehabt. Aber das ich mich mal genauer damit befasse, was diese Funktionen jetz machen und nich nur mich damit abgebe, was das Ergebnis is, wäre echt oft schon hilfreich…