Aus der Kategorie “zu doof für Mathematik, aber intelligent genug um sich trotzdem was einfallen zu lassen”: Heute habe ich mir mal wieder ausgiebig meinen alten Quellcode für meine Spacola-Maussteuerung vom August 2010 angesehen und mich daher auch mal wieder ein bisschen mit den mathematischen Grundlagen (Trigonometrie und Vektorarithmetik) auseinandergesetzt. Ein wenig Schmunzeln musste ich über meine unkonventionelle Lösung eines alten Problems, das ich damals hatte.

Man nehme einen Mauszeiger, der irgendwo in einem karthesischen Koordinatensystem liegt. Nun musste ich herausfinden, welchen Winkel der Richtungsvektor Ursprung->Mauszeiger mit der Y-Achse einschließt. Da ich selten mit der Mathematik einer Meinung war, aber dafür umso mehr gewillt, diese Aufgabe von alleine zu lösen, nahm ich mir seinerzeit Papier und Bleistift zur Hand und fing damit an, mir eine Lösung zu überlegen. Und ich fand einen Algorithmus, mit dem ich den Winkel näherungsweise bestimmen konnte.

Aber zunächst zur idealen und simpelsten Lösung:

trigo1Man benötigt im Prinzip nur Delta X und Delta Y zwischen den beiden Punkten, also den Ortsvektor. Anschließend teilt man Delta Y durch den Betrag des Vektors und berechnet aus diesem Wert den Arkus-Kosinus. Das Ergebnis muss dann noch mit 360 multipliziert und wieder durch 2 Pi geteilt werden. Schnell und effizient. Ich bin kein Mathegott, aber ein paar Minuten Suchen hat mich auf die richtige Lösung gebracht.

	public static int calculateDegrees(int pointX, int pointY){

		int deltaX = pointX - centerX;
		int deltaY = centerY - pointY;

		double alpha = Math.acos( deltaY / Math.sqrt( Math.pow( deltaX, 2 ) + Math.pow( deltaY, 2 ) ) );
	
		alpha = alpha * 360 / ( Math.PI * 2 );
		
		if(deltaX < 0)
			alpha = 360.0 - alpha;
	
		return (int) alpha;
	
	}

Witzig fand ich dagegen meinen Algorithmus, der folgendermaßen aussieht:

trigo2Ich habe beliebig viele Punkte (im Extremfall 360 Stück, in meinem Fall aber nur 40) auf einem Einheitkreis um den Mittelpunkt generiert und von jedem dieser gedachten Punkte den Abstand zur Mauszeigerkoordinate bestimmt. Mein gedanklicher Ansatz war der, dass der Punkt, der den kürzesten Abstand zur Zielkoordinate hat, mehr oder weniger exakt denselben Winkel zur Y-Achse hat. Mit dem Vorteil, dass ich dessen Winkel (da selbst generiert) genau kannte. Anschließend habe ich noch eine merkwürdige Umrechnung vom Bogenmaß ins Gradmaß durchgeführt. Das Ergebnis war zufriedenstellend.

public void calculateDegrees(){
		
	int xPos = Config.SCREEN_WIDTH / 2 + 10;
	int yPos = Config.SCREEN_HEIGHT / 2;
	int cnt = 0, diff = 10000, targetCnt = 1000;

	for (double i = 0; i <= 2 * (Math.PI); i += (2 * (Math.PI)) / 40) {

		xPos = Config.SCREEN_WIDTH / 2 + (int) (Math.cos(i) * Math.abs(Config.SCREEN_WIDTH / 2	- cursorCoordX));
		yPos = Config.SCREEN_HEIGHT	/ 2 - (int) (Math.sin(i)*(-1) * Math.abs(Config.SCREEN_HEIGHT / 2 - cursorCoordY));
		
		if (Math.abs(cursorCoordX - xPos) + Math.abs(cursorCoordY - yPos) < diff) {
			diff = Math.abs(cursorCoordX - xPos) + Math.abs(cursorCoordY - yPos);
			targetCnt = cnt;
		}
		cnt++;
	}
	int newDegrees = (90+(targetCnt * (360 / 40))) % 360;
		
	setDegrees(newDegrees);
		
}

Für letzteres Codebeispiel schlagt mich bitte nicht. Ich wusste damals schon, dass das nur ein Provisorium ist. Ich weiß noch nicht einmal mehr wofür “targetCnt” steht.

Zugegeben, meine Lösung ist extrem unperformant und relativ ungenau, aber nichts stärkt das Ego mehr als die Gewissheit, ein Problem selbst und ohne Hilfe bewältigt zu haben, wenn man es sich vorher nicht zugetraut hätte. Wie dem auch sei, heute habe ich meinen provisorischen Algorithmus abgelöst und durch die exakte Berechnung ersetzt.