std.vector — 2D-Vektoren
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 std.rect und std.circle, Spiellogik, Bildschirmkoordinaten.
Autor: Andreas Röne
Copyright: 2024–2025 Andreas Röne
Quelle: std/vector.lyx
<WRAP info> 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 std.math; wer Grad einsetzt, dreht praktisch um null.
</WRAP>
Import
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 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
- std.math_batch — SIMD-Operationen auf Zahlenfeldern
