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