C# OOP

Erstelle eine Vector Klasse, mit der du Vektorrechnungen durchführen kannst.

Lerne anhand dieses Beispiels die Grundlagen von OOP in C#.

Despicable Me: Vector

  1. Erstelle ein neues C#-Konsolen-Programm. Nenne es z.B. MathVector

  2. Erstelle darin eine neue Klasse Vector in einem neuen File (pro Klasse ein .cs File). Klicke dazu im Projektmappen-Explorer mit rechts auf den Namen der Solution → Hinzufügen → Klasse

  3. Erstelle einen Konstruktor in der Vector-Klasse. Shortcut: ctor + TAB.

  4. Ein Vector soll dann in Program.cs mithilfe eines Double-Arrays definiert werden können: Vector v = new Vector(new double[] { 1, 2, 3 });

  5. Ein Vektor hat Eigenschaften wie seine Dimension (Anzahl Zahlen, aus dem er besteht) oder seine Länge/Magnitude. Ist v ein Vektor, so soll z.B. über v.Magnitude auf diese Eigenschaft zugegriffen werden. Informiere dich darüber, was Eigenschaften und Felder, sowie getter und setter in C# sind. Schaue dir dazu z.B. folgende Quellen an:
  6. Füge nun deiner Vector-Klasse die Eigenschaften „Dim“ (für Dimension), „Magnitude“ und „Components“ hinzu. Diese sollen im Konstruktor über ein privates Feld festgelegt werden und über einen getter abgerufen werden. Natürlich sollen sie nicht von aussen über einen setter verändert werden können! Die Eigenschaft „Components“ beinhaltet das Double-Array zurückgeben, welches den Vektor definiert.

  7. Informiere dich nun über statische und nicht-statische Methoden, z.B. hier; Methoden in Klassen

  8. Füge deiner Klasse folgende (nicht-statische) public Methoden hinzu: IsNull() und IsUnit(). Diese geben den Bool true oder false zurück, jenachdem ob es sich beim Vektor um einen Nullvektor oder einen Einheitsvektor (unit vector) handelt.

  9. Nun wollen wir mit den Vektoren Rechnen können. Dazu fügen wir drei statische Methoden mit den Namen Add, Sub und ScalarProd für die Vektoraddition, Vektorsubtraktion und das Skalarprodukt hinzu. Über Vector.Add(v1,v2); soll man dann die Vektorsumme zweier Vektoren berechnen können.

  10. Die beiden Rechenmethoden vom letzten Punkt sollen können nur Funktionieren, wenn beide Vektoren, die als Argument übergeben werden, die gleiche Dimension haben. Überprüfe dies und gib gegebenenfalls einen Fehler aus: throw new System.Exception("...");

  11. Nun wollen wir aber nicht immer Vector.Sub(v1,v2); schreiben müssen, um zwei Vektoren zu Subtrahieren. Stattdessen soll das mit dem Operator -, also v1-v2, gehen. Dies geht ganz einfach mit einem Operator Overloading, siehe z.B. hier: C# - Operator Overloading. Füge nun jeweils ein Operator Overloading für die Operatoren + (Vektoraddition), - (Vektorsubtraktion) und * (Skalarprodukt) hinzu.

  12. Über eine Eigenschaft soll von einem Vektor der zugehörige Einheitsvektor ausgegeben werden: v.UnitVector. Dieser Einheitsvektor soll selbst wieder vom Typ Vector sein! Um von einem Vektor den zugehörigen Einheitsvektor zu erhalten, dividiert man jede Komponente des Vektors durch die Länge des gesamten Vektors. Tipp: Überlege dir gut, wo genau der Einheitsvektor berechnet werden soll!

Neue Features:

  1. Ein bestehender Vektor soll überschrieben werden können, aber nur, wenn er die gleiche Dimension wie der bestehende hat. Dazu soll man einfach v.Components neu setzen können. Dies geschieht im setter der Components-Eigenschaft.
  1. Für 3er Vektoren soll nun auch das Vektorprodukt berechnet werden können. Wikipedia Vektorprodukt

  2. Füge nun auch die Skalarmultiplikation hinzu. Dort wird ein Vektor mit einer reellen Zahl gestreckt oder gestaucht. Der Operator * soll so überladen werden, dass v1*v2 das Skalarprodukt ergibt und (s sei eine Zahl, kein Vektor) v*s oder s*v die Skalarmultiplikation.

  3. Erweitere deine Klasse mit weiteren sinnvollen Methoden usw.

  4. Falls du sehr motiviert bist, könntest du eine Klasse Matrix definieren, mit der man Matrizen-Rechnungen durchführen kann.

Ziel dieses Projekts: eine 2D-Zeichen-Bibliothek mit OO in C# schreiben, die es erlaubt, eine Zeichnung aus verschiedenen Objekten zu kombinieren und als Bild oder am Bildschirm auszugeben.

Dazu benötigen wir:

  • eine solide 2D-Vektor Klasse (s.o.)
  • eine Hierarchie von 2D-Figuren (Figure, Polygon, Circle, Oval…)
  • 2D-Figuren sollen Eigenschaften haben wie:
    • Stroke (Rand: Farbe, Breite, Strichart: dashed / solid)
    • Fill (Füllung: Farbe inkl. Transparenz)
  • eine Canvas (Leinwand), auf der die Figuren gezeichnet werden.

Dieses Repo hilft dir beim Start.

Hausaufgaben

  • 2023-05-24
    • Diese Figur zeichnen können:
    • Nötige Canvas Anpassungen kommen noch ins obige Repo rein.

  • Mehr Figuren (Circlenoch mehr)
  • 2D-Figuren kombinieren (add/subtract von Flächen): kannst du dieses Gemälde malen?

  • Simple Scene:
  • Bewegung!
    • Eine public void move(Vector v) Methode auf Figure, die es erlaubt, jede Figur zu bewegen (Wie? Zum Beispiel, indem alle Punkte der Figur bewegt werden - vielleicht benötigt ja Vector eine move Methode?)
    • Der Körper bewegt sich ohne externen Einflüsse mit einer konstanten Geschwindigkeit ($\frac{m}{s}$).
    • Achtung, die Geschwindikeit ist ein Vector-Wert!
    • In jedem Schritt berechnet sich die Translation aus der Geschwindigkeit und der seit dem letzten Update vergangenen Zeit (elapsedSeconds).
    • Vector translation = speed * elapsedSeconds; - dafür muss unsere Vector-Klasse die Skalarmultiplikation unterstützen.
    • Einheiten:
      • Achtung, wenn der Speed die Einheit $\frac{m}{s}$ hat, müssen wir die Meter irgendwie in Pixel umwandeln, damit das ganze Sinn macht.
      • Eine genaue Umrechnung ist nicht nötig, wir können davon ausgehen, dass 1m ca. 6000 Pixel entspricht (bei 150dpi - dots per inch).
  • Lasse deine sich bewegenden Figures mit den Wänden interagieren:
    1. Collision Detection: Erkenne eine Kollision des Gegenstands mit der Wand.
    2. Mirroring: Ändere das Vorzeichen die Geschwindigkeit in der Kollisionsrichtung.
    3. Attenuation: Ist die Kollision nicht perfekt elastisch, so geht ein Anteil der Energie beim Aufprall verloren.
  • Gravitation:
    • In jedem Update-Schritt sollen die beweglichen Körper eine Kraft senkrecht nach unten wirken, die die Figure beschleunigt.
    • Die Beschleunigung ist auf der Erdoberfläche $9.81 \frac{m}{s^2}$.
    • Die Änderung der Geschwindigkeit berechnet sich also aus elapsedSeconds * gravitational_acceleration.
    • Oder speed.Components[1] += elapsedSeconds * gravitational_acceleration (angenommen, die Y-Komponente ist die Richtung der Gravitation).
  • Medium:
    • In unserer Simulation verhalten sich Seifenblase, Ping-Pong-Ball und Stahlkugel genau gleich, weshalb?
      • Tipp: im Vakuum wäre dies tatsächlich der Fall…
    • Wenn wir davon ausgehen, dass unsere Box mit einem Medium (Luft, Wasser…) gefüllt ist, so führen Widerstand und Auftrieb zu einer Differenzierung.
      • Triff Annahmen für das Volumen und die Masse des Körpers
        • Interpretiere z.B. einen Kreis als Kugel.
        • Triff Annahmen für die Dichte von Körper und Medium ($\frac{kg}{m^3}$).
      • Strömungswiderstand: Ist annähernd proportional zum Quadrat der Geschwindigkeit $v$, und proportional zum Querschnitt des Körpers $A$ sowie der Dichte des Mediums $\rho$: $F_\mathrm{W} = c_\mathrm{W} \, A \, \frac{1}{2} \, \rho v^2$
        • Führe eine Term für den Widerstand in die Geschwindigkeitsberechung ein!
        • Experimentiere mit dem Faktor (dem Strömungswiderstandskoeffizient $c_\mathrm{W}$).
      • Statischer Auftrieb: Dieser ist proportional zur Menge des verdrängten Mediums, was wir aus dem Körpervolumen und der Dichte des Mediums berechnen können..
  • General Collisions
    • Figures erhalten eine BoundingBox (containing Rectangle)
    • Scene untersucht für alle möglichen Paare von Körpern, ob sie sich berühren und führt die Kollision aus.
  • talit/csharp_oop.txt
  • Zuletzt geändert: 2023-06-14 05:08
  • von hof