Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Canvas: Änderungen/Bewegungen zeichnen (https://www.delphipraxis.net/197670-canvas-aenderungen-bewegungen-zeichnen.html)

Scurra 26. Aug 2018 10:54

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: https://flyawaysimulation.com/downlo.../airtrfcfx.jpg

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:
IDrawable = interface
  procedure Draw(const Canvas: TCanvas);
end;
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?
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?

Rollo62 26. Aug 2018 13:09

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

KodeZwerg 26. Aug 2018 17:04

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 Bei Google suchenDelphi DirectX API sucht.

Edit
Oder über OpenGL

jaenicke 26. Aug 2018 17:13

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:
https://www.delphipraxis.net/132375-...-beta-6-a.html

Scurra 27. Aug 2018 06:33

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:

Ich würde immer erstmal versuchen in Bitmaps zu malen und diese dann für jedes Frame sichtbar zu machen, als Doublebuffer.
In diesem Fall wäre dann die TImage-Komponente am besten geeignet, oder?

Zitat:

Ansonsten ist es mit einer Paintbox auch kein Problem. Eine Demo für das Zeichnen darauf findest du hier:
https://www.delphipraxis.net/132375-...-beta-6-a.html
Ich denke, dass ich es erst einmal mit diesem Ansatz versuchen werde. Ich weiß zwar immer noch nicht, wie ich die Bewegungen implementieren soll (alles komplett neu zeichnen oder nur die Änderungen zeichnen), aber vllt. finde ich in dem von dir genannten Link noch Anregungen dafür.

Schokohase 27. Aug 2018 07:25

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Schau dir mal das hier

https://github.com/tothpaul/DelphiTi...AnimatedArrows

an und auch einige der anderen Projekte in dem Repository.

Fritzew 27. Aug 2018 10:04

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:
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;
Auf dieser Basis ist es erstmal möglich Tests für das verhalten zu erzeugen.


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:
IRadarCanvas = interface
  procedure DrawPlane(aPlane : TBaseAirPlane);
end;

IRadarDrawable = interface
  procedure Draw(const Canvas: IRadarCanvas);
end;
So kannst Du es in eine "grosse" Drawklasse packen oder
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:
https://github.com/graphics32/graphics32

Hat Alpha und Layer. Schaue es Dir an

Fritzew 27. Aug 2018 19:32

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..

stahli 28. Aug 2018 08:36

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:
https://www.delphipraxis.net/175033-...-schlecht.html
Im letzten Beitrag ist eine Demo zum Testen.

Scurra 28. Aug 2018 20:28

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.

jaenicke 29. Aug 2018 21:25

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Zitat von Scurra (Beitrag 1411856)
Ob ich nun direkt eine Paintbox zeichne oder ein Bitmap verwende, weiß ich noch nicht genau.

Du solltest in jedem Fall alles erst fertig in ein Bitmap zeichnen und dann in einem Rutsch dieses in die Paintbox kopieren. Ansonsten flackert es nur unnötig.

Scurra 1. Sep 2018 11:39

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Ich habe gestern und heute mal ein wenig herumgespielt. Da ich das graphics32-Package schon hatte, habe ich dieses verwendet (ich dachte bislang, dass das schon standardmäßig in Delphi enthalten ist ;)). Mit der TImage32-Komponente und den darin enthaltenen (Bitmap-)Layern klappt das bisher wunderbar. Das einzige, das ich etwas störend/umständlich finde, sind die runden Enden von Linien, die man mit der Methode LineTo auf die Bitmaps zeichnet. Ich habe hier im Forum schon eine Lösung dafür gefunden (https://www.delphipraxis.net/92808-t...ten-ecken.html), man muss aber extra jedes Mal die Winapi verwenden.

Ich habe gerade noch eine Frage: Was ich bei meiner Anforderung komplett vergessen habe, ist, dass man das Formular auch vergrößern und verkleinern kann. Wie würdet ihr in einem solchen Fall vorgehen? Im Moment mache ich es so, dass ich die TImage32-Komponente mit alClient über mein Radar-Panel setze. Die Maße der Bitmaps in den Layern müsste ich aber im OnResize auch ständig anpassen, was evtl. zu einem Flackern führen könnte, wenn man das Formular mit der Maus groß/klein zieht (das OnResize-Event wird dann oft ausgelöst).

stahli 1. Sep 2018 14:54

AW: Canvas: Änderungen/Bewegungen zeichnen
 
In meinem Projekt berechne ich das Bitmap in einem Thread. Für die Berechnung wird die Zielgröße ClientRect des Formulars mitgegeben.
Wenn die Berechnung fertig ist, wird das Bitmap synchronisiert auf den Formularcanvas kopiert.

Sofern zwischenzeitlich die Formulargröße verändert wurde (ist ja flüssig möglich, da die Bitmap-Berechnung in einem Thread läuft) passt der Inhalt nicht zur Formulargröße. Es gibt einen Rand oder es wird etwas abgeschnitten.
Es wird aber sofort eine Neuberechnung ausgelöst, so dass der Inhalt an die neue Größe angepasst wird. Man erhält also einen Nachlaufeffekt, bis die Bitmapgröße zur Formulargröße passt.
Flackern tut da aber nichts.

himitsu 1. Sep 2018 15:12

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Zitat von Scurra (Beitrag 1412199)
man muss aber extra jedes Mal die Winapi verwenden.

Du kannst das aber auch einmal in einer Funktion kapseln und dann die statt der WinAPI verwenden
oder besser noch es per Class-Helper als dein eigenes LineToIrgendwas an TCanvas hängen.

Jens01 1. Sep 2018 17:51

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Das einzige, das ich etwas störend/umständlich finde, sind die runden Enden von Linien, die man mit der Methode LineTo auf die Bitmaps zeichnet.
Das hier solltest Du Dir angucken:
http://angusj.com/delphi/gr32_lines.php

Es ist zwar schon veraltet aber gut...

Scurra 1. Sep 2018 19:43

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Sofern zwischenzeitlich die Formulargröße verändert wurde (ist ja flüssig möglich, da die Bitmap-Berechnung in einem Thread läuft) passt der Inhalt nicht zur Formulargröße. Es gibt einen Rand oder es wird etwas abgeschnitten.
Es wird aber sofort eine Neuberechnung ausgelöst, so dass der Inhalt an die neue Größe angepasst wird. Man erhält also einen Nachlaufeffekt, bis die Bitmapgröße zur Formulargröße passt.
Flackern tut da aber nichts.
Läuft wirklich sehr flüssig :thumb: Den Nachlaufeffekt kann man zwar sehen, ist aber weniger schlimm, als ich erwartet hätte.

Zitat:

oder besser noch es per Class-Helper als dein eigenes LineToIrgendwas an TCanvas hängen.
Klingt nach einer guten Idee :thumb:

Zitat:

Das hier solltest Du Dir angucken:
http://angusj.com/delphi/gr32_lines.php

Es ist zwar schon veraltet aber gut...
Wirklich interessant, dass es auch für solche Kleinigkeiten schon feritge Lösngen gibt. Ich werde es aber erst einmal bei der Lösung mit dem Class-Helper belassen. Hab ohnehin noch viel Arbeit vor mir.

KodeZwerg 1. Sep 2018 20:22

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zeig doch mal ein Foto, würde mich interessieren was Du Dir da Programmierst. Klingt auf jeden Fall nach einem schönen Thema.

Scurra 1. Sep 2018 21:05

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Es gibt noch nicht wirklich viel zu sehen. Im Hauptformular (Anhang Main.jpg) ist bisher nur der nackte Aufbau des UIs zu erkennen. Über einen separaten Einstellungs-Dialog kann man Datum und Uhrzeit im Spiel und das Wetter einstellen. Letzteres ist im Moment noch statisch, d. h. es verändert sich noch nicht mit der Zeit. Die Anzeige der eingestellten Uhrzeit hat mich besonders viel Zeit gekostet, weil ich den Eindruck hatte, dass Timer ziemlich unpräzise sind und ich auch weitestgehend auf Timer verzichten möchte. Stattdessen habe ich eine Klasse implementiert, die mit Hilfe eines Threads nach jeder Sekunde einen Callback an den UI-Thread ausführt, damit dieser im UI die aktuelle Spielzeit anzeigen kann. Die Klasse bietet auch die Möglichkeit, den Thread zu pausieren (wenn man Spieleinstellungen ändert, soll die Simulation stehen bleiben, d. h. auch die Uhrzeit darf sich nicht ändern).

Ähnlich möchte ich das auch für das Aktualisieren des Radarbildschirms machen, die Implementierung ist ja schon vorhanden. Hier soll der Thread beispielsweise alle 2 Sekunden ein Event triggern, das meinem Programm sagt, dass der Radarbildschirm aktualisiert werden soll.

Im Anhang "Zeichnen.jpg" sind meine ersten Versuche beim Zeichnen, die ich im Rahmen dieses Themas im Forum gemacht habe. In der Demoanwendung habe ich einen Timer verwendet, um die Position der angezeigten Elemente jede Sekunde um 3 Pixel nach rechts wandern zu lassen (letzteres kann man im Screenshot natürlich nicht sehen :D).

KodeZwerg 1. Sep 2018 21:42

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Scurra, Danke fürs Zeigen, nun hat man eine Ersteindruck woran Du werkelst auch visuell.
Dein "Flugzeug mit Label" Rohbau macht auch ne gute Figur (das zweite Bild).
Sieht vielversprechend aus, nur weiter so!

Scurra 25. Dez 2019 10:16

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Hallo zusammen,

nach einer langen Pause habe ich mir nun endlich wieder die Zeit genommen, an diesem Projekt weiter zu arbeiten.

Aktuell beschäftigt mich immer noch das Zeichnen. Aktuell mache ich das folgendermaßen (ich benutze die GR32-Komponente):

Ich habe folgendes Interface, das mir den Inhalt der Radarfläche kapselt:
Delphi-Quellcode:
IRadarScreen = interface(IBaseInterface)
    function GetContent: TImage32;

    /// <summary>The image-information which shall be displayed.</summary>
    /// <remarks>The interface will free the content when implementation is freed. Do not
    ///   hold a reference to the content unless you are sure that implementation lives longer than
    ///   your reference!</remarks>
    property Content: TImage32 read GetContent;
  end;
Ganz innen habe ich einen Thread zum Zeichnen. Dieser erzeugt eine TImage32-Instanz mit den gewünschten Bitmap-Layern und verpackt diese Instanz im IRadarScreen-Interface. Über eine Callback-Methode reicht der Thread dieses Interface nach außen. Hier mein aktueller Code:
Delphi-Quellcode:
procedure TDrawerThread.Run;
var
  radarScreen: IRadarScreen;
begin
  inherited;

  while not Terminated do
  begin
    radarScreen := TestRadarScreenUpdate; // diese Methode erzeugt die TImage32-Instanz und liefert dieses als IRadarScreen-Interface zurück.
    if Assigned(FOnDrawSuccess) then
      Synchronize(procedure
                  begin
                    FOnDrawSuccess(radarScreen)
                  end);
    sleep(33);
  end;
end;
(Später soll dieser Code immer dann getriggert werden, wenn etwas neu gezeichnet werden muss, sei es durch eine Neuberechnung der Positionen in der Simulation oder durch ein Resize der Radar-Fläche. Die Prozedur FOnDrawSuccess wird per Constructor-Injection an den Thread übergeben)

Das IRadarScreen-Interface wird dann über verschiedene Schichten im Model an den Presenter und von dort an mein View übergeben. Das View hat schließlich Zugriff aufs Formular und führt bei einem Update folgenden Code aus:

Delphi-Quellcode:
procedure TMainView.SetRadarScreen(const UpdatedRadarScreen: IRadarScreen);
var
  radarScreenRefOld: IRadarScreen;
begin
  // ensure that the old IRadarScreen is freed only after the reference to its image has been removed!
  radarScreenRefOld := FRadarScreenRef;
  FRadarScreenRef := UpdatedRadarScreen;

  if Assigned(FRadarScreenContent) then
    FRadarScreenContent.Parent := nil;

  FRadarScreenContent := UpdatedRadarScreen.Content;
  radarScreenRefOld := nil;
  FRadarScreenContent.Parent := Form.pnlRadar;
  FRadarScreenContent.Left := 0;
  FRadarScreenContent.Top := 0;
end;
Kurz gesagt heißt das, dass ich immer die komplette TImage32-Instanz auf dem Formular austausche. Ich habe es schon einmal mit TImage32.Assign ausprobiert, aber das liefert eine Exception, weil man TImage32 das zuweisen auf diese Art wohl nicht unterstützt.
Da ich bisher noch kaum Erfahrung mit dem Zeichnen/Neuzeichnen habe, wollte ich mal fragen, ob meine Implementierung vllt. zu unerwünschten Effekten führen könnte (weil ich immer das komplette TImage32-Objekt austausche) und ob es da vllt. bessere Möglichkeiten gibt.

Jens01 25. Dez 2019 10:37

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Probier das mal:
Delphi-Quellcode:
TImage32.Bitmap := NewBitmap;
TImage32 assigned das neue Bitmap.

Frohe Weihnachten ...

Michael II 25. Dez 2019 11:59

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Zitat von Scurra (Beitrag 1453920)
(weil ich immer das komplette TImage32-Objekt austausche)

Bei GR32 musst du dich um Dinge wie "Bitmap zeichnen im Hintergrund und dann raus auf den Schirm" nicht kümmern: Du schreibst, dass du Layer verwendest. Du passt einfach die Grafik im Layer an, änderst allenfalls die Position des Layers - fertig.

Also etwa so:

Delphi-Quellcode:
MeinImage32.BeginUpdate;

BitMapLayersAnpassen...

MeinImage32.EndUpdate;
Wobei MeinImage32 ein TImage32 ist, welches den ganzen Anzeigebereich ausfüllt.

Dann ruckelt nix.

(Es gibt Demoprogramme, welche mit GR32 mitgeliefert werden.)

Scurra 27. Dez 2019 17:37

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Danke für eure Hinweise.

Ich war mal wieder etwas überängstlich. Ich tausche jetzt weiterhin jedes Mal die TImage32-Komponente aus. Beim Verändern der Formulargröße gibt es zwar einen deutlichen "Nachzieh-Effekt", aber da meine Fläche ohnehin fast nur schwarz ist, kann ich das Problem leicht beheben, indem ich auch das Panel, auf dem die TImage32-Komponente liegt, schwarz einfärbe.

Inzwischen habe ich mich schon etwas an das händische Zeichnen auf ein Canvas gewöhnt, aber mir ist da trotzdem ein Fehler aufgefallen. Oder mache ich etwas falsch?

Ich möchte eine gestrichelte Linie zeichnen. Ich habe bereits gelesen, dass die Winapi wohl fehlerhaft ist und man dazu einen gepunkteten (anstatt einem gestrichelten) Pen benutzen muss. Aber leider wird die Farbe nicht übernommen, die gestrichelte Linie wird weiß gezeichnet, unabhängig davon, welche Farbe ich wähle. Das hier ist mein Code dazu:

Delphi-Quellcode:
procedure TRunwayRenderer.DrawIlsLine(const Bitmap: TBitmap32);
var
  penHandle: HPEN;
  pen: TPen;
  logBrush: TLogBrush;
  canvas: TCanvas;
begin
  penHandle := ExtCreatePen(PS_GEOMETRIC or PS_DOT or PS_ENDCAP_FLAT or PS_JOIN_MITER, 1, logBrush, 0, nil);
  canvas := Bitmap.Canvas;
  pen := canvas.Pen;
  pen.Handle := penHandle;
  pen.Style := psDot;
  pen.Color := $00222222;

  canvas.MoveTo(1, 100);
  canvas.LineTo(500, 100);
end;
Als workaround fällt mir nur ein, die gestrichelte Linie wirklich komplett händisch zu zeichnen, also eine durchgezogene Linie zusammen mit einer Abfolge von canvas.MoveTo und canvas.LineTo zu verwenden.

Jens01 27. Dez 2019 17:55

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Ich glaube, Du verfolgst einen etwas falschen Ansatz.
Eigentlich bearbeitest Du ein TBitmap32. Dafür gibt es viele Tools und Methoden in Bitmap32. (Das ist ja der Grund, weswegen Du TBitmap32 und TImage32 verwendest.) Und portierst es dann (wie von mir gezeigt) zu TImage32.

Die Layervariante von Micheal ist noch viel besser, aber auch komplizierter. Das solltest Du machen, wenn Du noch fitter bist mit GR32.

Scurra 31. Dez 2019 08:57

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Eigentlich bearbeitest Du ein TBitmap32. Dafür gibt es viele Tools und Methoden in Bitmap32. (Das ist ja der Grund, weswegen Du TBitmap32 und TImage32 verwendest.)
Mir war bislang nicht klar, dass es da so viele Hilfsmittel zur Verfügung stehen. Ich habe es jetzt aber mal ausprobiert. Es hat zwar zugegebenermaßen trotz der Demo-Programme von Graphics32 über einen halben Tag gedauert, bis ich es etwas besser verstanden habe (z. B. was genau es mit dem Stippling auf sich hat), aber jetzt funktioniert es und das Ergebnis sieht mit Antialiasing wesentlich besser aus, als wenn man es von Hand zeichnet.

Zitat:

Die Farbe wird nicht angenommen, weil der LOGBRUSH-Struktur kein Farbwert zugewiesen wurde.
Generell halte ich dein Code-Beispiel für undurchdacht.
Danke, dass man dem LOGBRUSH noch eine Farbe zuweisen muss, wusste ich nicht. Mit deinem Beispiel funktioniert es jedenfalls.

Jens01 31. Dez 2019 10:48

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Es gibt nicht nur in TBitmap32 sondern auch umzu viele Tools. Ein Veraltetes hatte ich hier zuvor mal geposted. Angus hat aber zwischenzeitlich auch etwas Neues. Mußt vllt mal ausprobieren:
http://www.angusj.com/delphi/image32/Docs/Examples.htm
https://sourceforge.net/projects/image32/

Die Linien werden hierbei als gefülltes Polygon erstellt und sind deswegen besser und schärfer als die Canvas-Linien.
Aber natürlich ist der Schwierigkeitsgrad auch etwas höher.:wink:

Guten Rutsch ...

Scurra 1. Jan 2020 09:48

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Ich hoffe, ihr seid alle gut im neuen Jahr angekommen :)

Zitat:

Es gibt nicht nur in TBitmap32 sondern auch umzu viele Tools. Ein Veraltetes hatte ich hier zuvor mal geposted. Angus hat aber zwischenzeitlich auch etwas Neues. Mußt vllt mal ausprobieren:
http://www.angusj.com/delphi/image32/Docs/Examples.htm
https://sourceforge.net/projects/image32/
Ich versuche mich erst einmal bei dem "Standard" Graphics32 ;)

Weiß zufällig jemand, ob folgendes ein Bug in Graphics32 ist?
Wenn ich folgenden Code ausführe, erhalte ich in der Zeile "bitmap.LineToFSP" eine Exception, weil intern einmal versucht wird, durch 0 zu teilen (Image ist vom Typ TImage32):

Delphi-Quellcode:
procedure TForm2.Button1Click(Sender: TObject);
var
  bitmap: TBitmap32;
  bitmapLayer: TBitmapLayer;
  rect: TFloatRect;
begin
  Image.Width := 1670;
  Image.Height := 969;
  bitmapLayer := Image.Layers.Add(TBitmapLayer) as TBitmapLayer;
  rect.Left := 0;
  rect.Top := 0;
  rect.Right := Image.Width;
  rect.Bottom := Image.Height;
  bitmapLayer.Location := rect;
  bitmap := bitmapLayer.Bitmap;
  bitmap.Width := Image.Width;
  bitmap.Height := Image.Height;
  bitmap.MoveToF(-1, 485);
  bitmap.LineToFSP(-1091, 485);
end;
Es gibt in GR32 einen Codeblock, der untersucht, ob die Linie überhaupt sichtbar ist (so habe ich den Code zumindest verstanden). GR32 erkennt das in diesem Fall aber wohl nicht richtig. Wenn man die -1 durch eine -2 ersezt bei bitmap.MoveToF(), dann erkennt GR32, dass die Linie gar nicht im sichtbaren Bereich ist und verlässt die Methode.

Ich könnte natürlich in meinen Code schon eine Prüfung einbauen, damit LineToFSP in solchen Fällen gar nicht aufgerufen wird, aber dann müsste ich mir extra erst einmal einen Wrapper für TBitmap32 bauen.

Edit: Ich glaube, die Frage hat sich erledigt. Habe gerade in GitHub nachgeschaut und da gibt es schon eine neuere Version, die die Variable, durch die geteilt wird, vorher auf = 0 prüft.

Edit2: Hat zwar mit dem eigentlichen Thema nur indirekt zu tun, aber vllt. kann mir trotzdem jemand helfen: Ich möchte die neuste Version von Graphics32 installieren. Wenn ich das Package installieren möchte, dann erhalte ich jedoch folgende Fehlermeldung:

Zitat:

Package 'GR32_RRX3' kann nicht geladen werden. Es enthält die Unit 'GR32_XPThemes', die auch im Package 'GR32_RSXE5' enthalten ist.
Ich verstehe nicht ganz, wodurch diese Fehlermeldung ausgelöst wird?! Ich habe das Package GR32_RSXE5 nicht installiert. Warum wird das dann bemängelt?

Scurra 2. Jan 2020 10:59

AW: Canvas: Änderungen/Bewegungen zeichnen
 
Zitat:

Ich verstehe nicht ganz, wodurch diese Fehlermeldung ausgelöst wird?! Ich habe das Package GR32_RSXE5 nicht installiert. Warum wird das dann bemängelt?
Ich konnte das Problem nun lösen: In den Verzeichnissen C:\Users\Public\Documents\Embarcadero\Studio\20.0\ Bpl und C:\Users\Public\Documents\Embarcadero\Studio\20.0\ Cdp lagen noch Dateien mit dem Package. Diese Dateien habe ich gelöscht.
Ich glaube, das Problem war, dass ich noch ein weiteres Package benutze, das auf das "veraltete" Package verwiesen hat. Jetzt kann ich zwar dieses Package nicht mehr benutzen, aber das brauche ich eigentlich auch nicht mehr.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz