Im ersten Beispiel folge ich ganz der Aufgabenstellung und gehe von einem Vektor aus, der durch die Summe seines Ortsvektors mit einem skalaren Vielfachen seines Richtungsvektors definiert wird. Die Richtungszuweisungen der anderen geforderten Vektoren sind ja eigentlich schon alle in der Aufgabenstellung verraten worden, das einzige, was noch ergänzt werden müsste, ist die Auseinandersetzung mit dem Satz des Pythagoras, mit dem man den Betrag des Vektors aus der Wurzel der Summe der Quadrate seiner Richtungsvektorkoordinaten berechnen kann. Da das Ganze ziemlich anspruchslos ist, habe ich mir erlaubt, noch eine kleine Prozedur hinzuzufügen, mit der man den Schnittpunkt mit einem anderen Vektor bestimmen kann.
BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] Graphics 800,600
Local BildschirmMitteX:Int = GraphicsWidth()/2 Local BildschirmMitteY:Int = GraphicsHeight()/2
Local MausVektor:TVektor = TVektor.erschaffen([BildschirmMitteX, BildschirmMitteY], [MouseX() - BildschirmMitteX, MouseY() - BildschirmMitteY]) Local GegenVektor:TVektor = TVektor.erschaffen([BildschirmMitteX, BildschirmMitteY], [-MausVektor.Richtung[0], -MausVektor.Richtung[1]]) Local SenkrechtVektor:TVektor = TVektor.erschaffen([BildschirmMitteX, BildschirmMitteY], [-MausVektor.Richtung[1], MausVektor.Richtung[0]])
Local ZufallsVektor:TVektor = TVektor.erschaffen([Rand(200,GraphicsWidth()-200), Rand(200,GraphicsHeight()-200)], [Rand(-200,200), Rand(-200,200)])
While Not KeyDown(KEY_ESCAPE) And Not AppTerminate() Cls
MausVektor.NeueRichtung([MouseX() - BildschirmMitteX, MouseY() - BildschirmMitteY]) MausVektor.Zeichnen(255, 0, 0) DrawText "MausVektor", 10, 10 GegenVektor.NeueRichtung([-MausVektor.Richtung[0], -MausVektor.Richtung[1]]) GegenVektor.NeuerBetrag(100) GegenVektor.Zeichnen(255,255,0) DrawText "Entgegengesetzter Vektor mit genormten Betrag von 100 Pixeln", 10, 30 SenkrechtVektor.NeueRichtung([-MausVektor.Richtung[1], MausVektor.Richtung[0]]) SenkrechtVektor.NeuerBetrag(MausVektor.ErrechneBetrag()/2) SenkrechtVektor.Zeichnen(0,255,0) DrawText "Orthogonaler Vektor, halb so lang wie MausVektor", 10, 50 If MouseHit(1) Then ZufallsVektor = Null ZufallsVektor = TVektor.erschaffen([Rand(200,GraphicsWidth()-200), Rand(200,GraphicsHeight()-200)], [Rand(-200,200), Rand(-200,200)]) End If ZufallsVektor.Zeichnen(0,255,255) DrawText "ZufallsVektor (neu mit Mausklick!)", 10, 70 Local Schnittpunkt:Float[] = MausVektor.ErrechneSchnittpunkt(ZufallsVektor) If Schnittpunkt <> Null Then SetColor 255,255,255 DrawOval Schnittpunkt[0]-3, Schnittpunkt[1]-3, 7, 7 DrawText "Schnittpunkt", Schnittpunkt[0]+16, Schnittpunkt[1]+12 End If Flip Wend
Type TVektor
Field Ort:Int[2], Skalar:Float, Richtung:Int[2] Function erschaffen:TVektor(Ort:Int[], Richtung:Int[]) Local NeuerVektor:TVektor = New TVektor NeuerVektor.NeuerOrt(Ort) NeuerVektor.NeueRichtung(Richtung) Return NeuerVektor End Function
Method NeuerOrt(O:Int[]) Ort[0] = O[0] Ort[1] = O[1] End Method Method NeueRichtung(R:Int[]) Richtung[0] = R[0] Richtung[1] = R[1] Skalar = 1 End Method
Method NeuerBetrag(B:Float) If Richtung[0] <> 0 Or Richtung[1] <> 0 Then Skalar = B / Sqr(Richtung[0]^2 + Richtung[1]^2) End If End Method Method ErrechneBetrag:Float() Return Skalar * Sqr( Richtung[1]^2 + Richtung[0]^2 ) End Method
Method Zeichnen(Rot:Int, Gruen:Int, Blau:Int) SetColor Rot, Gruen, Blau DrawLine Ort[0], Ort[1], Ort[0] + Skalar * Richtung[0], Ort[1] + Skalar * Richtung[1] End Method Method ErrechneSchnittpunkt:Float[](AndererVektor:TVektor) If (Richtung[0] = 0 And Richtung[1] = 0) Or (AndererVektor.Richtung[0] = 0 And AndererVektor.Richtung[1] = 0) Then Return Null If Richtung[0] * AndererVektor.Richtung[1] = Richtung[1] * AndererVektor.Richtung[0] Then Return Null Local SchnittSkalar:Float[2] SchnittSkalar[1] = 1.0 * ( Ort[1] * Richtung[0] - Ort[0] * Richtung[1] + AndererVektor.Ort[0] * Richtung[1] - AndererVektor.Ort[1] * Richtung[0] ) / ( Richtung[0] * AndererVektor.Richtung[1] + Richtung[1] * AndererVektor.Richtung[1] - Richtung[1] * AndererVektor.Richtung[0] - Richtung[1] * AndererVektor.Richtung[1] ) If Richtung[0] <> 0 Then SchnittSkalar[0] = 1.0 * ( AndererVektor.Ort[0] - Ort[0] + SchnittSkalar[1] * AndererVektor.Richtung[0] ) / Richtung[0] Else SchnittSkalar[0] = 1.0 * ( AndererVektor.Ort[1] - Ort[1] + SchnittSkalar[1] * AndererVektor.Richtung[1] ) / Richtung[1] End If If (Skalar > 0 And (SchnittSkalar[0] < 0 Or Skalar < SchnittSkalar[0])) Or (Skalar < 0 And (SchnittSkalar[0] > 0 Or Skalar > SchnittSkalar[0])) Then Return Null If (AndererVektor.Skalar > 0 And (SchnittSkalar[1] < 0 Or AndererVektor.Skalar < SchnittSkalar[1])) Or (AndererVektor.Skalar < 0 And (SchnittSkalar[1] > 0 Or AndererVektor.Skalar > SchnittSkalar[1])) Then Return Null
Return [ Ort[0] + SchnittSkalar[0] * Richtung[0], Ort[1] + SchnittSkalar[0] * Richtung[1] ]
End Method
End Type
Auch wenn es in der Aufgabenstellung "verboten" wurde, möchte ich es nicht versäumen, ein alternatives Beispiel mit Winkeln statt Richtungsvektoren anzuführen, weil diese Methode nach meiner Erfahrung in vielen Spielsituationen praktikabler ist. Zum Beispiel könnte ich ohne weiteren Rechenaufwand den Richtungswinkel nutzen, um an den Vektorspitzen passend gedrehte Pfeile zu malen, was im obigen Beispiel gar nicht so leicht ginge und letztlich doch diesen einen Drehungswinkel benötigte.
BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] Graphics 800,600
Local BildschirmMitteX:Int = GraphicsWidth()/2 Local BildschirmMitteY:Int = GraphicsHeight()/2 Local MausVektor:TWinkelVektor = TWinkelVektor.erschaffen_aus_zwei_Punkten([BildschirmMitteX, BildschirmMitteY], [MouseX(), MouseY()]) Local GegenVektor:TWinkelVektor = TWinkelVektor.erschaffen_aus_Winkel_und_Betrag([BildschirmMitteX, BildschirmMitteY], (MausVektor.Winkel + 180) Mod 360, 100) Local SenkrechtWinkelVektor:TWinkelVektor = TWinkelVektor.erschaffen_aus_Winkel_und_Betrag([BildschirmMitteX, BildschirmMitteY], (MausVektor.Winkel + 90) Mod 360, MausVektor.Betrag/2) Local ZufallsVektor:TWinkelVektor = TWinkelVektor.erschaffen_aus_Winkel_und_Betrag([Rand(100,GraphicsWidth()-100), Rand(100,GraphicsHeight()-100)], Rand(0,360), Rand(100,200))
While Not KeyDown(KEY_ESCAPE) And Not AppTerminate() Cls
MausVektor.VerbindeMit(MouseX(), MouseY()) MausVektor.Zeichnen(255, 0, 0) DrawText "MausVektor", 10, 10 GegenVektor.NeuerWinkel((MausVektor.Winkel + 180) Mod 360) GegenVektor.NeuerBetrag(100) GegenVektor.Zeichnen(255,255,0) DrawText "Entgegengesetzter Vektor mit genormten Betrag von 100 Pixeln", 10, 30 SenkrechtWinkelVektor.NeuerWinkel((MausVektor.Winkel + 90) Mod 360) SenkrechtWinkelVektor.NeuerBetrag(MausVektor.Betrag/2) SenkrechtWinkelVektor.Zeichnen(0,255,0) DrawText "Orthogonaler Vektor, halb so lang wie MausVektor", 10, 50 If MouseHit(1) Then ZufallsVektor = Null ZufallsVektor = TWinkelVektor.erschaffen_aus_Winkel_und_Betrag([Rand(100,GraphicsWidth()-100), Rand(100,GraphicsHeight()-100)], Rand(0,360), Rand(100,200)) End If ZufallsVektor.Zeichnen(0,255,255) DrawText "ZufallsVektor (neu mit Mausklick!)", 10, 70 Local Schnittpunkt:Float[] = MausVektor.ErrechneSchnittpunkt(ZufallsVektor) If Schnittpunkt <> Null Then SetColor 255,255,255 DrawOval Schnittpunkt[0]-3, Schnittpunkt[1]-3, 7, 7 DrawText "Schnittpunkt", Schnittpunkt[0]+16, Schnittpunkt[1]+12 End If Flip Wend
Type TWinkelVektor
Field Anker:Int[2], Betrag:Float, Winkel:Float Function erschaffen_aus_zwei_Punkten:TWinkelVektor(Anker:Int[], EndPunkt:Int[]) Local NeuerWinkelVektor:TWinkelVektor = New TWinkelVektor NeuerWinkelVektor.NeuerAnker(Anker) NeuerWinkelVektor.VerbindeMit( EndPunkt[0], EndPunkt[1] ) Return NeuerWinkelVektor End Function Function erschaffen_aus_Winkel_und_Betrag:TWinkelVektor(Anker:Int[], Winkel:Float, Betrag:Float) Local NeuerWinkelVektor:TWinkelVektor = New TWinkelVektor NeuerWinkelVektor.NeuerAnker(Anker) NeuerWinkelVektor.NeuerWinkel(Winkel) NeuerWinkelVektor.NeuerBetrag(Betrag) Return NeuerWinkelVektor End Function
Method NeuerAnker(A:Int[]) Anker[0] = A[0] Anker[1] = A[1] End Method Method NeuerWinkel(W:Float) Winkel = W End Method
Method NeuerBetrag(B:Float) Betrag = B End Method Method VerbindeMit( Punkt_X:Int, Punkt_Y:Int ) Betrag = Sqr( (Anker[0]-Punkt_X)^2 + (Anker[1]-Punkt_Y)^2 ) Winkel = ATan2( Punkt_Y - Anker[1], Punkt_X - Anker[0] ) End Method
Method Zeichnen(Rot:Int, Gruen:Int, Blau:Int) SetColor Rot, Gruen, Blau DrawLine Anker[0], Anker[1], Anker[0] + Betrag * Cos(Winkel), Anker[1] + Betrag * Sin(Winkel) End Method Method ErrechneSchnittpunkt:Float[](AndererVektor:TWinkelVektor) If Betrag = 0 Or AndererVektor.Betrag = 0 Then Return Null Winkel = Winkel Mod 360 If Winkel < 0 Then Winkel:+360 AndererVektor.Winkel = AndererVektor.Winkel Mod 360 If AndererVektor.Winkel < 0 Then AndererVektor.Winkel:+360 If Abs(Winkel - AndererVektor.Winkel) Mod 180 = 0 Then Return Null Local SchnittBetrag:Float[2] SchnittBetrag[1] = ( Anker[1] - AndererVektor.Anker[1] + Tan(Winkel) * (AndererVektor.Anker[0] - Anker[0]) ) / ( Sin(AndererVektor.Winkel) - Tan(Winkel) * Cos(AndererVektor.Winkel) ) If Cos(Winkel) <> 0 Then SchnittBetrag[0] = ( AndererVektor.Anker[0] - Anker[0] + SchnittBetrag[1] * Cos(AndererVektor.Winkel) ) / Cos(Winkel) Else SchnittBetrag[0] = ( AndererVektor.Anker[1] - Anker[1] + SchnittBetrag[1] * Sin(AndererVektor.Winkel) ) / Sin(Winkel) End If If SchnittBetrag[0] < 0 Or SchnittBetrag[1] < 0 Then Return Null If SchnittBetrag[0] > Betrag Or SchnittBetrag[1] > AndererVektor.Betrag Then Return Null Return [ Anker[0] + Float(SchnittBetrag[0] * Cos(Winkel)), Anker[1] + Float(SchnittBetrag[0] * Sin(Winkel)) ]
End Method
End Type
Die Vektoren meiner Beispiele sind zweidimensional. Die analoge Erweiterung ins Dreidimensionale lässt sich leicht umsetzen und wurde nur deswegen weggelassen, weil allein das Darstellen von Geraden im Raum ein neues Problem birgt. Kennt diesbezüglich jemand ein schnelles und unkompliziertes Verfahren, das auch noch einigermaßen ansprechend aussieht? Das wäre schön.
|