Some finalizations.
[kic.git] / sequence.pic
blobcf86d6b1f5c587cbdae5b33eef05a0ee39a877cf
1 #/usr/bin/pic2plot -Tps
3 # Pic macros for drawing UML sequence diagrams
5 # (C) Copyright 2004-2005 Diomidis Spinellis.
7 # Permission to use, copy, and distribute this software and its
8 # documentation for any purpose and without fee is hereby granted,
9 # provided that the above copyright notice appear in all copies and that
10 # both that copyright notice and this permission notice appear in
11 # supporting documentation.
13 # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
14 # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
15 # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 # $Id$
21 # Default parameters (can be redefined)
23 # Spacing between messages
24 spacing = 0.25;
25 # Active box width
26 awid = .1;
27 # Box height
28 boxht = 0.3;
29 # Bix width
30 boxwid = 0.75
31 boxwid = 1.3
32 # Commend folding
33 corner_fold=awid
34 # Comment distance
35 define comment_default_move {up 0.25 right 0.25};
36 # Comment height
37 comment_default_ht=0.5;
38 # Comment width
39 comment_default_wid=1;
40 # Underline object name
41 underline=1;
43 # Create a new object(name,label)
44 define object {
45         $1: box $2; move;
46         # Could also underline text with \mk\ul\ul\ul...\rt
47         if (underline) then {
48                 line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
49         }
50         move to $1.e;
51         move right;
52         # Active is the level of activations of the object
53         # 0 : inactive : draw thin line swimlane
54         # 1 : active : draw thick swimlane
55         # > 1: nested : draw nested swimlane
56         active_$1 = 0;
57         lifestart_$1 = $1.s.y;
60 # Create a new external actor(name,label)
61 define actor {
62         $1: [
63                 XSEQC: circle rad 0.06;
64                 XSEQL: line from XSEQC.s down .12;
65                 line from XSEQL.start - (.15,.02) to XSEQL.start + (.15,-.02);
66                 XSEQL1: line from XSEQL.end left .08 down .15;
67                 XSEQL2: line from XSEQL.end right .08 down .15;
68                 line at XSEQC.n invis "" "" "" $2;
69         ]
70         move to $1.e;
71         move right;
72         active_$1 = 0;
73         lifestart_$1 = $1.s.y - .05;
76 # Create a new placeholder object(name)
77 define placeholder_object {
78         $1: box invisible;
79         move;
80         move to $1.e;
81         move right;
82         active_$1 = 0;
83         lifestart_$1 = $1.s.y;
86 define pobject {
87         placeholder_object($1);
90 define extend_lifeline {
91         if (active_$1 > 0) then {
92                 # draw the left edges of the boxes
93                 move to ($1.x - awid/2, Here.y);
94                 for level = 1 to active_$1 do {
95                         line from (Here.x, lifestart_$1) to Here;
96                         move right awid/2
97                 }
99                 # draw the right edge of the innermost box
100                 move right awid/2;
101                 line from (Here.x, lifestart_$1) to Here;
102         } else {
103                 line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
104         }
105         lifestart_$1 = Here.y;
108 # complete(name)
109 # Complete the lifeline of the object with the given name
110 define complete {
111         extend_lifeline($1)
112         if (active_$1) then {
113                 # draw bottom of all active boxes
114                 line right ((active_$1 + 1) * awid/2) from ($1.x - awid/2, Here.y);
115         }
118 # Draw a message(from_object,to_object,label)
119 define message {
120         down;
121         move spacing;
122         # Adjust so that lines and arrows do not fall into the
123         # active box.  Should be .5, but the arrow heads tend to
124         # overshoot.
125         if ($1.x <= $2.x) then {
126                 off_from = awid * .6;
127                 off_to = -awid * .6;
128         } else {
129                 off_from = -awid * .6;
130                 off_to = awid * .6;
131         }
133         # add half a box width for each level of nesting
134         if (active_$1 > 1) then {
135                 off_from = off_from + (active_$1 - 1) * awid/2;
136         }
138         # add half a box width for each level of nesting
139         if (active_$2 > 1) then {
140                 off_to = off_to + (active_$2 - 1) * awid/2;
141         }
143         if ($1.x == $2.x) then {
144                 arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " " " " ;
145         } else {
146                 arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
147         }
150 # Display a lifeline constraint(object,label)
151 define lifeline_constraint {
152         off_from = awid;
153         # add half a box width for each level of nesting
154         if (active_$1 > 1) then {
155                 off_from = off_from + (active_$1 - 1) * awid/2;
156         }
158         box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
161 define lconstraint {
162         lifeline_constraint($1,$2);
165 # Display an object constraint(label)
166 # for the last object drawn
167 define object_constraint {
168         { box invis with .s at last box .nw $1 ljust; }
171 define oconstraint {
172         object_constraint($1);
175 # Draw a creation message(from_object,to_object,object_label)
176 define create_message {
177         down;
178         move spacing;
179         if ($1.x <= $2.x) then {
180                 off_from = awid * .6;
181                 off_to = -boxwid * .51;
182         } else {
183                 off_from = -awid * .6;
184                 off_to = boxwid * .51;
185         }
187         # add half a box width for each level of nesting
188         if (active_$1 > 1) then {
189                 off_from = off_from + (active_$1 - 1) * awid/2;
190         }
192         # See comment in destroy_message
193         XSEQA: arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) "«create»" " ";
194         if ($1.x <= $2.x) then {
195                 { XSEQB: box $3 with .w at XSEQA.end; }
196         } else {
197                 { XSEQB: box $3 with .e at XSEQA.end; }
198         }
199         {
200                 line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
201         }
202         lifestart_$2 = XSEQB.s.y;
203         move (spacing + boxht) / 2;
206 define cmessage {
207         create_message($1,$2,$3);
210 # Draw an X for a given object
211 define drawx {
212         {
213         line from($1.x - awid, lifestart_$1 - awid) to ($1.x + awid, lifestart_$1 + awid);
214         line from($1.x - awid, lifestart_$1 + awid) to ($1.x + awid, lifestart_$1 - awid);
215         }
218 # Draw a destroy message(from_object,to_object)
219 define destroy_message {
220         down;
221         move spacing;
222         # The troff code is \(Fo \(Fc
223         # The groff code is also \[Fo] \[Fc]
224         # The pic2plot code is \Fo \Fc
225         # See http://www.delorie.com/gnu/docs/plotutils/plotutils_71.html
226         # To stay compatible with all we have to hardcode the characters
227         message($1,$2,"«destroy»");
228         complete($2);
229         drawx($2);
232 define dmessage {
233         destroy_message($1,$2);
236 # An object deletes itself: delete(object)
237 define delete {
238         complete($1);
239         lifestart_$1 = lifestart_$1 - awid;
240         drawx($1);
243 # Draw a message return(from_object,to_object,label)
244 define return_message {
245         down;
246         move spacing;
247         # See comment in message
248         if ($1.x <= $2.x) then {
249                 off_from = awid * .6;
250                 off_to = -awid * .6;
251         } else {
252                 off_from = -awid * .6;
253                 off_to = awid * .6;
254         }
256         # add half a box width for each level of nesting
257         if (active_$1 > 1) then {
258                 off_from = off_from + (active_$1 - 1) * awid/2;
259         }
261         # add half a box width for each level of nesting
262         if (active_$2 > 1) then {
263                 off_to = off_to + (active_$2 - 1) * awid/2;
264         }
266         arrow from  ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
269 define rmessage {
270         return_message($1,$2,$3);
273 # Object becomes active
274 # Can be nested to show recursion
275 define active {
276         extend_lifeline($1);
277         # draw top of new active box
278         line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
279         active_$1 = active_$1 + 1;
282 # Object becomes inactive
283 # Can be nested to show recursion
284 define inactive {
285         extend_lifeline($1);
286         active_$1 = active_$1 - 1;
287         # draw bottom of innermost active box
288         line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
291 # Time step
292 # Useful at the beginning and the end
293 # to show object states
294 define step {
295         down;
296         move spacing;
299 # Switch to asynchronous messages
300 define async {
301         arrowhead = 0;
302         arrowwid = arrowwid * 2;
305 # Switch to synchronous messages
306 define sync {
307         arrowhead = 1;
308         arrowwid = arrowwid / 2;
311 # same as lifeline_constraint, but Text and empty string are exchanged.
312 define lconstraint_below{
313         off_from = awid;
314         # add half a box width for each level of nesting
315         if (active_$1 > 1) then {
316                 off_from = off_from + (active_$1 - 1) * awid/2;
317         }
319         box at ($1.x + off_from, Here.y) invis "" $2 ljust;
322 # begin_frame(left_object,name,label_text);
323 define begin_frame {
324         # The lifeline will be cut here
325         extend_lifeline($1);
326         # draw the frame-label
327         $2: box $3 invis with .n at ($1.x, Here.y);
328         d = $2.e.y - $2.se.y;
329         line from $2.ne to $2.e then down d left d then to $2.sw;
330         # continue the lifeline below the frame-label
331         move to $2.s;
332         lifestart_$1 = Here.y;
335 # end_frame(right_object,name);
336 define end_frame {
337         # dummy-box for the lower right corner:
338         box invis "" with .s at ($1.x, Here.y);
339         # draw the frame
340         frame_wid = last box.se.x - $2.nw.x
341         frame_ht = - last box.se.y + $2.nw.y
342         box with .nw at $2.nw wid frame_wid ht frame_ht;
343         # restore Here.y
344         move to last box.s;
347 # comment(object,[name],[line_movement], [box_size] text);
348 define comment {
349         old_y = Here.y
350         # draw the first connecting line, at which's end the box wil be positioned
351         move to ($1.x, Here.y)
352         if "$3" == "" then {
353                 line comment_default_move() dashed;
354         } else {
355                 line $3 dashed;
356         }
358         # draw the box, use comment_default_xx if no explicit
359         # size is given together with the text in parameter 4
360         old_boxht=boxht;
361         old_boxwid=boxwid;
362         boxht=comment_default_ht;
363         boxwid=comment_default_wid;
364         if "$2" == "" then {
365                 box invis $4;
366         } else {
367                 $2: box invis $4;
368         }
369         boxht=old_boxht;
370         boxwid=old_boxwid;
372         # draw the frame of the comment
373         line from       last box.nw \
374                 to          last box.ne - (corner_fold, 0) \
375                 then to last box.ne - (0, corner_fold) \
376                 then to last box.se \
377                 then to last box.sw \
378                 then to last box.nw ;
379         line from       last box.ne - (corner_fold, 0) \
380                 to          last box.ne - (corner_fold, corner_fold) \
381                 then to last box.ne - (0, corner_fold) ;
383         # restore Here.y
384         move to ($1.x, old_y)
387 # connect_to_comment(object,name);
388 define connect_to_comment {
389         old_y = Here.y
390         # start at the object
391         move to ($1.x, Here.y)
392         # find the best connection-point of the comment to use as line-end
393         if $1.x < $2.w.x then {
394                 line to $2.w dashed;
395         } else {
396                 if $1.x > $2.e.x then {
397                         line to $2.e dashed;
398                 } else {
399                         if Here.y < $2.s.y then {
400                                 line to $2.s dashed;
401                         } else {
402                                 if Here.y > $2.n.y then {
403                                         line to $2.n dashed;
404                                 }
405                         }
406                 }
407         }
408         # restore Here.y
409         move to ($1.x, old_y)