1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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 }