GDI+ und Direct2D unter eine Haube bringen

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

    GDI+ und Direct2D unter eine Haube bringen

    Hallo allerseits.

    Wie einige vielleicht wissen arbeite ich zurzeit an einer kleinen 2D-Gameengine. Eines meiner Ziele ist es, Renderer beliebig austauschen zu können, momentan sind GDI+ und Direct2D (über SharpDX) implementiert.
    Bisher ist es mir auch recht gut gelungen, die beiden Engines auf gemeinsame Interfaces zu bringen, jetzt bin ich jedoch an einen Punkt gestoßen, an dem ich nicht weiterkomme. Es handelt sich um den GraphicsPath (GDI) und die GeometrySink-/SimplifiedGeometrySink-Klasse (Direct2D). Ich will es erstmal nicht übertreiben und nur die notwendigsten Methoden implementieren. AddLine, AddLines, AddBezier und AddBeziers konnte ich auch schon mit relativ wenig Aufwand fertigstellen.
    Allerdings brauche ich auch noch AddArc, und das bereitet mir leider Kopfschmerzen. Bei den anderen Methoden verhielten sich die Engines noch relativ ähnlich, hier jedoch funktioniert alles komplett anders. Hier ist die GDI-Version und hier die Direct2D-Version bzw. die ArcSegment-Struktur für Direct2D. Ein Problem ist auch, dass ich bis zum heutigen Tage die Direct2D-Variante nicht vollends verstanden habe. :whistling:
    Hat jemand nen Plan oder zumindest nen Ansatz, wie man diese beiden Methoden auf gemeinsame Argumente zurückführen könnte, damit ich sie beide über die selbe Interface-Methode aufrufen kann?
    Hat den hierzu keine ner Idee?
    Die Sache ist ziemlich wichtig, ohne die AddArc-Funktion ist das ganze IGeometry-Interface nicht halb so viel wert.
    Es geht ja eigentlich nur darum, Point, Size, RotationAngle, SweepDirection und ArcSize nach Rectangle, StartAngle und SweepAngle zu konvertieren, ich hab da aber keinen Plan.
    es fehlt noch die AddPath-Methode. Die halte ich für die allerwichtigste beim GraphicsPath, denn damit kann man Sub-Pathes erstellen und das Zeugs ineinander verschachteln, was einem enorm mächtige Möglichkeiten an die Hand gibt.
    Das ganze im Zusammenspiel mitte Matrix-Klasse, damit man GraphicsPathes quasi als Template anlegen kann, und für Bewegungen (rotation, Verschiebung, zoomen, verzerren) nur noch mittels GraphicsPath.Transform(Matrix) transformieren kann.
    Nu habich von Sharp-DirectX-2D keine Ahnung, aber vom GraphicsPath weißich, dasser intern zum grössten Teil auf Bezier-Kurven aufbaut.
    Kannste mal gugge, wennde eine Ellipse in einen GP addest, was die PathTypes-Property ausgibt.
    zur Bedeutung der Pathtypes gugge die PathPointType-Klasse an.

    guggemol Control mit beweglicher Figur, oder (c#!!): Gezieltes OwnerDrawing, wie das alles ineinander greift bei GDI+

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

    Hm, ich hab mich dazu jetzt mal erkundigt und auch was gefunden, allerdings immer nur für Arcs auf Basis eines Kreises. Sowohl Direct2D alsoauch GDI+ unterstützen aber Arcs auf Basis einer Ellipse, ich würde dadurch also Möglichkeiten verlieren.
    Weiterhin möchte ich eigentlich, wenn möglich, immer auf die schon fertigen Funktionen der APIs zurückgreifen, anstatt was selbst zu implementieren, da diese vermutlich besser und performanter sind, als etwas, das ich produzieren würde. Es geht mir daher eigentlich nicht darum, wie man einen Arc berechnet, das lasse ich mal schön die APIs selbst machen, ich muss nur dafür sorgen, dass ich die beiden APIs mit den gleichen Parametern aufrufen kann.

    Artentus schrieb:

    Ein Problem ist auch, dass ich bis zum heutigen Tage die Direct2D-Variante nicht vollends verstanden habe.
    Also die Direct2d-Variante scheint mir der Wpf-Path-Geormetry zu ähneln, und da ist die Logik die folgende:

    Denke dir eine komplette Ellipse, auf der 2 Punkte bekannt sind.
    Nu hast du logisch 2 Verbindungen zw. den beiden Punkten, meist einen kürzeren und einen längeren Bogen. Kommt halt drauf an, ob du der Krümmung als rechtsrum gekrümmt oder als linksrum gekrümmt folgst.

    (ich hoffe, du verstehst, wassich meine ;))
    Ja, scheint logisch.
    Aber der erste Punkt ist ja immer vorgegeben, was bedeutet, dass man die Angaben theoretisch auch so treffen könnte, dass es nicht möglich ist, daraus einen Arc zu bilden, oder sehe ich das falsch? Ich könnte ja z.B. den Endpunkt so angeben, dass er nicht auf der Ellipse liegt, die ich angegeben habe.
    naja, das Arc-Segment ist ja so definiert, dass die beiden Punkte verbunden werden.
    Also in Wpf-Logik gibts 2 Punkte, 2 Radien, und ob rechts oder linksrum, und damit ist ein Ellipsen-Segment eindeutig definiert.

    ich glaub ich weiß nicht, was du mit "der erste Punkt ist ja immer vorgegeben" meinst.
    Wenn Start- und Endpunkt weiter voneinander entfernt sind, als der Radius der Ellipse, dann gibt es keine Möglichkeit, die beiden Punkte mit dieser Ellipse zu verbinden.

    Mit "der erste Punkt ist ja immer vorgegeben" meine ich, das mein System so funktioniert, dass das zeichnen immer vom Ende des bereits bestehenden Pfades ausgeht, anstatt wie be GDI+ üblich eine Linie dazwischen gezogen wird.

    Artentus schrieb:

    Mit "der erste Punkt ist ja immer vorgegeben" meine ich, das mein System so funktioniert, dass das zeichnen immer vom Ende des bereits bestehenden Pfades ausgeht
    ausgezeichnet! - genau so tickt Wpf-PathGeometrie auch.

    Das mitte unmögliche Radien - kenne ich mich nicht so mit aus - ich glaub in wpf hieß das auch Size, statt dass 2 Radien angegeben wären.
    gut möglich, dass man da eine Exception kriegt.
    Sehr schön, vielleicht hast du ja dann ne Idee, wie man Ellipse + Start- und Zielpunkt nach Ellipse + Start- und Endwinkel umrechnet? :)
    Da muss dann auch noch SweepDirection rein und außerdem kann man bei Direct2D die Ellipse ja auch um einen bestimmten Winkel "kippen", wie würde das in GDI+ aussehen?

    Artentus schrieb:

    hast du ja dann ne Idee, wie man Ellipse + Start- und Zielpunkt nach Ellipse + Start- und Endwinkel umrechnet?
    Ähm - Mathe-Leistungskurs habich grad noch so rechtzeitig abgewählt.
    Aber du hast doch bestimmt auffm Gymnasium so völlig weltfremde Mathe-Pauker, oder? Die kannste bestimmt glücklich machen mit sone Frage:
    "Gegeben 2 Punkte, 2 Radien - berechne das umschließende Rechteck der Ellipse" ;)

    (In GDI+ würde ich die Ellipse basteln, und dann mitte Matrix rotieren.)
    Also um ehrlich zu sein bin ich der weltfremde Mathe-Pauker (oder zumindest halten mich die anderen immer dafür, obwohl ich sogut wie noch nie für Mathe gelernt hab). :D
    Es wird wohl irgendwie mit Sinus und Cosinus funktionieren, allerdings kann ich sowas nur für nen Kreis berechnen, nicht für ne Ellipse, und schon gar nicht für ne gekippte Ellipse.

    In GDI+ würde ich die Ellipse basteln, und dann mitte Matrix rotieren.
    Also du meinst mithilfe der Angaben die Ellipse berechnen und dann um deren Mittelpunkt rotieren?
    Vielleicht mal über Polarkoordinaten probieren: Klick

    Obwohl es keine eindeutige Lösung für zwei einzelne Punkte gibt. Wären die Punkte Enden von Linien, könnte man zusätzlich Bedingungen definieren (z.B. Stetigkeit) um eine eindeutige Lösung zu erzwingen.

    (Keine Garantie das der Vorschlag zum Erfolg führt, ist schon ne Weile her das ich mich damit beschäftigt habe...)
    Hm, ich bin mir nicht sicher, ob das so funktioniert, aber ich kenn mich da auch nicht aus.

    Ich veranschauliche einfach mal anhand von Bildern, was passieren soll.
    Spoiler anzeigen
    1. Gegeben ist der bereits bestehende Pfad (bzw. es interessiert eigentlich nur dessen Endpunkt A) und der Endpunkt B. Zwischen A und B soll jetzt ein Arc gezeichnet werden.


    2. Zusätzlich ist noch eine Ellipse gegeben. Von dieser Ellipse sind beide Radien und ihre Drehung um ihren Mittelpunkt gegeben.


    3. Diese Ellipse soll nun so positioniert werden, dass die Punkte A und B auf ihr liegen.


    4. Gezeichnet werden soll dann aber nur der Teil der Ellipse, der zwischen den beiden Punkten liegt.



    Dies wäre übrigens nur die gewünschte Positionierung, wenn der ArcMode Small und die Richtung Clockwise wäre.

    Bei ArcMode Large + Clockwise sähe es so aus:


    Bei ArcMode Small + Counterclockwise so:


    Und bei ArcMode Large + Counterclockwise so:

    Die Angaben sind also A, B, RadiusX, RadiusY und Rotation, wobei A und B eben die beiden Punkte und die anderen drei Werte die Ellipse beschreiben. Für Direct2D ist das super, weil dort kann ich einfach diese Angaben übergeben und den Rest übernimmt dann Direct2D für mich.
    Bei GDI+ ist es aber nicht so einfach, denn dort wird der Arc anders definiert. GDI+ verlangt weder einen Punkt, noch eine Ellipse. Stattdessen wird die Ellipse dort mit einem Rectangle beschrieben (was leider auch bedeutet, dass die Ellipse nicht gedreht werden kann, da muss man dann also nochmal manuell Hand anlegen) und Start- und Endpunkt werden in Form von Winkeln angegeben. Das heißt, ich muss also zuerst einmal die Zeichenfläche so drehen, dass die Achsen der Ellipse parallel zu den Koordinatenachsen liegen. Dann muss ich die Ellipse (oder halt das Rectanlge, durch das die Ellipse definiert wird) manuell an die richtige Stelle schieben und zu guter letzt die Punkte A und B in ihre entsprechenden Winkel auf der Ellipse umrechnen. Dann kann ich den Arc zeichnen und die Zeichenfläche wieder zurückdrehen.
    Und die Frage lautet: wie geht das?
    Also das Problem besteht leider auch weiterhin. Ich brauche es eigentlich nur soweit, dass ich den Mittelpunkt der verschobenen Ellipse berechnet bekomme, den Rest schaffe ich dann auch alleine.
    Irgendwie müssen das schließlich auch die Leute von Direct2D berechnen. ?(
    Wenn mir jemand erklären könnte, wie man das aus der Bezirkurve bildet, wärs natürlich auch in Ordnung.

    Wenn ich das nicht irgendwie gelöst bekomme, dann kann ich den ganzen GDI-Renderer wegschmeißen, was heißen würde die Arbeit von Monaten wäre umsonst gewesen.
    Wie man nen Spline aus Bezieren zusammensetzt weiß ich, das musste ich schon implementieren, da Direct2D von Haus aus keine Splines unterstützt.
    Ich hab auch schon was dazu gefunden, wie man einen Kreis-Arc als Bezierkurve darstellt: hansmuller-flex.blogspot.de/20…cular-arc-with-cubic.html
    Nur leider hab ich halt keine Ahnung von diesen ganzen Formeln und wüsste daher auch nicht, wie ich das anpassen müsste, damits für einen Ellipse-Arc funktioniert.