![]() |
Delphi-Version: 10 Seattle
Canvas: Änderungen/Bewegungen zeichnen
Hallo zusammen,
ich schreibe gerade zum Üben einen Flugsicherungssimulator, wobei es mir besonders auf strukturierten und testbaren Code ankommt. Ein Kernelement des UIs soll ein Radarbildschirm sein, vom Prinzip her vergleichbar wie auf folgendem Bild: ![]() Neben der eigentlichen Darstellung (z. B. Flugzeuge, deren letzten Positionen, Landebahnen, Wegpunkte, Beschreibungstexte) soll später auch die Möglichkeit bestehen, den Bildausschnitt zu verschieben (so dass der Flughafen nicht zwingend zentriert liegen muss) und den sichtbaren Ausschnitt zu zoomen. Außerdem soll es evtl. möglich sein, durch Klicken auf die dargestellten Flugzeuge zusätzliche Beschreibungstexte anzuzeigen. Da ich bislang kaum mit einem Canvas gearbeitet habe und mir die Erfahrung damit fehlt, bräuchte ich einmal euren Rat bei ein paar Fragen. 1. Welche Komponente bietet sich eurer Meinung nach bei meiner Problemstellung an? Wäre eine TPaintBox eine gute Wahl? 2. Wie kann man Änderungen in einer Zeichnung am besten implementieren? Sollte im Datenmodell die Position aller grafischen Elemente, die sich auf dem Canvas befinden, gespeichert werden, so dass man bei jeder Änderung (z. B. Position eines Flugzeugs wird verändert) alle Elemente komplett neuzeichnen kann? Falls ja, könnte das zu Flackern beim Neuzeichnen führen? Oder sollte jedes Element sich selbst darum kümmern, dass die alte Position gelöscht und die neue Position gezeichnet wird? 3. Rein logisch finde ich, dass es besser ist, wenn jedes Objekt selbst in der Lage ist, sich zu zeichnen. Meine erste Idee war beispielsweise, folgendes Interface anzulegen:
Delphi-Quellcode:
Jedes Element, das auf dem Radarbildschirm sichtbar sein kann, muss lediglich dieses Interface unterstützen, womit es in der Lage wäre, sich auf den übergebenen Canvas zu zeichnen. Allerdings stellt sich mir bei diesem Ansatz die Frage: Wer kümmert sich darum, dass die alte Position vom Canvas verschwindet? Klar könnte sich jedes Objekt die alte Position merken und jedes Mal, wenn Draw aufgerufen wird, setzt das Objekt die Punkte von der letzten Position wieder zurück, aber das würde zu falschem Verhalten führen, wenn sich Positionen von verschiedenen Objekten überlagern, denn dann führt das Zurücksetzen/Löschen der alten Position dazu, dass möglicherweise Bestandteile anderer dargesteller Objekte mitgelöscht werden. Außerdem soll ja auch ein Verschieben/Zoomen des Bildausschnittes möglich sein. Wie können sich die Objekte wie Flugzeuge selbst auf ein Canvas zeichnen, wenn sie nicht wissen, welcher Bildausschnitt gerade sichtbar ist und an welcher Position sie sich auf dem Canvas platzieren sollen?
IDrawable = interface
procedure Draw(const Canvas: TCanvas); end; Aus diesem Blickwinkel wäre es wiederum gut, wenn es eine separate Klasse (eine Art "Zeichnen-Manager") gibt, die sich um alle Zeichenoperationen kümmert. Ich sehe darin dann aber ein sehr schlechtes Design, denn jedes Mal, wenn ein neuer zeichenbarer Objekttyp erstellt wird, dann müsste man die Klasse anpassen, damit sie auch weiß, wie der neue Objekttyp zu zeichnen ist. Außerdem: Angenommen eine Instanz einer Flugzeugklasse meldet sich beim "Zeichnen-Manager", weil es neu gezeichnet werden soll. Wie weiß dann dieser "Zeichnen-Manager", welche Pixel zur vorherigen Position gehören, damit er diese Pixel zurücksetzen kann? Wie ihr seht, habe ich noch keine passende Idee, wie ich mein Vorhaben implementieren kann. Habt ihr einen Vorschlag für mich? |
AW: Canvas: Änderungen/Bewegungen zeichnen
Ich würde immer erstmal versuchen in Bitmaps zu malen und diese dann für jedes Frame sichtbar zu machen, als Doublebuffer.
Evtl. Auch in fmx mit Vektor Elementen arbeiten über z.B. einem Grundbitmap, wenn das dann nicht zu kleinteilig wird. Paintbox oder Image mit Scrollbox bei Fmx wäre OK. Wenns von der Performance für dich nicht reicht evtl. Gameengine, Aber ich glaube der Radar macht eigentlich nur kleine Änderungen pro Zeiteinheit. Sollte wohl OK sein. Rollo |
AW: Canvas: Änderungen/Bewegungen zeichnen
Ich weiß ja nun nicht wie groß dieses Radar dargestellt werden soll und über wieviel einzelne Objekte wir hier reden.
Ab einem gewissen Maß könnte auch ein Gedanke an DirectX mit Hardware Beschleunigung Sinn machen. Näheres kann man Erfahren wenn man zum Beispiel nach ![]() Edit Oder über ![]() |
AW: Canvas: Änderungen/Bewegungen zeichnen
Das schon genannte Firemonkey wäre dafür die ideale Lösung. Nicht ganz einfach von der Einarbeitung, aber wenn man es dann gut benutzen kann, wirklich gut dafür. Denn dort kannst du Elementen Bewegungspfade geben, Animationsgeschwindigkeiten, ...
Und es ist eben kein großer Aufwand, man muss es nur verstehen. Ansonsten ist es mit einer Paintbox auch kein Problem. Eine Demo für das Zeichnen darauf findest du hier: ![]() |
AW: Canvas: Änderungen/Bewegungen zeichnen
Danke schon einmal für die Antworten.
Ich benutze ein VCL-Projekt, d. h. die Möglichkeit über eine FMX-Komponente scheidet für mich aus (auch wenn der Ansatz interessant klingt). Zitat:
Zitat:
|
AW: Canvas: Änderungen/Bewegungen zeichnen
Schau dir mal das hier
![]() an und auch einige der anderen Projekte in dem Repository. |
AW: Canvas: Änderungen/Bewegungen zeichnen
Hallo,
das klingt nach einem interessanten Projekt ;-) Da Du ja Wert auf strukturierten und Testbaren Code Wert legst würde ich den folgenden Ansatz verfolgen: Vorgaben: (So wie ich es verstanden habe) Darstellung Zoom und Verschiebbar Selektion einzelner Bereiche und eventuell zusätzlicher Informationen Animation der Objekte (Flugzeuge) also Bewegung, Höhe etc... Mein Ansatz wäre: Definieren der Klassen ohne! Darstellungs-Optionen Also etwa: // Pseudo Code nur so hingetipt
Delphi-Quellcode:
Auf dieser Basis ist es erstmal möglich Tests für das verhalten zu erzeugen.
type
TBaseAirplane = class public property Position : Tposition3d; property Speed : Double; property Direction : TDirection3d; property LastUpdateTime : TUpdateTime; end; TPlaneCalculator = class public // Bestimmt die aktuelle Position etc anhand der Zeit, Route etc function CalcNewPos(aBaseAirplane : TBaseAirPlane; actualTime : tUpdateTime) : boolean; end; TSimulator = class private fAirPlanes : Tlist<TBaseAirplane>; fCalculator : TPlaneCalculator; public // funcs zum verwalten der AirPlanes also add, delete, etc... // Von aussen z.b über einen Timer anstossen procedure UpdatePositions(actualTime : TUpdateTime); begin for plane in fAirPlanes do fCalculator(plane, actualPlane); end; end; Zeichnen: Dein Ansatz über ein Interface ist nicht schlecht. Ich würde aber weitergehen und das Komplett abstrahieren. Also nicht über den TCanvas sondern einen Eigenen Virtuellen Zeichenbereich. Aktuell willst Du nur auf den Bildschirm, nächste Woche aber bestimmt auch auf einen Drucker, als SVG zu ner WebPage, als Dokument in Pdf etc.......... Der Vorteil beim Abstrahieren ist, Deine Klassen müssen nicht wissen wie sie gezeichnet werden. Alle Daten innerhalb Deiner Klassen arbeiten in World-Koordinaten. Nur für den Canvas ist es wichtig zu wissen wie skaliert etc.. wird. So etwa:
Delphi-Quellcode:
So kannst Du es in eine "grosse" Drawklasse packen oder
IRadarCanvas = interface
procedure DrawPlane(aPlane : TBaseAirPlane); end; IRadarDrawable = interface procedure Draw(const Canvas: IRadarCanvas); end; halt dann ableiten von TBaseAirplane also
Delphi-Quellcode:
class TAirplaneDrawer = class(TbaseAirplane, IRadarDrawable)
procedure Draw(const Canvas: IRadarCanvas); end; Beim Zeichnen auf den Bildschirm würde ich das ganze in "Layer" verpacken. Also einen Layer für den statischen Hintergrund (Das RadarBild). Layer für das animierte Radar (Dieser Rotierende Linie). Layer für die Aktuell vorhandenen Flugzeuge. Layer für die Zusatzinformationen. Wenn wir mal Threads aussen vor lassen, benötigst Du mindestens 1 Timer. Dieser löst immer eine Neubestimmung der aktuellen Positionen aus, ruft also TSimulator.UpdatePositions auf. Anschliesend wird ein "fertiges" Bitmap für die Darstellung aus den Layern erzeugt und ein Invalidate auf Deine Paintbox. Im Paint Event wird dann immer nur das "fertige Bitmap gezeichnet" Es wurden ja auch schon einige Möglichkeiten genannt für Graphic Bibliotheken hier aber noch eine: ![]() Hat Alpha und Layer. Schaue es Dir an |
AW: Canvas: Änderungen/Bewegungen zeichnen
Liste der Anhänge anzeigen (Anzahl: 1)
So könnte das aussehen,
als Zip da das Gif zu gross ist.. |
AW: Canvas: Änderungen/Bewegungen zeichnen
@scurra
Ich habe mal ein graphisches Framework gebaut, das nur zyklisch in ein Bitmap zeichnet und dieses auf den Bildschirm kopiert. Damit baue ich Formulare auf, ähnlich VCL bzw. FMX. Die Positionen der Controls speichere ich in der Datenschicht und ermittle darüber, über welchem Control die Maus sich befindet. Hat ganz gut funktioniert und ging wirklich schnell (sogar ohne Grafikbeschleunigung etc). Schau mal hier: ![]() Im letzten Beitrag ist eine Demo zum Testen. |
AW: Canvas: Änderungen/Bewegungen zeichnen
Danke noch einmal für den Input von euch. Ich werde versuchen, das beste daraus zu machen und gebe dann evtl. noch einmal Bescheid ob es geklappt hat. Kann allerdings noch etwas dauern, da das ein Freizeitprojekt (und just for fun) ist und ich durchschnittlich wahrscheinlich nur 1-2 Stunden pro Woche daran arbeite. Also nicht wundern, wenn ich erst einmal keine Rückmeldung gebe.
Die Implementierung von Fritzew sieht für mich gut aus, vor allem der Schritt, die Zeichenfläche noch einmal abzukapseln. Ob ich nun direkt eine Paintbox zeichne oder ein Bitmap verwende, weiß ich noch nicht genau. Im Moment sieht es für mich aber eher so aus, als wäre es leichter, eine Bitmap zu verwenden, vor allem, weil dort die Verwendung mehrerer Schichten möglich ist. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:40 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz