Klassenkonzept

  • VB.NET

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Klassenkonzept

    Heyho,

    Ich arbeite gerade an einer Klasse für die Vektorgeometrie. Mittlerweile bin ich kurz davor Ebenen einzubauen, daher meine Frage: Wie konzeptioniere ich die Klassen untereinander? Die Ebene hat ja 3(bzw. 4) unterschiedliche Formen:
    - Parametergleichung
    - Normalengleichung
    - Koordinatengleichung
    - (Hessche Normalform)

    Eine einzige Klasse Ebene mit all diesen Propertys wäre sehr unübersichtlich. Als Info: Die Bibliothek enthält Vector3D und Point3D Klassen die verwendet werden können. Wie würdet ihr das angehen?

    8-) faxe1008 8-)
    Sofern du eine vollständige Implementierung durchführst kannst du die Form verwenden, die dir am ehesten zusagt. Berechnungen werden dann über von dir implementierte Operatoren/Funktionen durchgeführt, sodass Drittcode vollständig unabhängig von der verwendeten Repräsentation ist.
    Einzig um das Erstellen aus allen möglichen Formen musst du dich kümmern, dafür entweder Konstruktoren oder statische Funktionen verwenden. Optional kannst du auch noch ToXYZString-FUnktionen implementieren, um alle möglichen Darstellungen auszugeben, ähnlich wie bei DateTime.
    Was willst Du mit den Ebenen anstellen können? Ich sag jetzt einfach mal "Kürzeste Distanz zu einem Punkt finden". Dann könntest Du eine abstrakte Basisklasse schreiben, in der diese Funktion abstrakt ist. Dann leitest Du davon ab und implementierst die Funktion so, wie man das Ergebnis eben mit der Art von Ebene ausrechnet.
    Wenn Du nicht neben der Basisklasse die 4 anderen Ebenen-Klassen öffentlich verfügbar machen willst, kannst Du sie z.B. als Friend deklarieren, damit sie nur in der eigenen Assembly zugreifbar sind, und in der Basisklasse statische Funktionen so in der Richtung zur Verfügung stellen:

    VB.NET-Quellcode

    1. Public MustInherit Class Plane
    2. '...
    3. Public Shared Function FromXYZ(XYZ As Irgendwas) As Plane
    4. Return New XYZPlane(XYZ)
    5. End Function
    6. Public Shared Function FromThreePoints(Point1 As Point3D, Point2 As Point3D, Point3 As Point3D) As Plane
    7. Return New PlaneThroughThreePoints(Point1, Point2, Point3)
    8. End Function
    9. End Class
    10. Friend Clas XYZPlane
    11. Inherits Plane
    12. '...
    13. End Class
    14. Friend Clas PlaneThroughThreePoints
    15. Inherits Plane
    16. '...
    17. End Class
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:

    Was willst Du mit den Ebenen anstellen können?


    Alles.
    - Parallele Ebenen erzeugen
    - Abstand zu einem Punkt
    - Abstand zu einer Gerade
    - Schnittpunkt mit einer Geraden
    - Schnittgerade zweier Ebenen
    - Schnittwinkel mit Gerade
    - Schnittwinkel mit Ebene

    Wie regel ich das mit den Properties in der Basisklasse? Die Formen müssen sich nahtlos ineinander umkonvertieren lassen, denn wenn ein PropertyChanged ausgelöst wird (z.B. der Spannvektor der Parametergleichung) muss sich ja auch der Normalenvektor ändern und umgekehrt. Ist das mit der Methode reibungslos möglich?

    8-) faxe1008 8-)
    Hi
    ich würde einen einzigen Wertetyp X für Ebenen schreiben (mit der Normalenform) und für die anderen Formen je eine Klasse anlegen. Anschließend würde ich ein Interface mit IProvidePlane oder etwas in der Richtung einführen, das eine Methode X ToPlane besitzt (noch schöner wäre ggf. sogar, dort die hessesche Normalenform zu generieren). Anschließend würde ich alle vier Typen (also auch den Wertetyp) eine Instanz von X erstellen lassen. Gegen die hesesche Normalenform spricht allerdings die Ineffizienz der Quadratwurzelberechnung.

    Viele Grüße
    ~blaze~
    Wenn auch die Properties von außen zugreifbar sein sollen, funzt das mit den Friend Klassen natürlich nicht mehr. Aber ansonsten würde das Konzept gleich bleiben.
    Aber mir stellt sich halt die Frage, warum man zwischen den Formen konvertieren möchte.
    Ich stelle mir da sowas vor (vereinfacht):
    Beispiel

    VB.NET-Quellcode

    1. Public MustInherit Class Line
    2. Public MustOverride Function GetY(X As Double) As Double
    3. End Class
    4. Public Class LineThroughTwoPoints
    5. Inherits Line
    6. Dim _Point1 As Point2D
    7. Public ReadOnly Property Point1 As Point2D
    8. Get
    9. Return _Point1
    10. End Get
    11. End Property
    12. Dim _Point2 As Point2D
    13. Public ReadOnly Property Point2 As Point2D
    14. Get
    15. Return _Point2
    16. End Get
    17. End Property
    18. Public Sub New(NewPoint1 As Point2D, NewPoint2 As Point2D)
    19. _Point1 = NewPoint1
    20. _Point2 = NewPoint2
    21. End Sub
    22. Public Overrides Function GetY(X As Double) As Double
    23. Return _Point1.Y + (X - _Point1.X) * (_Point2.Y - _Point1.Y) / (_Point2.X - _Point1.X)
    24. End Function
    25. End Class
    26. Public Class LineFromLinearEquation
    27. Inherits Line
    28. Dim _K As Double
    29. Public ReadOnly Property K As Double
    30. Get
    31. Return _K
    32. End Get
    33. End Property
    34. Dim _D As Double
    35. Public ReadOnly Property D As Double
    36. Get
    37. Return _D
    38. End Get
    39. End Property
    40. Public Sub New(NewK As Double, NewD As Double)
    41. _K = NewK
    42. _D = NewD
    43. End Sub
    44. Public Overrides Function GetY(X As Double) As Double
    45. Return _K * X + _D
    46. End Function
    47. End Class
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @faxe1008 Mach Dir zunächst eine ordentliche Vektor-Klasse, auf der Du dann alles andere aufbauen kannst.
    Vielleicht ist es sinnvoll, zwischen Orts- und Richtungsvektoren zu unterscheiden, da kannst Du die Rechenoperatoren besser programmieren.
    Wie Du die Ebenen-Daten reinbekommst, sollte an dei Anforderungen angepasst werden.
    Auf welche Art Du die Ebenen intern darstellst, sollte so sein, dass Du gut und verständlich damit rechnen kannst. Ich nehme da einen Punkt (Ortsvektor) und 2 Richtungsvektoren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RFG:
    So sehen die Beispielklassen momentan aus:
    Point3D

    VB.NET-Quellcode

    1. Public Class Point3D
    2. 'X1-Koordinate
    3. Public Property X1 As Double
    4. 'X2-Koordinate
    5. Public Property X2 As Double
    6. 'X3-Koordinate
    7. Public Property X3 As Double
    8. 'Konstruktor
    9. Public Sub New(ByVal x1 As Double, ByVal x2 As Double, ByVal x3 As Double)
    10. _X1 = x1
    11. _X2 = x2
    12. _X3 = x3
    13. End Sub
    14. Public Function DistanceToPoint(ByVal Point As Point3D) As Double
    15. Return Math.Sqrt(Math.Pow(X1 - Point.X1, 2) + Math.Pow(X2 - Point.X2, 2) + Math.Pow(X3 - Point.X3, 2))
    16. End Function
    17. Public Overrides Function ToString() As String
    18. Return String.Concat({"(", _X1.ToString, "|", _X2.ToString, "|", _X3.ToString, ")"})
    19. End Function
    20. End Class

    Vector3D

    VB.NET-Quellcode

    1. Public Class Vector3D
    2. 'X-Koordinate
    3. Public Property X1 As Double
    4. 'X2-Koordinate
    5. Public Property X2 As Double
    6. 'X3-Koordinate
    7. Public Property X3 As Double
    8. 'Konstruktor
    9. Public Sub New(ByVal x1 As Double, ByVal x2 As Double, ByVal x3 As Double)
    10. _X1 = x1
    11. _X2 = x2
    12. _X3 = X3
    13. End Sub
    14. Public Sub FromPoints(ByVal Point1 As Point3D, ByVal Point2 As Point3D)
    15. _X1 = Point2.X1 - Point1.X1
    16. _X2 = Point2.X2 - Point1.X2
    17. _X3 = Point2.X3 - Point1.X3
    18. End Sub
    19. Public Function Magnitude() As Double
    20. Return Math.Sqrt(Math.Pow(X1, 2) + Math.Pow(X2, 2) + Math.Pow(X3, 2))
    21. End Function
    22. Public Function ScalarProduct(ByVal vector As Vector3D) As Double
    23. Return X1 * vector.X1 + X2 * vector.X2 + X3 * vector.X3
    24. End Function
    25. Public Function IsOrthogonalTo(ByVal vector As Vector3D) As Boolean
    26. Return ScalarProduct(vector) = 0
    27. End Function
    28. Public Function AngleTo(ByVal vector As Vector3D) As Double
    29. Return Math.Acos(Math.Abs(ScalarProduct(vector)) / (Magnitude() * vector.Magnitude)) / Math.PI * 180
    30. End Function
    31. Public Sub New(ByVal Point1 As Point3D, ByVal Point2 As Point3D)
    32. _X1 = Point2.X1 - Point1.X1
    33. _X2 = Point2.X2 - Point1.X2
    34. _X3 = Point2.X3 - Point1.X3
    35. End Sub
    36. Public Overrides Function ToString() As String
    37. Return String.Concat({"{", _X1.ToString, "|", _X2.ToString, "|", _X3.ToString, "}"})
    38. End Function
    39. End Class

    Line3D

    VB.NET-Quellcode

    1. Public Class Line3D
    2. Public Property DirectionVector As Vector3D
    3. Public Property SupportVector As Vector3D
    4. Public Sub New(ByVal supportvector As Vector3D, directionvector As Vector3D)
    5. _DirectionVector = directionvector
    6. _SupportVector = supportvector
    7. End Sub
    8. Public Sub New(ByVal Point1 As Point3D, ByVal Point2 As Point3D, ByVal Point3 As Point3D)
    9. DirectionVector = New Vector3D(Point1, Point2)
    10. SupportVector = New Vector3D(Point3.X1, Point3.X2, Point3.X3)
    11. End Sub
    12. Public Function PointOnLine(ByVal t As Double) As Point3D
    13. Return New Point3D(SupportVector.X1 + t * DirectionVector.X1, SupportVector.X2 + t * DirectionVector.X2, SupportVector.X3 + t * DirectionVector.X3)
    14. End Function
    15. '!!!!!!!!!!!!!!!!!
    16. Public Function DistanceToPoint(ByVal Point As Point3D) As Double
    17. 'Point(p1|p2|p3)
    18. 'Line= {s1,s2,s3}+t*{r1,r2,3}
    19. 'general point on line GP(s1+t*r1|s2+t*r2|s3+t*r3)
    20. 'vector Point to GP GPPoint(p1-(s1+t*r1)|p2-(s2+t*r2)|p3-(s3+t*r3))
    21. 'GPPoint(p1-s1-t*r1|p2-s2-t*r2|p3-s3-t*r3)
    22. 'product from point and GPPoint = 0
    23. 'p1*(p1-s1-t*r1)+p2*(p2-s2-t*r2)+p3*(p3-s3-t*r3)=0
    24. 't = (p1^2 + - p1*s1 + p2^2 - p2*s2 + p3^2-p3*s3)/(p1*r1+p2*r2+p3*r3)
    25. Dim p1 As Double = Point.X1
    26. Dim p2 As Double = Point.X2
    27. Dim p3 As Double = Point.X3
    28. Dim s1 As Double = SupportVector.X1
    29. Dim s2 As Double = SupportVector.X2
    30. Dim s3 As Double = SupportVector.X3
    31. Dim r1 As Double = DirectionVector.X1
    32. Dim r2 As Double = DirectionVector.X2
    33. Dim r3 As Double = DirectionVector.X3
    34. Dim t As Double = (r1 * (p1 - s1) + r2 * (p2 - s2) + r3 * (p3 - s3)) / (r1 ^ 2 + r2 ^ 2 + r3 ^ 2)
    35. Dim NearestPointofLine As Point3D = PointOnLine(t)
    36. Return NearestPointofLine.DistanceToPoint(Point)
    37. End Function
    38. Public Function ContainsPoint(ByVal Point As Point3D) As Boolean
    39. 'Point.x1 = supportvector.x1 + t * directionvector.x1
    40. 'Point.x2 = supportvector.x2 + t * directionvector.x2
    41. 'Point.x3 = supportvector.x3 + t * directionvector.x3
    42. ' t1 = (Point.x1 - supportvector.x1) / directionvector.x1
    43. ' t2 = (Point.x2 - supportvector.x2) / directionvector.x2
    44. ' t3 = (Point.x3 - supportvector.x3) / directionvector.x3
    45. 't1=t2=t3 => Line contains Point
    46. Dim t1 As Double = (Point.X1 - SupportVector.X1) / DirectionVector.X1
    47. Dim t2 As Double = (Point.X2 - SupportVector.X2) / DirectionVector.X2
    48. Dim t3 As Double = (Point.X3 - SupportVector.X3) / DirectionVector.X3
    49. If t1 = t2 AndAlso t2 = t3 Then Return True
    50. Return False
    51. End Function
    52. Public Overrides Function ToString() As String
    53. Return SupportVector.ToString & "t*" & DirectionVector.ToString
    54. End Function
    55. End Class


    Mittlerweile bin ich fast soweit, dass ich die Ebene als Parameterform einfach implementiere und in den .ToString Überladungen die anderen Formen abfragbar mache.

    8-) faxe1008 8-)
    Ich hatte mal einiges rumgefuhrwerkt mit Sizes, Points, Lines, Rectangles etc, und ist mir tierisch auf die Nerven gefallen, dass man jeden Sch... mindestens doppelt codieren musste, mal in X, mal in Y-Richtung.

    Durch vieles Hirnen kam ich zu einem neuartigen Konzept, was
    einen Vektor als 2 Zahlen auffasst.
    Und eine Strecke als 2 Vektoren.
    Und ein Rechteck als 2 Strecken.

    Also dasses immer dasselbe bleibt, nur mit mehr Dimensionen kommen auch mathematisch Dimensionen hinzu.

    Dann habich das so organisiert, dass man immer mit foreach beide Richtungen pro Dimension durchnudeln konnte, ohne Sonder-Code schreiben zu müssen für x, y, width, height.
    Und der Gedanke kam mir, dass mein Ansatz in 3D-Geometrie noch viel sinniger sei.

    Also ausgeführt habe ich meine Ausführungen hier: codeproject.com/Articles/58647…ws-StickyWindows-reloaded
    Ich habe das Konzept "homogene structure" genannt, aber möglicherweise ist ähnliches in der Mathematik als "Tesseract" ein alter Hut (jaja, das Rad neu erfinden :thumbdown: )
    Na, vlt. auch nicht - bei Tesseracts bin ich längst abgeschnallt ;)

    Nur so als Anregung, weil wenn du an Intersection gehst und sowas, kann sein, dasses da auch auftritt, dass man immer dasselbe mehrmals coden muss.

    ErfinderDesRades schrieb:

    ausgeführt habe ich meine Ausführungen


    Made my day :D :thumbsup:

    ErfinderDesRades schrieb:

    Nur so als Anregung, weil wenn du an Intersection gehst und sowas, kann sein, dasses da auch auftritt, dass man immer dasselbe mehrmals coden muss.


    Ja das ist im Prinzip sehr häufig das selbe in 2D und 3D. Ich finde die Idee zwar sehr interessant allerdings ist es für schnelle 0815 Programme ein wenig arg kompliziert.

    Niko Ortner schrieb:

    Aber mir stellt sich halt die Frage, warum man zwischen den Formen konvertieren möchte.


    Weils es mit der Parametergleichung extremst schwer ist Abstände zu berechnen z.B.

    8-) faxe1008 8-)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „faxe1008“ ()

    Wenn du mehrere Darstellungen zur Berechnung brauchst nur zu, verwende sie, das muss man deinem Code nicht ansehen können.
    Du solltest allerdings darauf achten, die verschiedenen Darstellungen möglichst effizient ineinander zu konvertieren. D.h. entweder bei jeder Änderung genau einmal konvertieren oder besser das erste mal, wenn es gebraucht wird, konvertieren (bis eine Änderung dich dazu zwingt, die restlichen Darstellungen zu verwerfen).
    Ich würde ebenfalls mich für eine Form entscheiden (Hess'sche Normalform imo) und dann statische Methoden wie public static Plane3D FromXYZForm(Vector3D supp, Vector3D dir1, Vector3D dir2) und dann darin zur Normalform umrechnen.
    »There's no need to "teach" atheism. It's the natural result of education without indoctrination.« — Ricky Gervais
    @faxe1008 Schmeiß das Math.Pow(x, 2) raus und mach auch das ToString() ordentlich:

    VB.NET-Quellcode

    1. Public Function DistanceToPoint(ByVal Point As Point3D) As Double
    2. Dim sum = (X1 - Point.X1) * (X1 - Point.X1)
    3. sum += (X2 - Point.X2) * (X2 - Point.X2)
    4. sum += (X3 - Point.X3) * (X3 - Point.X3)
    5. Return Math.Sqrt(sum)
    6. End Function
    7. Public Overrides Function ToString() As String
    8. Return String.Format("({0}|{1}|{2}", _X1, _X2, X3)
    9. End Function
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    naja, und eiglich heissen Koordinaten doch X, Y, Z?

    Und DistanceToPoint(pt As Point) ist auch ein "weißer Schimmel" - es reicht der Name Distance - dasses eine Distance zum Point ist erkennt man ja am Argument.
    Den Namen Distance sollteste dann sooft überladen, wie gebraucht mit je anderen Argument-Typen.

    Hier nochmal eine fiktive Anwendung des Homogen-Structure-Ansatzes:

    VB.NET-Quellcode

    1. Class Point3D
    2. Private _Values(2) As Double 'ein Punkt hat in jeder Dimension einen Wert
    3. Public Function SquareDistance(ByVal Point As Point3D) As Double
    4. Return _Values.Zip(Point._Values, Function(p0, p1) (p1 - p0) * (p1 - p0)).Sum
    5. End Function
    6. Public Function Distance(ByVal Point As Point3D) As Double
    7. Return Math.Sqrt(SquareDistance(Point))
    8. End Function
    9. End Class
    SquareDistance sollte man extra anbieten, weil ist vielfach schneller, und für viele Nutzungen ebensogut brauchbar wie die korrekte Distanz.