Vektorrechnung in C#

Wer Software entwickelt, die geometrische Berechnungen durchführt, kommt meist nicht drum herum sich erneut mit der aus der Schulzeit bereits bekannten Vektorrechnung zu beschäftigen. Die im Folgenden vorgestellte Klasse Vector3D macht uns das Leben etwas einfacher. Ein Objekt dieser Klasse repräsentiert einen dreidimensionalen Vektor und alle gängigen Vektoroperationen sind bereits implementiert.

Hier sind einige Anwendungsbeispiele:

            // Zwei neue Vektoren erstellen:
            Vector3D V = new Vector3D(1, 3, 4);
            Vector3D W = new Vector3D(2, 1, 5);

            // Die Vektoren addieren, subtrahieren und multiplizieren:
            Vector3D sum = V + W;
            Vector3D diff = V - W;
            double s = V * W; // Skalarprodukt berechnen
            Vector3D k = V.cross(W); // Kreuzprodukt berechnen
            Vector3D V_resize = V * 2.5; // Vektor mit Skalar multiplizieren

            // Die Länge eines Vektors berechnen:
            double V_length = V.length();

            // Einen Vektor normalisieren (d.h. seine Länge auf 1 ändern):
            Vector3D V_normalized = V.normalize();

            // Gegenvektor berechnen (d.h. mit -1 multiplizieren):
            Vector3D V_opposite = V.opposite();

            // Prüfen, ob zwei Vektoren parallel sind:
            if(V.isCollinearTo(W))
            {
                Console.WriteLine("V und W sind parallel!");
            }

In der Klasse Vector3D gibt es noch einige weitere Methoden, z.B. um einen Vektor zu drehen oder auf eine Ebene oder einen anderen Vektor zu projizieren. Diese Methoden werden durch Kommentare im Quelltext erklärt.

Die Klasse Vektor3D:

    public class Vector3D
    {
        public double X { get; private set; }
        public double Y { get; private set; }
        public double Z { get; private set; }

        // Einheitsvektoren:
        public static readonly Vector3D eX = new Vector3D(1, 0, 0);
        public static readonly Vector3D eY = new Vector3D(0, 1, 0);
        public static readonly Vector3D eZ = new Vector3D(0, 0, 1);
        public static readonly Vector3D Zero = new Vector3D(0, 0, 0);

        public Vector3D(double _x, double _y, double _z)
        {
            X = _x; Y = _y; Z = _z;
        }

        public static Vector3D operator +(Vector3D v, Vector3D w)
        {
            return new Vector3D(v.X + w.X, v.Y + w.Y, v.Z + w.Z);
        }

        public static Vector3D operator -(Vector3D v, Vector3D w)
        {
            return new Vector3D(v.X - w.X, v.Y - w.Y, v.Z - w.Z);
        }

        public static double operator *(Vector3D v, Vector3D w)
        {
            return v.X * w.X + v.Y * w.Y + v.Z * w.Z;
        }

        public static Vector3D operator *(Vector3D v, double w)
        {
            return new Vector3D(v.X * w, v.Y * w, v.Z * w);
        }

        public Vector3D cross(Vector3D w) // Kreuzprodukt (this x W)
        {
            return new Vector3D(Y * w.Z - Z * w.Y, Z * w.X - X * w.Z, X * w.Y - Y * w.X);
        }

        public double length()
        {
            return Math.Sqrt(X * X + Y * Y + Z * Z);
        }

        public Vector3D normalize()
        {
            double L = length();
            return new Vector3D(X / L, Y / L, Z / L);
        }

        public Vector3D opposite()
        {
            return new Vector3D(-X, -Y, -Z);
        }

        public void assertNormal()
        {
            double l = length();
            if (l < 0.9999 || l > 1.0001) throw new Exception("Vector3D is not normalized!");
        }

        public bool isCollinearTo(Vector3D w)
        {
            double sp = this * w;
            double l = this.length() * w.length();
            return Math.Abs(sp - l) < 0.0001 || Math.Abs(sp + l) < 0.0001;
        }

        public override string ToString()
        {
            return "(" + X.ToString("F2") + "; " + Y.ToString("F2") + "; " + Z.ToString("F2") + ")";
        }

        public static double matrixDet(Vector3D a, Vector3D b, Vector3D c)
        {
            return a.X * b.Y * c.Z + b.X * c.Y * a.Z + c.X * a.Y * b.Z
                 - a.Z * b.Y * c.X - b.Z * c.Y * a.X - c.Z * a.Y * b.X;
        }

        public static bool cramer(Vector3D A, Vector3D B, Vector3D C, Vector3D Sum, out double a, out double b, out double c)
        { // Löst das LGS a*A + b*B +c*C = Sum. Wenn es keine Lösung gibt wird false zurück gegeben.
            double det = Vector3D.matrixDet(A, B, C);
            if (det == 0) { a = 0; b = 0; c = 0; return false; }
            a = Vector3D.matrixDet(Sum, B, C) / det;
            if (Double.IsNaN(a)) { b = 0; c = 0; return false; }
            b = Vector3D.matrixDet(A, Sum, C) / det;
            if (Double.IsNaN(b)) { c = 0; return false; }
            c = Vector3D.matrixDet(A, B, Sum) / det;
            if (Double.IsNaN(c)) return false;
            return true;
        }

        public Vector3D projectOnPlane(Vector3D planeNormal)
        { // Projiziert den Vektor auf die Ebene senkrecht zu planeNormal, bzw. entfernt die Komponente in planeNormal-Richtung aus dem Vektor.
            planeNormal = planeNormal.normalize();
            return this - planeNormal * (planeNormal * this);
        }

        public Vector3D projectOnVector3D(Vector3D vec)
        {// Projeziert den Vektor auf vec, bzw. gibt nur seine Komponente in vec-Richtung zurück.
            vec = vec.normalize();
            return vec * (this * vec);
        }

        public Vector3D rotateAround(Vector3D startPoint, Vector3D axis, double angle)
        { // Rotiert den durch this beschriebenen Punkt um die Gerade mit Stützpunkt startPoint und Richtung axis. Bei Blickrichtung in axis-Richtung wird der Punkt bei positivem angle im Uhrzeigersinn rotiert.
            axis = axis.normalize();
            Vector3D V = (this - startPoint).projectOnPlane(axis); // Vektor von Gerade (Projektionspunkt) zu this, senkrecht zu axis
            Vector3D W = axis.cross(V).normalize() * V.length();  // W= V um 90° im Uhrzeigersinn weiter gedreht.
            double a = (this - V - startPoint).length(); // Abstand von startPoint zum Projektionspunkt von this auf die Gerade.
            return startPoint + (axis * a) + V * Math.Cos(angle) + W * Math.Sin(angle);
        }

        double angle(Vector3D P1, Vector3D P2)
        { // Berechnet den Winkel zwischen den Punkten P1 und P2 am Punkt this in Radiant
            Vector3D V1 = (P1 - this);
            Vector3D V2 = (P2 - this);
            return Math.Acos(V1 * V2 / (V1.length() * V2.length()));
        }
    }

Ein Gedanke zu „Vektorrechnung in C#

  1. Pingback: Raytracing Teil 1: Einleitung | Volkers Blog

Kommentare sind geschlossen.