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.
21 # Default parameters (can be redefined)
23 # Spacing between messages
35 define comment_default_move {up 0.25 right 0.25};
37 comment_default_ht=0.5;
39 comment_default_wid=1;
40 # Underline object name
43 # Create a new object(name,label)
46 # Could also underline text with \mk\ul\ul\ul...\rt
48 line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
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
57 lifestart_$1 = $1.s.y;
60 # Create a new external actor(name,label)
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;
73 lifestart_$1 = $1.s.y - .05;
76 # Create a new placeholder object(name)
77 define placeholder_object {
83 lifestart_$1 = $1.s.y;
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;
99 # draw the right edge of the innermost box
101 line from (Here.x, lifestart_$1) to Here;
103 line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
105 lifestart_$1 = Here.y;
109 # Complete the lifeline of the object with the given name
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);
118 # Draw a message(from_object,to_object,label)
122 # Adjust so that lines and arrows do not fall into the
123 # active box. Should be .5, but the arrow heads tend to
125 if ($1.x <= $2.x) then {
126 off_from = awid * .6;
129 off_from = -awid * .6;
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;
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;
143 if ($1.x == $2.x) then {
144 arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " " " " ;
146 arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
150 # Display a lifeline constraint(object,label)
151 define lifeline_constraint {
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;
158 box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
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; }
172 object_constraint($1);
175 # Draw a creation message(from_object,to_object,object_label)
176 define create_message {
179 if ($1.x <= $2.x) then {
180 off_from = awid * .6;
181 off_to = -boxwid * .51;
183 off_from = -awid * .6;
184 off_to = boxwid * .51;
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;
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; }
197 { XSEQB: box $3 with .e at XSEQA.end; }
200 line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
202 lifestart_$2 = XSEQB.s.y;
203 move (spacing + boxht) / 2;
207 create_message($1,$2,$3);
210 # Draw an X for a given object
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);
218 # Draw a destroy message(from_object,to_object)
219 define destroy_message {
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»");
233 destroy_message($1,$2);
236 # An object deletes itself: delete(object)
239 lifestart_$1 = lifestart_$1 - awid;
243 # Draw a message return(from_object,to_object,label)
244 define return_message {
247 # See comment in message
248 if ($1.x <= $2.x) then {
249 off_from = awid * .6;
252 off_from = -awid * .6;
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;
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;
266 arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
270 return_message($1,$2,$3);
273 # Object becomes active
274 # Can be nested to show recursion
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
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);
292 # Useful at the beginning and the end
293 # to show object states
299 # Switch to asynchronous messages
302 arrowwid = arrowwid * 2;
305 # Switch to synchronous messages
308 arrowwid = arrowwid / 2;
311 # same as lifeline_constraint, but Text and empty string are exchanged.
312 define lconstraint_below{
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;
319 box at ($1.x + off_from, Here.y) invis "" $2 ljust;
322 # begin_frame(left_object,name,label_text);
324 # The lifeline will be cut here
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
332 lifestart_$1 = Here.y;
335 # end_frame(right_object,name);
337 # dummy-box for the lower right corner:
338 box invis "" with .s at ($1.x, Here.y);
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;
347 # comment(object,[name],[line_movement], [box_size] text);
350 # draw the first connecting line, at which's end the box wil be positioned
351 move to ($1.x, Here.y)
353 line comment_default_move() dashed;
358 # draw the box, use comment_default_xx if no explicit
359 # size is given together with the text in parameter 4
362 boxht=comment_default_ht;
363 boxwid=comment_default_wid;
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) ;
384 move to ($1.x, old_y)
387 # connect_to_comment(object,name);
388 define connect_to_comment {
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 {
396 if $1.x > $2.e.x then {
399 if Here.y < $2.s.y then {
402 if Here.y > $2.n.y then {
409 move to ($1.x, old_y)