der springende Punkt

Für Probleme bezüglich Grafik, Audio, GL, ACS, ...
Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

der springende Punkt

Beitrag von Frank8001 »

Hallo!
Ich bin ein blutiger Lazarus-Einsteiger, habe aber Delphi-Erfahrung. Auch wenn die Befehle, Objekte, etc. etwa vergleichbar sind, reagiert Lazarus zumindest mit der Graphik etwas anders.

Ich möchte ein kleines Programm erstellen, in dem ein Bildpunkt (mit variabler Größe) auf der Mitte des Bildschirms von rechts nach links läuft, und zwar mit variabler also einstellbarer Geschwindigkeit.
Wenn das Programm dann mal groß ist, soll es eine EKG-Simulation werden. Ich habe schon viel gegoogelt, aber bislang nix gefunden, was mir weitergeholfen hat.

Macht man sowas über image.canvas.pixels[x,y] oder gibt es eine bessere Methode?

Über Hilfe wäre ich sehr dankbar!

Gruß
Frank

Benutzeravatar
theo
Beiträge: 10921
Registriert: Mo 11. Sep 2006, 19:01

Re: der springende Punkt

Beitrag von theo »


Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: der springende Punkt

Beitrag von corpsman »

Hi Frank8001,

Willkommen im Forum.

Code: Alles auswählen

Wenn das Programm dann mal groß ist, soll es eine EKG-Simulation werden.
Das heist du willst etwas Proffesionelles machen ? In dem Fall wäre canvas.pixels ungeeignet.

Für ein "Schulprojekt" oder mal zum ausprobieren reicht Canvas.pixels jedoch. Ich persöhnlich würde ja OpenGL nehmen, ..
in dem ein Bildpunkt (mit variabler Größe)
Hier für wäre canvas.pixels weniger gut geeignet, habe schon gelesen, dass es "Anfänger" gibt die hierfür ein TImage nehmen und dessen .left und .right Koordinate über einen TTimer ändern. Auch hierbei würde ich lieber OpenGL nehmen.

Damit man dir Fundierte Hinweise oder Richtungen nennen kann wäre es sinnvoll, wenn du mehr über dich erzählst. Was ist dein Mathematischer Hintergrund? Eine EKG Funktion ist alles andere als Linear => Woher willst du deine Geschwindigkeits und Richtungsdaten nehmen ?
Hast du einen Projektplan? Welche weiteren Funktionen willst du Realisieren? Was willst du später mit deiner Anwendung erreichen?

Am allerbesten ist allerdings, wenn du einfach mal anfängst, all die oben genannten Fragen erläuterst und uns dann konkrete Punkte nennst an denen du nicht mehr weiter kommst ( mit Quellcode ). Eine global gehaltene Frage kann nie Spezifische Informationen enthalten *g*.
--
Just try it

Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

Re: der springende Punkt

Beitrag von Frank8001 »

Vielen Dank schon mal für die Antwort.
Ich hatte vor, die y-Koordinaten eines EKG-Komplexes als Matrix in einer Variablen zu speichern. Die x-Koordinaten laufen ja mit konstanter Geschwindigkeit von rechts nach links durch. Aus verschiedenen Variablen, die zu geg. Zeitpunkt abgerufen werden (frequenzabhängig), werden dann die y-Koordinaten ausgelesen. So wäre es möglich, unterschiedliche Formen von EKG-Komplexen zu simulieren.
Sollte das Problem mit dem EKG gelöst sein, folgt die Implementierung von weiteren Vitalwert-Kurven, wie z.B. CO2, SO2, etc.
Wenn das Programm dann ausgewachsen ist, soll es eine Simulationssoftware für unseren Patientensimulator darstellen.
Man soll über diverse Regler die Vitalwerte (Herzfrequenz, SO2, Blutdruck, CO2, etc.) sowie die dazugehörigen Kurvenformen ändern können.
Optimalerweise wäre noch die Implementierung der Fähigkeit, Midi-Daten eines Midi-Controlers (zB. Behringer BCF 2000) zu empfangen, um so Daten (Fader und Regler, bzw. Taster) zur Steuerung der Frequenz zu verwenden.
Ich weiß, das ist kein kleines Projekt. Aber ich mach mich mal drüber und schaue, wie weit ich komme. Von nix kommt nix.

Gruß
Frank

Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

Re: der springende Punkt

Beitrag von Frank8001 »

Teil 2:
Problem 1:
Suche also zum Einen eine Funktion, die den Bildpunkt erzeugt. ....canvas.pixels[x,y] erscheint mir etwas hölzern und ungelenk. Ausserdem habe ich beim Experimentieren immer den Effekt (den ich auch schon von Delphi kenne), dass eine Graphikfunktion immer erst nach Beendigung der Funktionsroutine sichtbar wird. Sprich, setze ich in einer Routine mehrfach den Befehl ....canvas.pixels... ab, sehe ich erst nach Beendigung der Routine die gesamte Veränderung (also eine ganze Linie von Links nach Rechts) und nicht die Einzelschritte (das Entstehen der Linie). Das geht sicher eleganter als mit canvas.pixels....
Da bräuchte ich aber eine Anleitung oder zumindest einen konkreten Befehl (und ich glaube mit einem Befehl ist das nicht getan :( )
Problem 2,3,4,5,...:
...ergibt sich eigentlich erst, wenn Problem 1 gelöst ist.

Wann soll eine dieser Matrixvariablen (in denen die y-Koordinaten des EKG-Komplexes gespeichert sind) aus gelesen werden?
Da habe ich mir folgendes gedacht:
Aus der vorgegebenen Herzfrequenz lässt sich ja eine Zeit errechnen, nach dem der nächste EKG-Komplex beginnen muss. Diese sei Delta-t (dt) genannt. (1/Herz-Freq=dt)
Mit Beginn des Auslesens einer EKG-Matrixvariabelen (in der Folge EMV genannt) wird die Systemzeit in einer Variabelen gespeichert (genannt t1). In der Punkt-von-links-nach-rechts-Routine wird die Systemzeit wiederkehrend abgefragt und die Differenz dieser Zeit und t1 mit dt verglichen. Übersteigt die Differenz dt, wird ein neuer EMV-Ausleseprozess gestartet.

Gibt es eine Möglichkeit, Midi-Input-Daten abzufragen und daraus Parameteränderungen abzuleiten? Wird die Änderung eines Midi-Input-Status angezeigt und kann man damit eine Prozedur iniziieren?

Ich weiß, dass klingt einfacher als es werden wird!
Aber wie gesagt, wir brauchen dieses Programm zum Aufbau unseres Zentrums für Patientensicherheit und Zwischenfallsimulation und daher werde ich nicht nachlassen, bis es läuft. So ein Programm gibt es leider nicht in dieser Form auf dem Markt.

Gruß und vielen Dank im Voraus für Eure Mühen!!!
Frank

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: der springende Punkt

Beitrag von Michl »

Suche also zum Einen eine Funktion, die den Bildpunkt erzeugt. ....canvas.pixels[x,y] erscheint mir etwas hölzern und ungelenk.
Ich finde die Vorgehensweise mit canvas.pixels erstmal nicht schlecht, da man meines Erachtens nach den viel höheren Aufwand der Modulation der Funktionen recht einfach darstellen kann. Ein Wechsel später zu einer geeigneteren Darstellung sollte dann relativ einfach zu bewerkstelligen sein und lenkt wahrscheinlich jetzt erstmal zu sehr von der zu verwirklichenden Aufgabe ab. Ist aber nur meine Meinung!
Ausserdem habe ich beim Experimentieren immer den Effekt (den ich auch schon von Delphi kenne), dass eine Graphikfunktion immer erst nach Beendigung der Funktionsroutine sichtbar wird.
Ich weiss nicht, wie aufwendig die Berechnung der Punkte ist, doch die Darstellung eines Pixels dauert nur Bruchteile von Sekunden (Selbst mit mehreren 100 bzw. 1000 Pixeln dürfte die Darstellung noch flüssig laufen).

Ich denke dies liegt eher daran, dass deine Routine zum Berechnen der einzelnen Punkte zu seitaufwendig ist, um diese Darstellung gut zu bewerkstelligen.

Eine Lösung könnte da eine geringe Punktzahl (evtl. dann eine Linie zwischen den Punkten) oder der Einsatz eines Timers darstellen.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: der springende Punkt

Beitrag von m.fuchs »

Frank8001 hat geschrieben:Ausserdem habe ich beim Experimentieren immer den Effekt (den ich auch schon von Delphi kenne), dass eine Graphikfunktion immer erst nach Beendigung der Funktionsroutine sichtbar wird. Sprich, setze ich in einer Routine mehrfach den Befehl ....canvas.pixels... ab, sehe ich erst nach Beendigung der Routine die gesamte Veränderung (also eine ganze Linie von Links nach Rechts) und nicht die Einzelschritte (das Entstehen der Linie). Das geht sicher eleganter als mit canvas.pixels....
Wenn du zwischendurch mal Application.ProcessMessages aufrufst, siehst du auch die einzelnen Schritte.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

Re: der springende Punkt

Beitrag von Frank8001 »

Das ist ja schon mal ein Ansatz!!!
Vielen Dank! Werde damit mal experimentieren.
Gruß
Frank

Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

Re: der springende Punkt

Beitrag von Frank8001 »

So!
Bin ja dann schon mal wesentlich weiter. Es läuft eine wunderschöne EKG-Kurve über den Monitor. Nur: leider viel zu langsam!

Habe 'Image.canvas.pixels[x,y);' verwendet, in einer Schleife 'for x=1 to image.width then begin'
In einem Array werden vorher die y-Koordinaten gespeichert: EKG[0..40]: array of integer, welch dann eine nach der anderen ausgelesen werden.
mit application.ProcessMessages werden die Veränderungen auch angezeigt, wird für jedes x wiederholt.
Vor dem EKG-Komplex läuft ein schwarzes Rechteck von rechts nach links her um die alte Kurve zu löschen und Platz für die Neue zu machen:
Image.canvas.rectangle(x,1,x+10,image.height); brush.color:=clblack; ....bssolid

Es dauert ca 15-20s bis eine Zeile EKG geschrieben ist. Das ist zu langsam. Bräuchte etwa die doppelte Geschwindigkeit.
Und, das Rechteck löscht die alte Kurve nicht. Keine Ahnung warum nicht.

Dazu habe ich 2 Ideen:
1. Man geht nicht x für x durch sondern überspringt immer einen Punkt. Sieht vermutlich dann etwas zackiger aus.
2. Wie initial von corpsman schon vorgeschlagen mit OpenGL. Allerdings: keine Ahnung wie!

Verstehe vor allem nicht, warum dieser doch eigentlich nicht so aufwendige Graphikprozess so lange braucht.
Für Anregungen wie immer sehr dankbar!

Gruß
Frank

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: der springende Punkt

Beitrag von m.fuchs »

Frank8001 hat geschrieben:1. Man geht nicht x für x durch sondern überspringt immer einen Punkt. Sieht vermutlich dann etwas zackiger aus.
Mach mal nur bei jedem zweiten Punkt ein Application.ProcessMessages.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: der springende Punkt

Beitrag von gocher »

Also ich bin da nicht der Experte, aber ich glaube auch Positionen beginnen bei 0 demnach image.height-1
Frank8001 hat geschrieben: Image.canvas.rectangle(x,1,x+10,image.height); brush.color:=clblack; ....bssolid
Auf jeden Fall kannst Du Dir schon mal sparen 9-mal die selben Pixel zu überschreiben, in dem das Du nur einen Pixel übermalst!

Code: Alles auswählen

Image.canvas.rectangle(x+9,0,x+10,image.height-1); brush.color:=clblack; .... 
Allerdings gibt es auch einen Befehl zum Zeichnen einer schwarzen Linie, sollte so funktionieren und sollte auch wesentlich schneller sein!

Code: Alles auswählen

Image.Canvas.Pen.Color := clBlack;
Image.Canvas.Line(x+10,0,x+10,image.height-1);
Image.Canvas.Pen.Color:= cl....; //natürlich die Farbe zurücksetzen
 
Aber wie gesagt ich bin da kein Experte!

Ich benutze die Funktionen von GDI+ unter Windows mit denen werden in Bruchteilen von Sekunden riesige Bilder gedreht, gespiegelt, gekippt, Ausschnitte genommen und skaliert also beim zeichnen einer Kurve musst Du wohl noch andere Problemchen in Deinem Programm haben weshalb es nicht flüssig läuft! Zeig doch mal die Schleife in der Du die Kurve zeichnest!
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

Re: der springende Punkt

Beitrag von Frank8001 »

Danke für die raschen Antworten!

Habe leider den Code nicht dabei, aber ich versuche es grob zu rekonstruieren:

Code: Alles auswählen

Var
XLE,YLE: integer; //alte x/y-Koordinaten
EKG: array[0..40] of integer; //y-Koordinaten (werden in anderer Procedur mit Werten beschrieben, die dann eine Kurve bilden
tsys,tas: TTime;
dt,dtss,dtms: word;
 
//Systemzeit speichern
tas:= time
 
For x=1 to image.width do begin
 
//Systemzeit speichern
tsys := time;
 
//Berechnung der Intervallzeit dt zwischen 2 EKG-Komplexen aus der Frequenz die im Eingabefeld geändert werden kann
dt := 60 / strtoint(Eingabefeld.text);
//Zerlegung in Sekunden und Milisekunden
dtss:=floattostr(trunc(dt)); 
dtms:=floattostr(dt - trunc(dt)); 
 
//wenn Intervall zwischen 2 Komplexen vorbei:
if tsys > tas + encode(0,0,dtss,dtms) then begin
  tsys :=time; //neues Intervall starten
  a:= 0;
  end;
 
//solange innerhalb des Intervalls: Graphikprozess
if tsys < tas + encode(0,0,dtss,dtms) then begin
  canvas.rectangle(x,1,x+10,image.height);
  canvas.line(XLE,YLE,x,EKG[a]);
  XLE:=x;
  YLE:=EKG[a];
  application.ProcessMessages;
  end;
 
end;
So lautet der Code in etwa grob (in meiner Erinnerung). Fehlen höchstens ein paar Variablenkonvertierungen oä.

Gruß
Frank
Zuletzt geändert von Lori am Mi 8. Mai 2013, 14:37, insgesamt 1-mal geändert.
Grund: Highlighter

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: der springende Punkt

Beitrag von gocher »

mal ein paar Gedanken dazu:

Code: Alles auswählen

var
  //XLE,
  YLE: integer; //alte x/y-Koordinaten
  EKG: array[0..40] of integer; //y-Koordinaten (werden in anderer Procedur mit Werten beschrieben, die dann eine Kurve bilden
  tsys,tas: TTime;
  ct: real; //*** muss real oder float sein
  a, x, dt, dtss,dtms, Code: word;
//...
begin
  //Systemzeit speichern
  tas:= time;
  x := 1;
  while x < Image.width do //loop
  begin
    a := 0; //*** irgendwo sollte a initialisiert werden
    //Systemzeit speichern
    tsys := time;
 
    //Berechnung der Intervallzeit dt zwischen 2 EKG-Komplexen aus der Frequenz die im Eingabefeld geändert werden kann
    //*** falls nicht vorgesehen ist die Eingabe in der Schleife zu ändern, dann
    //*** die folgenden Zeilen ausserhalb der Schleife legen
    Val(Eingabefeld.text, dt, Code); //*** Wandlung mit etwas mehr Sicherheit
    if Code = 0 then  //*** Wert war gültig
    begin
      //Zerlegung in Sekunden und Milisekunden
      dtss := 60 DIV dt; //*** ich denke das ist sauberer
      dtms := Trunc((60/dt)-dtss) * 1000; //*** das wäre der Millisekundenanteil sonst muss die Variable vom Typ real sein damit Du die nachkommastellen bekommst
      ct := encode(0,0,dtss,dtms);
    end;
 
    //wenn Intervall zwischen 2 Komplexen vorbei:
    if tsys > tas + ct then
    begin
      //tsys := time; //neues Intervall starten
      tas := time; //*** sonst startet kein neues Intervall
      a := 0;
    end
    //solange innerhalb des Intervalls: Graphikprozess
    else //*** if tsys < tas + ct then
    begin
      Image.Canvas.Pen.Color := clBlack;
      //*** 10 Pixel weiter zurücksetzen
      if x<Image.Width-10 then
        Image.Canvas.Line(x+10, 0, x+10, Image.height-1)
      else
        Image.Canvas.Line(x+10-Image.Width, 0, x+10-Image.Width, Image.height-1);
      Image.Canvas.Pen.Color:= clGreen; //natürlich die Farbe zurücksetzen
      Image.Canvas.Line(x-1,YLE,x,EKG[a]);
      //XLE:=x; //alte x-Koordinate kannst Du Dir sparen => x-1
      YLE:=EKG[a];
      application.ProcessMessages;
    end;
    inc(a); //*** irgendwo sollte a erhöht werden
    inc(x); //*** und x  
    if (x > Image.Width-1) then //*** eine Möglichkeit zum durchstarten
      x := 0; //*** Achtung Endlosschleife, Ausstiegsmöglichkeit sollte noch vorgesehen werden 
  end;
 
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Frank8001
Beiträge: 24
Registriert: Fr 26. Apr 2013, 20:28

Re: der springende Punkt

Beitrag von Frank8001 »

Vielen Dank an 'gocher' für die Mühe!!!!
Habe das so umgesetzt und nur kleine Veränderungen vornehmen müssen. Sieht etwas eleganter aus als vorher.
Lief allerdings nicht schneller.
Was es gebracht hat, war, dass 'application.processmessages' nur jedes zweite Mal durchgeführt wird. Das scheint ja auch logisch.
Also nochmal vielen Dank, das hat mir sehr geholfen!

Gruss
Frank

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: der springende Punkt

Beitrag von gocher »

Also in meinen Anmerkungen habe ich angedeutet die Zeilen mit der Umwandlung des Wertes aus dem Eingabefeldes und den Berechnungen außerhalb der Schleife zu legen, hast Du das einmal gemacht?
Ich weiß ja nicht was in Deiner Funktion encode(0,0,dtss,dtms) passiert aber, meines Erachtens müsste der Rest schnell durchlaufen bei ähnlichen Sachen musste ich schon Verzögerungen einbauen, damit man noch etwas erkennen konnte!

Also z.B. folgendes Beispiel läuft so schnell das ich nicht sehen kann wo aktuell der Punkt gezeichnet wird ohne das ich einen Breakpoint in der Schleife setze!

Code: Alles auswählen

var
  EKG: array[0..40] of integer;
  a, x: word;
begin
  Randomize;
  for a:=0 to 40 do
    EKG[a] := Random (Image.Height);
  x := 1;
  a := 0;
  while x < Image.width do
  begin
    Image.Canvas.Pen.Color := clBlack;
    if x<Image.Width-10 then
      Image.Canvas.Line(x+10, 0, x+10, Image.height-1)
    else
      Image.Canvas.Line(x+10-Image.Width, 0, x+10-Image.Width, Image.height-1);
    Image.Canvas.Pen.Color:= clWhite;
    Image.Canvas.Line(x-1,EKG[a-1],x,EKG[a]);
    application.ProcessMessages;
    inc(a);
    inc(x);
    if x = Image.Width then x := 1;
    if a = 40 then a := 0;
  end; 
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Antworten