java.lang.StackOverflowError vermeiden?

Hallo, Konrad, du bist aber auch überall :slight_smile:

Ja die 15 Pixel kommen einfach daher weil ich beim Farbablesen und so nicht will dass die Mitte eines Steins benutzt wird sondern ein Pixel so oben links im Stein (wie man ja sieht auf dem Bild sieht hat jeder Stein aussenrum einen dunklen Rahmen, in der unteren Hälfte einen dunklen und oben einen hellen Bereich sowie ein Symbol.
Zum Farbvergleich will ich bevorzugt die helle Farbe im oberen Steinbereich nehmen. daher auch so grob oben links auf dem Stein den Farbvergleich)
Hatte es anfangs durchaus mit sowas wie deltax/5.0 versucht aber irgendwie funktionierte das nicht so recht. Und da es mir mittlerweile zu nervig wurde, habe ich das +15 gehardcoded weil das so eine halbwegs passende Entfernung von der Steinecke oben links ist.

Ich benutze nur den Robot, ausschließlich.
Irgendwie scheint die Farberkennung so halbwegs zu passen positionsmässig (wobei manchmal komischerweise eine -1 als farbe bestimmt wird, also doch manchmal an eienr falschen Stelle geguckt wird.)

Nur wenn ich auf die praktisch selben koordinaten wie beim Farbablesen (Formeln sind ja Diesselben) mousemove, verhält es sich… komisch.
Ich versuche mal ein Video aufzunehmen wie genau sich die Maus bewegt.
Die Mausbewegungen ergeben teilweise gar keinen Sinn, innerhalb einer Zeile sollte es sich ja schließlich simpel bei gleicher y komponente um einen festen Abstand nach rechts bewegen.

Bin mir auch bis jetzt nicht sicher ob das Koordinatensystem des Robots nun von 0,0 bis 1919,1079 geht. Oder ob es skalierfaktorbedingt (skalierung 125%) etwas Anderes ist.

Wobei übrigens die 8 Pixel „Messfehler“ schon recht viel ausmachen können wenn man bedenkt dass ein Stein nur so 60x60 Pixel breit ist.

Video
Ich hab jetzt mal ein Video gemacht wie sich der Cursor durch das laufende Programm bewegt.
Ich habe ihn anfangs oben rechts positioniert,
Alle weiteren bewegungen kommen durch die Roboter.mouseMove() Befehle zustande.

Ich verstehe nicht wie der Robot da so „daneben“ liegen kann während aber gleichzeitig die Farbanalyse mittels getPixelColor mehr oder minder immer die Steine trifft. O_o

Hm, also jetzt blicke ich gar nichts mehr.
Die Eckkoordinaten des Feldes hatte ich mit dem Code

        while (true) {

            b = MouseInfo.getPointerInfo().getLocation();
            x = (int) b.getX();
            y = (int) b.getY();
            Color color = bot.getPixelColor(x, y);
            System.out.println("x=" + x + "; y=" + y + "; green=" + color.getGreen() + "; blue=" + color.getBlue()
                    + "; red=" + color.getRed());
            bot.delay(200);
        }

abgelesen, also namentlich mit MouseInfo.getPointerInfo().getLocation().

Jetzt habe ich aber mal just for fun diese abgelesenen Koordinaten (die ja auch hardcoded im Code stehen als topleftx usw.)
und mousemove damit benutzt.
die kordinaten sind oben links bzw. unten rechts jenseits des Feldes, heißt die von mouseinfo erhaltenen koordinaten nehme das Feld „größer“ war als es ist.
Ich würde jetzt noch verstehen wnen Alles nahc unten rechts gestreckt wäre oder so.
Aber kann das Feld als ein größeres Rechteck wahrgenommen werden als es real ist?
Da müsste ja , statt von oben links, vond er Bildschirmmitte aus gestreckt worden sein? O_o

Ich hasse das, warum kann Java kein braves programm sein und einfach die realen Koordinaten (0,0) bis (1920,1080) einfach so übernehmen wie sie sind?

Das liegt einfach daran, dass du mehr als einen Monitor hast. Für Java sind alle Monitore zusammen eine große Fläche, wenn du die Location auf einem Monitor haben willst, musst du das noch berechnen

Point location = MouseInfo.getPointerInfo()                
            .getLocation();
    
    
Rectangle bounds = MouseInfo.getPointerInfo()
            .getDevice()
            .getDefaultConfiguration()
            .getBounds();
    
    
Point wantedLocation = new Point(location.x - bounds.x, location.y - bounds.y);
    
System.out.println(location);
System.out.println(wantedLocation);

Also ich habe ehrlich gesagt noch immer ein paar Probleme, zu verstehen, was Du genau machst.

Mit der MouseInfo Zeile bekommst Du auch Screen Koordinaten - die sind identisch zu den Koordinaten des Roboters.

Also einfach mal ein kleines Test-Programm:

package de.kneitzel;

import javax.swing.*;
import java.awt.*;

public class ScreenPointerTest {
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame("MouseTest");
        frame.setSize(800, 600);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        JTextArea area = new JTextArea();
        frame.add(area);
        frame.setVisible(true);

        Robot robot = new Robot();

        while(true) {
            Point posBeforeMove = MouseInfo.getPointerInfo().getLocation();
            robot.mouseMove(posBeforeMove.x, posBeforeMove.y);
            Point posAfterMove = MouseInfo.getPointerInfo().getLocation();
            SwingUtilities.invokeLater( () -> area.setText(posBeforeMove + " - " + posAfterMove));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {}
        }
    }
}

Das ist nicht schön, aber es hilft bei einem ersten Test. Wir haben ein Fenster, in dem wir was ausgeben. Das ist aber erst einmal egal. Der Thread außerhalb macht etwas anderes:

  • Die Position der Maus wird gelesen
  • Die Position der Maus wird dann gesetzt
  • Dann wird die Position erneut gelesen.

Da wirst Du dann sehen: Die Maus verändert die Position nicht.

Um zu sehen, ob es funktionieren würde, könnte man die Position verändern - also bei x und y jeweils noch ein +10 oder so.
==> Dann springt die Maus jede Sekunde ein kleines Stück und man sieht in der Ausgabe auch die veränderte Position.

Das sind so Tests, mit denen ich mir immer etwas erarbeiten würde. Dieses Programm kannst Du z.B. Nutzen um dann die Koordinaten auf dem Bildschirm zu bestätigen: Wo sind die einzelnen Eckpunkte?
Dann kannst Du schauen: Berechnest Du die korrekt? (Das wäre bei mir eine eigene Klasse. Die bekommt dann im Konstruktor die äußeren zwei Eckpunkte und berechnet dann alles:
→ Koordinaten der Grid in Screen Punkte (für Klick)
→ Screen Punkte in Grid Koordinaten (Brauchst Du evtl. nicht, aber das kann man nutzen für Tests. Also z.B. die Applikation oben angepasst um dann die zwei äußeren Eckpunkte zu setzen um dann bei der Maus immer die Grid Koordinaten angezeigt zu bekommen.

Wichtig ist halt, alles in Schritten zu entwickeln. Dabei Teile entwickeln, die man testen kann (Und deren Entwicklung erst abgeschlossen ist, wenn diese Tests komplett abgeschlossen sind. Diese tests sind in der Regel automatische Tests (sogenannte Unit Tests) aber auch so manuelle Tests sind am Anfang durchaus ok.

@Ebi:
Ich habe nicht mehrere Monitore.Höchstens mein notebook versteckt noch irgendwo einen Monitor vor mir, von dem ich ncihts weiß :wink:

@all:
Okay, ich habe glaube ich mein Problem auf eine recht unerwartete Art gelöst:
Sache war ich hatte die von der Uni vorgegebene Eclipse Version, Workspace, Eisntellungen und Co. benutzt.
Diese waren auf java 8 eingestellt und auch Eclipse war eine Asbach Uralt Version.

Ich habe also komplett frisch die neueste Eclipse Version sowie Java 18 installiert, meine Projekte da rein importiert, wieder getestet.
Diese komischen „Ausbrecher“ bei den Positionen hatte ich shcon mal nicht, nur war das Ganze einfach zu weit unten rechts angesiedelt.
Hier kam nun wieder gute alte ich könnte so kotzen wenn ich den Rotz sehe dpi Skalierfaktor ins Spiel, den Java in seiner Dämlichkeit einfahc nicht ignorieren kann.

Also überall ein schlichtes /1.25 rangebaut und schon wird überall da geklickt, Farben abgelesen und Co. wie es sollte :slight_smile:

TL;DR:
Alte, von der Uni so vorgegebene und vorkonfigurierte Eclipse und Java version waren für den ganzen Unsinn verantwortlich.
Und halt das Bullshit DPI Scaling, das man in java mit einem Faktor 1/skalierfaktor kompensieren muss.

Das kann ich nicht nachvollziehen. Auch mit Skalierungsfaktor sind die Koordinaten gleich.

Unter dem Strich bedeutet es nur, dass Deine Werte, die Du angenommen hast für die Eckpunkte falsch waren. Das waren dann keine Bildschirm-Koordinaten sondern irgend etwas anderes, das Du irgendwoher hattest.

Du musst also in Deinem Code nichts wild mit Skallierungsfaktor Rechnen. Du kannst überall mit Bildschirm-Koordinaten rechnen (Auch bei mehreren Bildschirmen!) - alles was Du nur brauchst sind eben die Bildschirmkoordinaten des Grids auf dem Bildschirm.

Naja, ich hbe wie gesagt immer mit Mouseinfo… die koordinaten abgelesen.
Die beziehen sich auch offensichtlichen auf die reale Auflösung von 1920x1080.

Der Robot hingegen geht davon aus dass der Screen nur (1920/1.25) mal (1080/1.25) lang ist.
Keine Ahnung warum, Java wurde halt mies programmiert,
aber während mouseinfo den Skalierungsfaktor ignoriert bzw. als 1.0 annimmt,
beachtet der Robot den Faktor.

Egak wie rum, wenn ich die mit mouseinfo gemessenen koordinaten nehme und durch 1.25 teile, kriege ich faktishc koordinaten im koordinatesystem des Robots.

Und wie geagt, ich habe immer noch keine mehreren Bildschirme.
Nur den, den man auch im Video sieht.
Höchstens mein notebook ist vielleicht ein Transformer mit anderen Bildshcirmen, aber ich weiß mal nur von Einem :slight_smile:

Warum das Alles so sit, weiß ich auch nicht.
Wobei ich irgendwo las dass bei Java mouseinfo die Windows Api Methode getCurrPos oder so benutzt.
Und Robot nutzt wohl irgendwie andere „dpi aware“ Koordinaten oder so

Also ich habe bei mir ja auch mit dem Test-Programm getestet und auch wenn ich z.B. auf 125% gegangen bin, haben Robot und MouseInfo mit den gleichen Koordinaten gearbeitet. Da war an keiner Stelle irgend etwas zu rechnen.

Hast Du denn mal diese kleine Test-Routine auch bei dir laufen lassen?

Mich würde also wirklich einmal der Test mit meinem Programm interessieren. Ebenso mehr Details zu dem System. Windows 10 Laptop ohne externen Monitor? Und Skalierung auf 125%

Was ich gefunden habe in der Doku:

The coordinate system used for the mouse position depends on whether or not the GraphicsDevice is part of a virtual screen device. For virtual screen devices, the coordinates are given in the virtual coordinate system, otherwise they are returned in the coordinate system of the GraphicsDevice

Damit kann es durchaus so sein, dass da irgendetwas ist. Da wäre dann aber auch interessant, wie da vorzugehen ist. So findet sich z.B. auch eine AffineTransform wenn man sich das Device und dessen Konfiguration anschaut. Evtl. kann man damit etwas machen.

Das mag für Dich evtl. nicht notwendig scheinen, so Du nur auf Deinem Rechner arbeiten willst. Aber Dein Rechner und mein Rechner verhalten sich anscheinend schon unterschiedlich - dein Code würde hier also nicht funktionieren. Daher die Frage, was hier zu tun wäre, damit es universell laufen würde.

Ich habe gerade einmal deinen Code von weiter oben getestet.
Die im Fenster angezeigten Werte stimmen

  1. überein und
  2. gehen bei mir aber nur von (0,0) (cursor links oben im eck) bis (1535,863)(cursor rechts unten im eck des bildschirms)

Ich habe den Code in meinem „aktuellen“ Eclipse getestet.

Ich kann später den selben Code auch mal noch in der „alten“ Eclipse Umgebung (java 8, etc.) testen.

Edit:
Ich habs in der alten Eclipse+Java Version gerade getestet:
Da sind die 2 Koordinaten auch gleich, gehen allerdings bis (1919,1079).
Heißt in der alten Version gibt er mir Koordinaten gemäß der Auflösung aus, in der neuen Eclipse+Java Kombi kriege ich die Werte mit dem Faktor 1/1.25 multipliziert.

Danke fürs testen. Dann stammten die ermittelten Werte noch vom Java 8 nehme ich an. Dann könntest Du vermutlich die Werte neu ermitteln und dann die ganzen Kalkulationen sein lassen.

Ist diese Sichtweise korrekt oder habe ich etwas übersehen?

1 „Gefällt mir“

Ist jetzt shcon wieder ne Weile her aber irgendwo hatte ich beim Googeln heute auch was gelesen, mehrere Bug Report wo java 8 und der Robot nicht so zusammenpassten bzgl. Koordinaten.
Also generell schien unter java 8 das mit den koordinaten ziemlich zu spinnen.
Und die Uni nutzt natürlich genau java 8, „weil wir einige Funktionen nutzen die es in späteren java Versionen nicht mehr gibt“. Fragt sich nur Welche -.-

Öh, habs mal gerade getestet im neuen Eclipse.
Ich habe ja diese Klasse, die ich immer laufen lasse wenn ich von was die Position und die rgb Werte brauche:

import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;

public class FarbeUnterCursor {

    public static void main(String[] args) throws Exception {
        //System.setProperty("sun.java2d.uiScale", "1");
        Point b;
        int x;
        int y;

        Robot bot = new Robot();
        bot.delay(1000);

        while (true) {

            b = MouseInfo.getPointerInfo().getLocation();
            x = (int) b.getX();
            y = (int) b.getY();
            Color color = bot.getPixelColor(x, y);
            System.out.println("x=" + x + "; y=" + y + "; green=" + color.getGreen() + "; blue=" + color.getBlue()
                    + "; red=" + color.getRed());
            bot.delay(200);
        }
    }

}

(Die erste Kommentar in der Main methode ist neu und hatte ich nie dabei).
Wenn ich, wie bisher die Methode ganz normal laufen lasse, gehen die Koordinaten tatsächlich bis 1535, 863, wie du shcon vermutest.
Falls ich den Kommentarbefehl mit drin habe, gehen die Koordinaten bis 1919,1079, also ohne Skalierung mit drin.

Jedenfalls, wie du vermutest, ohne Rumfingern an den Systemproperties scheinen die Koordinaten nun allso schon diesen 1/Skalierung Faktor mit drin zu haben und umrechnen ist unnötig :slight_smile:
Müsste ich die 4-5 Punkte nur nochmal nachmessen, dann kann man die Rechnerei weglassen.
Wäre ja auch recht nice dann :slight_smile:

1 „Gefällt mir“

Dies ganzen Skalierungsfragen waren doch schon in Java Bildschirmkoordinaten vs Bilder Dimensionen - #4 von Marco13 ein Ärgernis. Anscheinend ist es schwierig, da definitive Antworten zu bekommen…

1 „Gefällt mir“

Definitiv, ich verstehe nicht waurm Java da nicht einfach die nackten kalten, auf die normale Auflösung bezogenen Koordinaten benutzt und angibt.
Statt in der einen Klasse und Methode irgendwelche skalierten Koordinaten zu erwarten und anderswo wieder ohne Skalierung zu arbeiten…

Naja, nun kriege ich wenigstens überall (Alles was die Robot Klasse angeht) nur skalierte Koordinaten, damit lässt sich dann wiederum arbeiten :slight_smile:

Das wäre aus meiner Sicht falsch. Die physikalischen Koordinaten gehen eine Applikation (normalerweise) nichts an. Das Betriebssystem ist verantwortlich für die Skalierung. Dazu hat es den physikalischen Bildschirm auf einen virtuellen Bildschirm zu transformieren. Das ist ähnlich wie ein Umschalten der Auflösung - wo dann ggf. der Monitor das skalieren würde auf eine physische Größe.

Muss ich jetzt nicht zwingend verstehen, ich sehe nur „meine Auflösung ist 1920 x 10980“, dann erartet man eigentlich darauf bezogene Pixelangaben. Zumindest ich tue das :slight_smile:

Habe jetzt auch nicht wirklich den plan was jetzt der Computer intern noch mit dpi, inches und Co. umrechnet.

Mal eine andere Idee, wie wäre es nicht Pixelkoordinaten, sondern die relative Positionen zu nehmen? Auf der GPU z.B. werden Bildschirmkoordinaten immer zwischen 0 und 1 angegeben, wobei der Punkt (0.5|0.5) dann die Mitte des Bildschirms wäre, also 50% Höhe, 50% Breite. Könnte man doch hier auch machen, oder nicht?

So eine Normalisierung ist oft gut und richtig und sinnvoll. Der Punkt ist halt, dass man irgendwann den Robot oder PointerInfo verwenden will, und das alles nur auf Pixelkoordinaten arbeitet, und man genau wissen muss, was man dort wie umrechnen muss, um diese ~„virtuelle Skalierung“ richtig einzubeziehen. Im verlinkten Thread hatte ich da mal kurz geschaut (aber habe nicht mit eigenen Skalierungen rumprobiert - das würde mir hier zu viel raushauen…), aber es ist schon eine Weile her, und irgendwann kamen keine Antworten mehr. Ganz grob könnte ein Problem halt (im Pseudocode) sein:

// Maus ist ganz unten rechts auf einem 1600x1200-Screen:
Point mousePosition = PointerInfo.getLocation();
System.out.println(mousePosition); // Gibt 1600,1200 aus

// Versuche die Maus um 10 pixel nach oben links zu bewegen:
robot.moveTo(mousePosition.x-10, mousePosition.y-10);

Und es könnte sein, dass die Maus jetzt bei falschen Koordinaten landet, weil im einen Fall in den Koordinaten eine 125%-Skalierung drinsteckt (oder angenommen wird) und im anderen nicht. Da müßte man mehr lesen und/oder ausprobieren (manchmal scheint das nicht explizit dokumentiert zu sein - aber vielleicht kann man eine allgemeine Regel ableiten…)

1 „Gefällt mir“

Hilft eigentlich nur, dass es die Lib-Entwickler von Java selber machen ^^ Das ist ja grausig.