View Javadoc

1   /*
2   * The contents of this file are subject to the BT "ZEUS" Open Source 
3   * Licence (L77741), Version 1.0 (the "Licence"); you may not use this file 
4   * except in compliance with the Licence. You may obtain a copy of the Licence
5   * from $ZEUS_INSTALL/licence.html or alternatively from
6   * http://www.labs.bt.com/projects/agents/zeus/licence.htm
7   * 
8   * Except as stated in Clause 7 of the Licence, software distributed under the
9   * Licence is distributed WITHOUT WARRANTY OF ANY KIND, either express or 
10  * implied. See the Licence for the specific language governing rights and 
11  * limitations under the Licence.
12  * 
13  * The Original Code is within the package zeus.*.
14  * The Initial Developer of the Original Code is British Telecommunications
15  * public limited company, whose registered office is at 81 Newgate Street, 
16  * London, EC1A 7AJ, England. Portions created by British Telecommunications 
17  * public limited company are Copyright 1996-9. All Rights Reserved.
18  * 
19  * THIS NOTICE MUST BE INCLUDED ON ANY COPY OF THIS FILE
20  */
21  
22  
23  
24  package zeus.visualiser.society;
25  
26  import java.io.*;
27  import java.util.*;
28  import java.awt.*;
29  import javax.swing.*;
30  import zeus.util.*;
31  import zeus.concepts.*;
32  import zeus.gui.graph.*;
33  
34  public class AnimationQueue extends Thread {
35     static final int  LETTER = 0;
36     static final int  ARROWS = 1;
37     /*** 
38      if we attempt to add more than FLUSH_QUEUE_LIMIT items to the queue it gets 
39      emptied - to stop it overflowing. 
40      */
41     static int FLUSH_QUEUE_LIMIT = 100; 
42  
43     static int   count = 0;
44  
45     long  speed  = 80;
46     int   length = 10;
47     int   mode   = LETTER;
48     Graph graph;
49  
50     protected Vector queue = new Vector();
51     protected boolean running = true;
52  
53     public AnimationQueue(Graph graph) {
54        this.graph = graph;
55        this.setPriority(Thread.NORM_PRIORITY+2);
56        this.start();
57     }
58  
59     synchronized void flush()          { queue.removeAllElements(); }
60     void              setSpeed(long s) { speed = s; }
61     long              getSpeed()       { return speed; }
62     int               getMode()        { return mode; }
63     int               getLength()      { return length; }
64  
65     void setMode(int s) {
66        switch(s) {
67           case LETTER:
68           case ARROWS:
69                mode = s;
70                break;
71           default:
72                Core.USER_ERROR("Illegal animation type " + s);
73        }
74     }
75  
76     void setLength(int L) {
77        Core.ERROR(L > 0,1,this);
78        length = L;
79     }
80  
81     void terminate() {
82        running = false;
83     }
84  
85     public synchronized void add(Performative msg, GraphNode sender,
86                                  GraphNode receiver, Color color) {
87        Core.ERROR(msg,1,this);
88        Core.ERROR(sender,2,this);
89        Core.ERROR(receiver,3,this);
90        Core.ERROR(color,4,this);
91         if (queue.size()>FLUSH_QUEUE_LIMIT) queue.removeAllElements();
92        AnimationQueueItem item =
93           new AnimationQueueItem(msg,sender,receiver,color);
94  
95        if ( queue.isEmpty() ) {
96           queue.addElement(item);
97           debug ("in add empty - notifying"); 
98           notify();
99           return;
100       }
101 
102       double s = msg.getSendTime().getTime();
103 
104       for(int i = 0; i < queue.size(); i++ ) {
105          AnimationQueueItem v = (AnimationQueueItem)queue.elementAt(i);
106          String id = v.id;
107          double s1 = v.sendTime;
108          double e1 = v.receiveTime;
109          if ( s >= e1 ) {
110             AnimationQueueConstraint constraint =
111                new AnimationQueueConstraint(id,AnimationQueueConstraint.FINISH);
112             item.addConstraint(constraint);
113          }
114          else if ( s < e1 && s > s1 ) {
115             AnimationQueueConstraint constraint =
116                new AnimationQueueConstraint(id,AnimationQueueConstraint.START);
117             item.addConstraint(constraint);
118          }
119       }
120       queue.addElement(item);
121       debug ("in add more than one - notifying"); 
122       notify();
123    }
124 
125 
126    private void debug (String str) { 
127       //  System.out.println("AnimationQueue >> " + str); 
128    }
129 
130    public void run() {
131       AnimationQueueItem item;
132 
133       while( running ) {
134          debug("in animation queue"); 
135          synchronized(this) {
136             while ( queue.isEmpty() ) {
137                try {
138                   wait();
139                   debug ("notified animation thread - waking"); 
140                }
141                catch(InterruptedException e) {
142                }
143             }
144             for(int i = 0; i < queue.size(); i++ ) {
145                item = (AnimationQueueItem)queue.elementAt(i);
146                if ( item != null ) {
147                   if ( item.isDone() )
148                      queue.removeElementAt(i--);
149                   else if ( item.isRunning() )
150                      item.next();
151                   else if ( !item.isRunning() && item.evalConstraints(queue) )
152                      item.start();
153                }
154             }
155          }
156          try {
157             sleep(speed);
158          }
159          catch(InterruptedException e) {
160          }
161          System.out.println("stopped sleeping"); 
162       }
163    }
164 
165    class AnimationQueueConstraint {
166       static final int FINISH = 1;
167       static final int START = 0;
168 
169       String id;
170       int    constraint;
171 
172       AnimationQueueConstraint(String id, int constraint) {
173          this.id = id;
174          this.constraint = constraint;
175       }
176    }
177 
178    class AnimationQueueItem {
179       Performative msg;
180       GraphNode    sender, receiver;
181       String       id;
182       Vector       constraints = new Vector();
183       Animation    duke = null;
184       double       sendTime, receiveTime;
185       Color        color;
186 
187       AnimationQueueItem(Performative msg, GraphNode sender,
188                          GraphNode receiver, Color color) {
189          this.msg = msg;
190          this.sendTime = msg.getSendTime().getTime();
191          this.receiveTime = msg.getReceiveTime().getTime();
192          this.sender = sender;
193          this.receiver = receiver;
194          this.color = color;
195          this.id = new String("AnimationQueueItemId-" + (++count));
196       }
197 
198       void addConstraint(AnimationQueueConstraint constr) {
199          constraints.addElement(constr);
200       }
201 
202       boolean isDone()    { return duke != null && duke.isDone(); }
203       boolean isRunning() { return duke != null && duke.isRunning(); }
204 
205       void next() {
206          if ( duke != null )
207             duke.next();
208       }
209 
210       boolean evalConstraints(Vector queue) {
211          boolean eval = true;
212          AnimationQueueConstraint c;
213          AnimationQueueItem item;
214 
215          for(int i = 0; i < constraints.size(); i++ ) {
216             c = (AnimationQueueConstraint) constraints.elementAt(i);
217             for(int j = 0; j < queue.size(); j++ ) {
218                item = (AnimationQueueItem) queue.elementAt(j);
219                if ( item.id.equals(c.id) ) {
220                   if ( c.constraint == AnimationQueueConstraint.START )
221                      eval = eval && item.isRunning();
222                   else if ( c.constraint == AnimationQueueConstraint.FINISH )
223                      eval = eval && item.isDone();
224                   break;
225                }
226             }
227             if ( !eval ) return eval;
228          }
229          return eval;
230       }
231 
232       void start() {
233          duke = new Animation(sender,receiver,color,msg);
234       }
235    }
236 
237 
238    class ImageLabel extends JPanel{
239        Color color;
240        JLabel label;
241        public  ImageLabel(Color color, Image image, String msg){
242           setLayout(new GridLayout(1,1));
243 	  label = new JLabel(msg,new ImageIcon(image),JLabel.CENTER);
244           label.setOpaque(true);
245           label.setBackground(color);
246           label.setFont(new Font("Helvetica", Font.PLAIN, 10));
247           label.repaint();
248 	  this.add(label);
249           Image img =  Toolkit.getDefaultToolkit().getImage(
250             SystemProps.getProperty("gif.dir") + "visualiser" +
251             File.separator + "border.gif");
252           this.setBorder(BorderFactory.createMatteBorder(2,4,2,4,
253              new ImageIcon(img)));
254           this.setPreferredSize(new Dimension(70,25));
255        }
256        public void setLetterColor(Color col){
257          label.setBackground(col);
258          label.repaint();
259        }
260    }
261 
262 
263    public class Animation {
264 
265       int  NUM_COUNT = 1;
266       int  LETTTER_WIDTH = 40;
267 
268       private GraphNode        source, target;
269       private Color            color;
270       private Point            p2, p1;
271       private boolean          done = false;
272       private boolean          running = false;
273       private int              count = 0;
274       private Image            image = null;
275       ImageLabel  imageLabel;
276 
277       public Animation(GraphNode source, GraphNode target, 
278                        Color color,Performative msg) {
279 
280          Core.ERROR(source,1,this);
281          Core.ERROR(target,2,this);
282          Core.ERROR(color,3,this);
283 
284          this.source = source;
285          this.target = target;
286          this.color  = color;
287 
288          Rectangle a = graph.getBounds(source);
289          Rectangle b = graph.getBounds(target);
290          p1 = new Point(a.x+a.width/2,a.y+a.height/2);
291          p2 = new Point(b.x+b.width/2,b.y+b.height/2);
292 
293          image = Toolkit.getDefaultToolkit().getImage(
294             SystemProps.getProperty("gif.dir") + "visualiser" +
295             File.separator + "anim.gif");
296 
297          imageLabel = new ImageLabel(color,image,msg.getType());
298          imageLabel.setVisible(false);
299          graph.add(imageLabel);
300          graph.validate();
301 
302          running = true;
303          switch( mode ) {
304             case LETTER:
305                  imageLabel.setVisible(true);
306 		 drawLetter(p1,imageLabel);
307                  break;
308             case ARROWS:
309                  drawArrows(p1,p2);
310                  break;
311          }
312       }
313 
314       boolean isDone()    { return done;    }
315       boolean isRunning() { return running; }
316 
317       void next() {
318          Rectangle a, b;
319          double phi, dist, len;
320 
321          if ( !done && (mode == LETTER) ) {
322             if ( !(imageLabel.isVisible()) )
323                imageLabel.setVisible(true);
324 
325 	    b = graph.getBounds(target);
326             p2 = new Point(b.x+b.width/2,b.y+b.height/2);
327             dist  = Math.sqrt(Math.pow((p2.x-p1.x),2) +
328                     Math.pow((p2.y-p1.y),2));
329             phi = ArrowData.GetAngle((double)p1.x, (double)p1.y,
330                                      (double)p2.x, (double)p2.y);
331             len = dist > length ? length : dist;
332             p1.x += (int) (len*Math.cos(phi));
333             p1.y += (int) (len*Math.sin(phi));
334 
335             drawLetter(p1,imageLabel);
336             done = b.contains(p1);
337          }
338          else if ( !done && mode == ARROWS ) {
339             imageLabel.setVisible(false);
340             graph.repaint();
341 	    drawArrows(p1,p2);
342             a = graph.getBounds(source);
343             b = graph.getBounds(target);
344             p1 = new Point(a.x+a.width/2,a.y+a.height/2);
345             p2 = new Point(b.x+b.width/2,b.y+b.height/2);
346             drawArrows(p1,p2);
347             done = ((++count) == NUM_COUNT);
348          }
349 
350          if ( done ) {
351             graph.remove(imageLabel);
352 	    running = false;
353             if (mode == ARROWS) drawArrows(p1,p2);
354 
355          }
356       }
357 
358       protected void drawArrows(Point p1, Point p2) {
359          Graphics g = graph.getGraphics();
360          if ( g != null ) {
361             double phi, dist, len;
362 
363             g.setXORMode(graph.getBackground());
364             Color col = g.getColor();
365             g.setColor( this.color );
366 
367             drawThickLine(g,p1,p2);
368 
369             Point x1 = new Point(p1.x,p1.y);
370             Point x2 = new Point(0,0);
371 
372             dist  = Math.sqrt(Math.pow((p2.x-x1.x),2) +
373                               Math.pow((p2.y-x1.y),2));
374             phi = ArrowData.GetAngle((double)x1.x, (double)x1.y,
375                                      (double)p2.x, (double)p2.y);
376             len = dist > 1.5*length ? 1.5*length : dist;
377             x2.x = x1.x + (int) (len*Math.cos(phi));
378             x2.y = x1.y + (int) (len*Math.sin(phi));
379             drawArrow(g,x1,x2);
380             while( len >= 1.5*length ) {
381                x1.x = x2.x; x1.y = x2.y;
382                dist  = Math.sqrt(Math.pow((p2.x-x1.x),2) +
383                                  Math.pow((p2.y-x1.y),2));
384                phi = ArrowData.GetAngle((double)x1.x, (double)x1.y,
385                                         (double)p2.x, (double)p2.y);
386                len = dist > 1.5*length ? 1.5*length : dist;
387                x2.x = x1.x + (int) (len*Math.cos(phi));
388                x2.y = x1.y + (int) (len*Math.sin(phi));
389                drawArrow(g,x1,x2);
390             }
391             g.setColor( col );
392          }
393       }
394 
395       protected void drawThickLine(Graphics g, Point p1, Point p2) {
396          if ( p1 != null && p2 != null && g != null ) {
397             int w = 2;
398             double theta = ArrowData.GetAngle((double)p1.x,(double)p1.y,
399                                               (double)p2.x,(double)p2.y);
400             double alpha = theta + Math.PI/2.0;
401             double beta  = theta - Math.PI/2.0;
402             int x, y;
403             Polygon pg = new Polygon();
404 
405             x = (int)((double)p1.x + (double)(w*Math.cos(alpha)));
406             y = (int)((double)p1.y + (double)(w*Math.sin(alpha)));
407             pg.addPoint(x,y);
408 
409             x = (int)((double)p1.x + (double)(w*Math.cos(beta)));
410             y = (int)((double)p1.y + (double)(w*Math.sin(beta)));
411             pg.addPoint(x,y);
412 
413             x = (int)((double)p2.x + (double)(w*Math.cos(beta)));
414             y = (int)((double)p2.y + (double)(w*Math.sin(beta)));
415             pg.addPoint(x,y);
416 
417             x = (int)((double)p2.x + (double)(w*Math.cos(alpha)));
418             y = (int)((double)p2.y + (double)(w*Math.sin(alpha)));
419             pg.addPoint(x,y);
420 
421             g.fillPolygon(pg);
422          }
423       }
424 
425 
426       protected void drawArrow(Graphics g, Point p1, Point p2) {
427           Point[] pts;
428 
429           g.drawLine( p1.x, p1.y, p2.x, p2.y );
430           pts = ArrowData.getPoints((double)p1.x, (double)p1.y,
431                                     (double)p2.x, (double)p2.y);
432           for( int i = 0; i < 2; i++ )
433              g.drawLine( pts[i].x, pts[i].y, pts[i+1].x, pts[i+1].y);
434       }
435 
436      protected void drawLetter(Point c, ImageLabel label) {
437          label.setLocation(c);
438          graph.repaint();
439      }
440 
441    }
442 }