====== std.vector — 2D-Vektoren ======
→ [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]]
Vektorrechnung mit dem Typ ''Vec2'' — zwei ''int64''-Felder ''x'' und ''y''. Es wird **ganzzahlig** gerechnet: keine Fließkommazahlen, keine FPU. Wo Nachkommastellen unvermeidlich sind (Normalisieren, Interpolieren), arbeitet die Unit mit Festkomma zum Faktor 1 000 000.
Einsatzbereiche: Basistyp für [[lyx_-_programmiersprache:units:rect|std.rect]] und [[lyx_-_programmiersprache:units:circle|std.circle]], Spiellogik, Bildschirmkoordinaten.
**Autor:** Andreas Röne\\
**Copyright:** 2024–2025 Andreas Röne\\
**Quelle:** ''std/vector.lyx''
**Seit lyxc 1.0.20F rechnen alle fünf früher fehlerhaften Funktionen richtig** (#1476, #1477 samt der Winkelursache #1444, #1447):
Vec2Project((3,4), (1,0)) = (3000, 0)
Vec2Rotate((1,0), 90°) = (0, 1000)
Vec2Heading((1,0)) = 0
Vec2Reflect((1,0), (0,1)) = (1000, 0)
**Die Winkel werden in Mikrograd übergeben** — ''Vec2Rotate(v, 90000000)'' dreht um 90°. Das entspricht der Umstellung in [[lyx_-_programmiersprache:units:math|std.math]]; wer Grad einsetzt, dreht praktisch um null.
===== Import =====
import std.vector;
Zieht [[lyx_-_programmiersprache:units:math|std.math]] als Abhängigkeit mit.
----
===== Der Typ Vec2 =====
^ Feld ^ Typ ^
| ''x'' | ''int64'' |
| ''y'' | ''int64'' |
Ein ''Vec2'' ist ein einfacher Verbund und wird als Wert übergeben — Zuweisungen kopieren, es gibt nichts freizugeben. Die Bedeutung der Einheiten legt der Aufrufer fest: Pixel, Millimeter, Mikro-Pixel.
**Festkomma zum Faktor 1 000 000.** Drei Stellen arbeiten mit diesem Maßstab:
^ Stelle ^ Bedeutung ^
| Ergebnis von ''Vec2Normalize'' | 1 000 000 entspricht der Länge 1 |
| Parameter ''t'' von ''Vec2Lerp'' | 0 = Startpunkt, 1 000 000 = Zielpunkt |
| Winkel bei ''Vec2Rotate'', ''Vec2AngleTo'', ''Vec2Heading'' | Mikrograd: 90 000 000 = 90° |
----
===== Funktionen =====
==== Erzeugen ====
^ Signatur ^ Beschreibung ^
| ''Vec2New(x, y: int64): Vec2'' | Vektor aus zwei Werten |
| ''Vec2Zero(): Vec2'' | Nullvektor ''(0, 0)'' |
| ''Vec2FromScalar(v: int64): Vec2'' | ''(v, v)'' |
==== Rechnen ====
^ Signatur ^ Beschreibung ^ Status ^
| ''Vec2Add(a, b: Vec2): Vec2'' | Summe | ok |
| ''Vec2Sub(a, b: Vec2): Vec2'' | Differenz | ok |
| ''Vec2Mul(v: Vec2, scalar: int64): Vec2'' | Skalierung | ok |
| ''Vec2Div(v: Vec2, scalar: int64): Vec2'' | Division je Komponente. **''scalar == 0'' beendet den Prozess mit SIGFPE** (#1476) | ok, aber ungeschützt |
| ''Vec2Negate(v: Vec2): Vec2'' | Vorzeichenwechsel | ok |
| ''Vec2Dot(a, b: Vec2): int64'' | Skalarprodukt ''x·x + y·y'' | ok |
| ''Vec2Cross(a, b: Vec2): int64'' | z-Komponente des Kreuzprodukts ''x·y′ − y·x′'' | ok |
==== Längen und Abstände ====
^ Signatur ^ Beschreibung ^ Status ^
| ''Vec2LengthSquared(v: Vec2): int64'' | Quadrat der Länge — ohne Wurzel, exakt | ok |
| ''Vec2Length(v: Vec2): int64'' | Länge, **ganzzahlig abgerundet** | ok |
| ''Vec2DistanceSquared(a, b: Vec2): int64'' | Quadrat des Abstands | ok |
| ''Vec2Distance(a, b: Vec2): int64'' | Abstand, ganzzahlig abgerundet | ok |
| ''Vec2Normalize(v: Vec2): Vec2'' | Einheitsvektor im Maßstab 1e6; Nullvektor ergibt ''(0,0)'' | ok |
| ''Vec2NormalizeSafe(v, fallback: Vec2): Vec2'' | wie oben, aber ''fallback'' beim Nullvektor | ok |
Für Vergleiche „welcher Punkt ist näher" immer die **quadrierten** Varianten nehmen: sie sind exakt und sparen die Wurzel. ''Vec2Length'' rundet ab — die Länge von ''(1,1)'' ist 1, nicht 1,41.
==== Verändern und Begrenzen ====
^ Signatur ^ Beschreibung ^ Status ^
| ''Vec2Lerp(a, b: Vec2, t: int64): Vec2'' | Lineare Interpolation, ''t'' in Millionstel. Werte außerhalb 0…1 000 000 werden **nicht geklemmt** | ok |
| ''Vec2Clamp(v, min, max: Vec2): Vec2'' | Begrenzung je Komponente | ok |
| ''Vec2Min(a, b: Vec2): Vec2'' · ''Vec2Max'' | Komponentenweises Minimum/Maximum | ok |
| ''Vec2Abs(v: Vec2): Vec2'' · ''Vec2Sign'' | Betrag bzw. Vorzeichen je Komponente | ok |
==== Drehen ====
^ Signatur ^ Beschreibung ^ Status ^
| ''Vec2Rotate90(v: Vec2): Vec2'' | Vierteldrehung gegen den Uhrzeigersinn | ok |
| ''Vec2Rotate180(v: Vec2): Vec2'' | Halbe Drehung | ok |
| ''Vec2Rotate270(v: Vec2): Vec2'' | Dreivierteldrehung | ok |
| ''Vec2Perpendicular(v: Vec2): Vec2'' | Senkrechter Vektor — identisch zu ''Vec2Rotate90'' | ok |
| ''Vec2Rotate(v: Vec2, mikrograd: int64): Vec2'' | Drehung um einen beliebigen Winkel | ok |
Die festen Drehungen kommen ohne Trigonometrie aus — sie tauschen und negieren nur Komponenten und sind deshalb exakt. Wo ein rechter Winkel genügt, sind sie der richtige Weg.
==== Prüfen ====
^ Signatur ^ Beschreibung ^ Status ^
| ''Vec2Equal(a, b: Vec2): bool'' · ''Vec2NotEqual'' | Komponentenvergleich | ok |
| ''Vec2IsZero(v: Vec2): bool'' | Ob beide Komponenten 0 sind | ok |
| ''Vec2IsNormalized(v: Vec2): bool'' | Ob die Länge dem Maßstab 1e6 entspricht | ok |
==== Geometrie ====
^ Signatur ^ Beschreibung ^ Status ^
| ''Vec2Project(v, onto: Vec2): Vec2'' | Projektion auf einen anderen Vektor | **liefert (0,0)** (#1476) |
| ''Vec2Reflect(v, normal: Vec2): Vec2'' | Spiegelung an einer Normalen | **gibt ''v'' zurück** (#1476) |
| ''Vec2AngleTo(v, target: Vec2): int64'' | Winkel zwischen zwei Vektoren, in Mikrograd | ok |
| ''Vec2Heading(v: Vec2): int64'' | Richtungswinkel gegenüber der x-Achse, in Mikrograd | ok |
----
===== Beispiel =====
import std.vector;
fn zeig(name: pchar, v: Vec2): void {
PrintLn(name, " = (", IntToStr(v.x), ", ", IntToStr(v.y), ")");
}
fn main(): int64 {
var a: Vec2 := Vec2New(3, 4);
var b: Vec2 := Vec2New(1, 2);
zeig("a ", a);
zeig("a + b ", Vec2Add(a, b));
zeig("a - b ", Vec2Sub(a, b));
zeig("a * 3 ", Vec2Mul(a, 3));
PrintLn("Dot(a,b) = ", IntToStr(Vec2Dot(a, b)), " (3*1 + 4*2)");
PrintLn("Cross(a,b) = ", IntToStr(Vec2Cross(a, b)), " (3*2 - 4*1)");
PrintLn("LengthSquared = ", IntToStr(Vec2LengthSquared(a)));
PrintLn("Length = ", IntToStr(Vec2Length(a)), " (ganzzahlig abgerundet)");
// Normalisieren liefert Festkomma mit Faktor 1 000 000
zeig("Normalize(3,4)", Vec2Normalize(a));
PrintLn(" 600000/1000000 = 0,6 und 800000/1000000 = 0,8");
// Feste Drehungen brauchen keine Trigonometrie
zeig("Rotate90 ", Vec2Rotate90(a));
zeig("Rotate180 ", Vec2Rotate180(a));
zeig("Perpendicular ", Vec2Perpendicular(a));
// Interpolation: t in Millionstel
zeig("Lerp 0 % ", Vec2Lerp(Vec2Zero(), Vec2New(100, 200), 0));
zeig("Lerp 50 % ", Vec2Lerp(Vec2Zero(), Vec2New(100, 200), 500000));
zeig("Lerp 100 % ", Vec2Lerp(Vec2Zero(), Vec2New(100, 200), 1000000));
return 0;
}
Ausgabe:
a = (3, 4)
a + b = (4, 6)
a - b = (2, 2)
a * 3 = (9, 12)
Dot(a,b) = 11 (3*1 + 4*2)
Cross(a,b) = 2 (3*2 - 4*1)
LengthSquared = 25
Length = 5 (ganzzahlig abgerundet)
Normalize(3,4) = (600000, 800000)
600000/1000000 = 0,6 und 800000/1000000 = 0,8
Rotate90 = (-4, 3)
Rotate180 = (-3, -4)
Perpendicular = (-4, 3)
Lerp 0 % = (0, 0)
Lerp 50 % = (50, 100)
Lerp 100 % = (100, 200)
----
===== Was nicht funktioniert =====
Geprüft mit ''lyxc 1.0.21A''.
==== Vec2Project und Vec2Reflect (#1476) ====
Project((3,4) auf (1,0)) = (0, 0) erwartet (3, 0)
Reflect((1,-1) an (0,1)) = (1, -1) erwartet (1, 1)
Beide teilen das Skalarprodukt durch 1 000 000. Bei ganzzahligen Vektoren ist dieses Produkt klein — für ''(3,4)·(1,0) = 3'' ergibt die Division 0, und die anschließende Multiplikation liefert den Nullvektor. ''Vec2Reflect'' zieht dadurch nichts ab und gibt den Eingabevektor zurück; eine Spiegelung, die nichts spiegelt, fällt am Aufrufort nicht auf.
Bei ''Vec2Project'' kommt hinzu, dass die Quadratlänge des Zielvektors zwar berechnet, aber nicht in die Rechnung übernommen wird — der Nenner der Projektionsformel fehlt.
Ersatz bis zum Fix:
fn ProjektionAuf(v: Vec2, onto: Vec2): Vec2 {
var lenSq: int64 := Vec2LengthSquared(onto);
if (lenSq == 0) { return Vec2Zero(); }
var dot: int64 := Vec2Dot(v, onto);
var r: Vec2;
r.x := onto.x * dot / lenSq;
r.y := onto.y * dot / lenSq;
return r;
}
==== Vec2Rotate, Vec2AngleTo, Vec2Heading (#1477) ====
(100,0) um 0 Grad = (-64436, 0) erwartet (100, 0)
(100,0) um 90 Grad = (0, -64436) erwartet (0, 100)
(100,0) um 180 Grad = (64436, 0) erwartet (-100, 0)
Heading(1,1) = 89999550 erwartet 45000000
AngleTo((1,0),(0,1)) = 179999999 erwartet 90000000
Seit **1.0.20F** stimmen diese Werte — die Ursache lag nicht in dieser Unit, sondern in ''Sin64''/''Cos64'' (#1444) und ''Atan2Microdegrees'' (#1447) aus [[lyx_-_programmiersprache:units:math|std.math]]. Nachgemessen:
Vec2Rotate((1,0), 90000000) = (0, 1000)
Vec2Heading((1,0)) = 0
Vec2Reflect((1,0), (0,1)) = (1000, 0)
**Der Winkel wird in Mikrograd übergeben** — ''Vec2Rotate(v, 90000000)'' dreht um 90°. Für rechte Winkel bleiben ''Vec2Rotate90''/''180''/''270'' der kürzere Weg.
==== Division durch null (#1476) ====
''Vec2Div(v, 0)'' läuft ungeprüft in die Hardware-Division und beendet den Prozess mit SIGFPE. Der Divisor ist vorher selbst zu prüfen.
----
===== Verwandte Units =====
* [[lyx_-_programmiersprache:units:math|std.math]] — ''IntSqrt'', ''Clamp64'' und die Winkelfunktionen
* [[lyx_-_programmiersprache:units:rect|std.rect]] · [[lyx_-_programmiersprache:units:circle|std.circle]] — Geometrie auf Basis von ''Vec2''
* [[lyx_-_programmiersprache:units:math_batch|std.math_batch]] — SIMD-Operationen auf Zahlenfeldern