unit animator; {animate simulations of the simulation toolbox Author: Lin Jensen, Bishop's University Date: 26 November 1996} { This unit adds the global object AnimationMonitor, and defines AnimationObj - an object that will be drawn instantiate one for every such object, typically as a field of anything you want to "see". You can point to the "parent", see QueueAnimator -- descendent type for showing a queue, which it points to as its "parent" You will probably want to redefine AnimationObj methods Draw and Erase to properly represent your object. The default is to Draw a * and erase to a "footprint" defined here as a CONST To animate a simulation, instead of Monitor.Runsimulation, call ANIMATE (SimLength, Samples, SimSlice, RealSlice) -- where SimLength & Samples are as before SimSlice is the interval of SIMULATED time and RealSlice is the real time interval (in seconds) to update the animation. also, if anything moves, you are responsible for changing the coordinates in AnimationObj.new (fields x,y:real) THIS IMPLEMENTATION is for a text screen. I expect somebody will modify it for graphics screen. START and STOP should enter and leave graphics mode, each DRAW and ERASE will have to be modified to use graphics. Everything else can stay as is. } INTERFACE USES dos, clock, queue, coproc, proc_man, usebios; {NOTE: Use a version of Proc_Man of November 25, 1996 or later } const Footprint:char = ' ' ; {use space for erase, or other char. to leave a "footprint"} TYPE anAnimator = object Animating : boolean; {is animation in progress?} constructor init; procedure RUNAnimatedSIMULATION (SimLength : REAL; SampleSize : WORD; SimSlice, RealSlice : REAL); {USE in place of Monitor.runSimulation} {---- redefine these for graphics screen -----------} procedure Start; virtual; {Animation screen} procedure Stop; virtual; private AnimationList : FifoQueue; {of AnimationObj} lastshown: real; {real time timer used by show} procedure Show (timeslice:real); {show animation after real time interval, in seconds} end; position = record { screen coordinates } x,y:real; end; AnimationPtr = ^AnimationObj; AnimationObj = object (queueElement) old:position; {where last shown, used by Update} new:position; {--this one you must keep current as appropriate} constructor Init (x, y:real {initial position}); procedure Update; procedure Draw; virtual; procedure Erase; virtual; destructor destroy; virtual; {take out of list} end; {--- specific animator for queues (bar shown) ----} QueueAnimator = object (AnimationObj) parent : ^list; MaxLen : integer; {max. length shown} constructor init (x,y:real; maxShown: integer; VAR Q:list); procedure Draw; virtual; procedure Erase; virtual; end; VAR ANIMATIONMonitor : anAnimator; procedure ANIMATE (SimLength : REAL; SampleSize : WORD; SimSlice, RealSlice : REAL); IMPLEMENTATION procedure ANIMATE(SimLength : REAL; SampleSize : WORD; SimSlice, RealSlice : REAL); begin AnimationMonitor.RunAnimatedSimulation (simlength, samplesize, simslice, realslice); end; constructor AnAnimator.Init; begin Animating := False; AnimationList.init; end; procedure anAnimator.RUNAnimatedSIMULATION (SimLength : REAL; SampleSize : WORD; SimSlice, RealSlice : REAL); {run the simulation using co-processes until time or number of samples expires} begin Start; { Monitor.RunSimulation (SimLength, SampleSize);} with MONITOR do begin {set termination conditions} SamplesTaken := 0; SamplesToTake := SampleSize; TimeToStop := SimLength; end; while Monitor.KeepGoing do begin Sleep_For (SimSlice); Show (realSlice); end; Stop; end; {RunAnimatedSimulation} procedure anAnimator.Start; var hr,m,s,cent:word; animal : animationPtr; begin ClrScr; {base version for text screen} Animating := True; Gettime (hr,m,s,cent); lastshown := s + cent/100; { Draw all existing objects} animal := animationPtr(animationList.headq); while animal <> NIL do begin animal^.draw; animal := animationPtr(animal^.next); end; end; procedure anAnimator.Stop; begin GotoXY (1,25); WaitKeyPress; ClrScr; Animating := False; end; procedure anAnimator.Show (timeslice:real); var hr,m,s,cent:word; realtime, showtime : real; animal : animationPtr; begin showtime := lastshown + timeslice; { - wait until realtime >= lastshown + timeslice } REPEAT Gettime (hr,m,s,cent); realtime := s + cent/100; {ASSUMES: timeslice < 60 seconds} if realtime < lastshown then realtime := realtime + 60.0; UNTIL realtime >= showtime; if showtime >= 60.0 then showtime := showtime - 60.0; lastshown := showtime; { For all animationObj in list do Erase; Draw } animal := animationPtr(animationList.headq); while animal <> NIL do begin animal^.update; animal := animationPtr(animal^.next); end; {======== write simulation clock =====} gotoXY (65,25); TheClock.rite ; end; constructor Animationobj.init (x, y:real{initial position}); {call this base constructor AFTER establishing all special stuff needed for inital Draw} begin QueueElement.Init; old.x := x; old.y := y; new := old; AnimationMonitor.AnimationList.Tail_insert (addr(self)); if AnimationMonitor.Animating then DRAW; {initial drawing} { Else START will draw it} end; {init} procedure Animationobj.Update; begin {if appropriate, prefix this with IF OLD <> NEW to avoid unnecessary drawing} Erase; old := new; Draw; end; procedure Animationobj.Draw; begin gotoXY (round(new.x), round(new.y)); write ('*'); {default char.} end; procedure Animationobj.Erase; begin gotoXY (round(old.x), round(old.y)); write (FootPrint); {blank, or leave trail?} end; destructor Animationobj.destroy; {take out of list} var dmy : ElementPtr; begin Erase; {off the screen} dmy := animationMonitor.animationlist.remove(addr(self)); queueElement.destroy; end; {destroy} constructor QueueAnimator.init (x,y:real; maxShown: integer; VAR Q:list); begin parent := addr(q); {must init Q first!} MaxLen := maxShown; AnimationObj.init(x,y); end; procedure QueueAnimator.Draw; var c,n:integer; begin gotoXY (round(new.x), round(new.y)); {standard location} n := parent^.card; {this many chars} if n > maxLen then n := MaxLen; {but limit bar length...} for c := 1 to n do write ('Û'); if n < parent^.card then write ('²'); {...indicate more} end; procedure QueueAnimator.Erase; var c:integer; begin gotoXY (round(new.x), round(new.y)); for c := 1 to maxLen+1 do write (' '); end; begin animationMonitor.init; end.