mirror of https://github.com/dspinellis/UMLGraph
408 lines
9.6 KiB
Plaintext
408 lines
9.6 KiB
Plaintext
#/usr/bin/pic2plot -Tps
|
||
#
|
||
# Pic macros for drawing UML sequence diagrams
|
||
#
|
||
# (C) Copyright 2004-2005 Diomidis Spinellis.
|
||
#
|
||
# Permission to use, copy, and distribute this software and its
|
||
# documentation for any purpose and without fee is hereby granted,
|
||
# provided that the above copyright notice appear in all copies and that
|
||
# both that copyright notice and this permission notice appear in
|
||
# supporting documentation.
|
||
#
|
||
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||
#
|
||
# $Id$
|
||
#
|
||
|
||
|
||
# Default parameters (can be redefined)
|
||
|
||
# Spacing between messages
|
||
spacing = 0.25;
|
||
# Active box width
|
||
awid = .1;
|
||
# Box height
|
||
boxht = 0.3;
|
||
# Commend folding
|
||
corner_fold=awid
|
||
# Comment distance
|
||
define comment_default_move {up 0.25 right 0.25};
|
||
# Comment height
|
||
comment_default_ht=0.5;
|
||
# Comment width
|
||
comment_default_wid=1;
|
||
# Underline object name
|
||
underline=1;
|
||
|
||
# Create a new object(name,label)
|
||
define object {
|
||
$1: box $2; move;
|
||
# Could also underline text with \mk\ul\ul\ul...\rt
|
||
if (underline) then {
|
||
line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
|
||
}
|
||
move to $1.e;
|
||
move right;
|
||
# Active is the level of activations of the object
|
||
# 0 : inactive : draw thin line swimlane
|
||
# 1 : active : draw thick swimlane
|
||
# > 1: nested : draw nested swimlane
|
||
active_$1 = 0;
|
||
lifestart_$1 = $1.s.y;
|
||
}
|
||
|
||
# Create a new external actor(name,label)
|
||
define actor {
|
||
$1: [
|
||
XSEQC: circle rad 0.06;
|
||
XSEQL: line from XSEQC.s down .12;
|
||
line from XSEQL.start - (.15,.02) to XSEQL.start + (.15,-.02);
|
||
XSEQL1: line from XSEQL.end left .08 down .15;
|
||
XSEQL2: line from XSEQL.end right .08 down .15;
|
||
line at XSEQC.n invis "" "" "" $2;
|
||
]
|
||
move to $1.e;
|
||
move right;
|
||
active_$1 = 0;
|
||
lifestart_$1 = $1.s.y - .05;
|
||
}
|
||
|
||
# Create a new placeholder object(name)
|
||
define placeholder_object {
|
||
$1: box invisible;
|
||
move;
|
||
move to $1.e;
|
||
move right;
|
||
active_$1 = 0;
|
||
lifestart_$1 = $1.s.y;
|
||
}
|
||
|
||
define pobject {
|
||
placeholder_object($1);
|
||
}
|
||
|
||
define extend_lifeline {
|
||
if (active_$1 > 0) then {
|
||
# draw the left edges of the boxes
|
||
move to ($1.x - awid/2, Here.y);
|
||
for level = 1 to active_$1 do {
|
||
line from (Here.x, lifestart_$1) to Here;
|
||
move right awid/2
|
||
}
|
||
|
||
# draw the right edge of the innermost box
|
||
move right awid/2;
|
||
line from (Here.x, lifestart_$1) to Here;
|
||
} else {
|
||
line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
|
||
}
|
||
lifestart_$1 = Here.y;
|
||
}
|
||
|
||
# complete(name)
|
||
# Complete the lifeline of the object with the given name
|
||
define complete {
|
||
extend_lifeline($1)
|
||
if (active_$1) then {
|
||
# draw bottom of all active boxes
|
||
line right ((active_$1 + 1) * awid/2) from ($1.x - awid/2, Here.y);
|
||
}
|
||
}
|
||
|
||
# Draw a message(from_object,to_object,label)
|
||
define message {
|
||
down;
|
||
move spacing;
|
||
# Adjust so that lines and arrows do not fall into the
|
||
# active box. Should be .5, but the arrow heads tend to
|
||
# overshoot.
|
||
if ($1.x <= $2.x) then {
|
||
off_from = awid * .6;
|
||
off_to = -awid * .6;
|
||
} else {
|
||
off_from = -awid * .6;
|
||
off_to = awid * .6;
|
||
}
|
||
|
||
# add half a box width for each level of nesting
|
||
if (active_$1 > 1) then {
|
||
off_from = off_from + (active_$1 - 1) * awid/2;
|
||
}
|
||
|
||
# add half a box width for each level of nesting
|
||
if (active_$2 > 1) then {
|
||
off_to = off_to + (active_$2 - 1) * awid/2;
|
||
}
|
||
|
||
if ($1.x == $2.x) then {
|
||
arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " " " " ;
|
||
} else {
|
||
arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
|
||
}
|
||
}
|
||
|
||
# Display a lifeline constraint(object,label)
|
||
define lifeline_constraint {
|
||
off_from = awid;
|
||
# add half a box width for each level of nesting
|
||
if (active_$1 > 1) then {
|
||
off_from = off_from + (active_$1 - 1) * awid/2;
|
||
}
|
||
|
||
box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
|
||
}
|
||
|
||
define lconstraint {
|
||
lifeline_constraint($1,$2);
|
||
}
|
||
|
||
# Display an object constraint(label)
|
||
# for the last object drawn
|
||
define object_constraint {
|
||
{ box invis with .s at last box .nw $1 ljust; }
|
||
}
|
||
|
||
define oconstraint {
|
||
object_constraint($1);
|
||
}
|
||
|
||
# Draw a creation message(from_object,to_object,object_label)
|
||
define create_message {
|
||
down;
|
||
move spacing;
|
||
if ($1.x <= $2.x) then {
|
||
off_from = awid * .6;
|
||
off_to = -boxwid * .51;
|
||
} else {
|
||
off_from = -awid * .6;
|
||
off_to = boxwid * .51;
|
||
}
|
||
|
||
# add half a box width for each level of nesting
|
||
if (active_$1 > 1) then {
|
||
off_from = off_from + (active_$1 - 1) * awid/2;
|
||
}
|
||
|
||
# See comment in destroy_message
|
||
XSEQA: arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) "<22>create<74>" " ";
|
||
if ($1.x <= $2.x) then {
|
||
{ XSEQB: box $3 with .w at XSEQA.end; }
|
||
} else {
|
||
{ XSEQB: box $3 with .e at XSEQA.end; }
|
||
}
|
||
{
|
||
line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
|
||
}
|
||
lifestart_$2 = XSEQB.s.y;
|
||
move (spacing + boxht) / 2;
|
||
}
|
||
|
||
define cmessage {
|
||
create_message($1,$2,$3);
|
||
}
|
||
|
||
# Draw an X for a given object
|
||
define drawx {
|
||
{
|
||
line from($1.x - awid, lifestart_$1 - awid) to ($1.x + awid, lifestart_$1 + awid);
|
||
line from($1.x - awid, lifestart_$1 + awid) to ($1.x + awid, lifestart_$1 - awid);
|
||
}
|
||
}
|
||
|
||
# Draw a destroy message(from_object,to_object)
|
||
define destroy_message {
|
||
down;
|
||
move spacing;
|
||
# The troff code is \(Fo \(Fc
|
||
# The groff code is also \[Fo] \[Fc]
|
||
# The pic2plot code is \Fo \Fc
|
||
# See http://www.delorie.com/gnu/docs/plotutils/plotutils_71.html
|
||
# To stay compatible with all we have to hardcode the characters
|
||
message($1,$2,"<22>destroy<6F>");
|
||
complete($2);
|
||
drawx($2);
|
||
}
|
||
|
||
define dmessage {
|
||
destroy_message($1,$2);
|
||
}
|
||
|
||
# An object deletes itself: delete(object)
|
||
define delete {
|
||
complete($1);
|
||
lifestart_$1 = lifestart_$1 - awid;
|
||
drawx($1);
|
||
}
|
||
|
||
# Draw a message return(from_object,to_object,label)
|
||
define return_message {
|
||
down;
|
||
move spacing;
|
||
# See comment in message
|
||
if ($1.x <= $2.x) then {
|
||
off_from = awid * .6;
|
||
off_to = -awid * .6;
|
||
} else {
|
||
off_from = -awid * .6;
|
||
off_to = awid * .6;
|
||
}
|
||
|
||
# add half a box width for each level of nesting
|
||
if (active_$1 > 1) then {
|
||
off_from = off_from + (active_$1 - 1) * awid/2;
|
||
}
|
||
|
||
# add half a box width for each level of nesting
|
||
if (active_$2 > 1) then {
|
||
off_to = off_to + (active_$2 - 1) * awid/2;
|
||
}
|
||
|
||
arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
|
||
}
|
||
|
||
define rmessage {
|
||
return_message($1,$2,$3);
|
||
}
|
||
|
||
# Object becomes active
|
||
# Can be nested to show recursion
|
||
define active {
|
||
extend_lifeline($1);
|
||
# draw top of new active box
|
||
line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
|
||
active_$1 = active_$1 + 1;
|
||
}
|
||
|
||
# Object becomes inactive
|
||
# Can be nested to show recursion
|
||
define inactive {
|
||
extend_lifeline($1);
|
||
active_$1 = active_$1 - 1;
|
||
# draw bottom of innermost active box
|
||
line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
|
||
}
|
||
|
||
# Time step
|
||
# Useful at the beginning and the end
|
||
# to show object states
|
||
define step {
|
||
down;
|
||
move spacing;
|
||
}
|
||
|
||
# Switch to asynchronous messages
|
||
define async {
|
||
arrowhead = 0;
|
||
arrowwid = arrowwid * 2;
|
||
}
|
||
|
||
# Switch to synchronous messages
|
||
define sync {
|
||
arrowhead = 1;
|
||
arrowwid = arrowwid / 2;
|
||
}
|
||
|
||
# same as lifeline_constraint, but Text and empty string are exchanged.
|
||
define lconstraint_below{
|
||
off_from = awid;
|
||
# add half a box width for each level of nesting
|
||
if (active_$1 > 1) then {
|
||
off_from = off_from + (active_$1 - 1) * awid/2;
|
||
}
|
||
|
||
box at ($1.x + off_from, Here.y) invis "" $2 ljust;
|
||
}
|
||
|
||
# begin_frame(left_object,name,label_text);
|
||
define begin_frame {
|
||
# The lifeline will be cut here
|
||
extend_lifeline($1);
|
||
# draw the frame-label
|
||
$2: box $3 invis with .n at ($1.x, Here.y);
|
||
d = $2.e.y - $2.se.y;
|
||
line from $2.ne to $2.e then down d left d then to $2.sw;
|
||
# continue the lifeline below the frame-label
|
||
move to $2.s;
|
||
lifestart_$1 = Here.y;
|
||
}
|
||
|
||
# end_frame(right_object,name);
|
||
define end_frame {
|
||
# dummy-box for the lower right corner:
|
||
box invis "" with .s at ($1.x, Here.y);
|
||
# draw the frame
|
||
frame_wid = last box.se.x - $2.nw.x
|
||
frame_ht = - last box.se.y + $2.nw.y
|
||
box with .nw at $2.nw wid frame_wid ht frame_ht;
|
||
# restore Here.y
|
||
move to last box.s;
|
||
}
|
||
|
||
# comment(object,[name],[line_movement], [box_size] text);
|
||
define comment {
|
||
old_y = Here.y
|
||
# draw the first connecting line, at which's end the box wil be positioned
|
||
move to ($1.x, Here.y)
|
||
if "$3" == "" then {
|
||
line comment_default_move() dashed;
|
||
} else {
|
||
line $3 dashed;
|
||
}
|
||
|
||
# draw the box, use comment_default_xx if no explicit
|
||
# size is given together with the text in parameter 4
|
||
old_boxht=boxht;
|
||
old_boxwid=boxwid;
|
||
boxht=comment_default_ht;
|
||
boxwid=comment_default_wid;
|
||
if "$2" == "" then {
|
||
box invis $4;
|
||
} else {
|
||
$2: box invis $4;
|
||
}
|
||
boxht=old_boxht;
|
||
boxwid=old_boxwid;
|
||
|
||
# draw the frame of the comment
|
||
line from last box.nw \
|
||
to last box.ne - (corner_fold, 0) \
|
||
then to last box.ne - (0, corner_fold) \
|
||
then to last box.se \
|
||
then to last box.sw \
|
||
then to last box.nw ;
|
||
line from last box.ne - (corner_fold, 0) \
|
||
to last box.ne - (corner_fold, corner_fold) \
|
||
then to last box.ne - (0, corner_fold) ;
|
||
|
||
# restore Here.y
|
||
move to ($1.x, old_y)
|
||
}
|
||
|
||
# connect_to_comment(object,name);
|
||
define connect_to_comment {
|
||
old_y = Here.y
|
||
# start at the object
|
||
move to ($1.x, Here.y)
|
||
# find the best connection-point of the comment to use as line-end
|
||
if $1.x < $2.w.x then {
|
||
line to $2.w dashed;
|
||
} else {
|
||
if $1.x > $2.e.x then {
|
||
line to $2.e dashed;
|
||
} else {
|
||
if Here.y < $2.s.y then {
|
||
line to $2.s dashed;
|
||
} else {
|
||
if Here.y > $2.n.y then {
|
||
line to $2.n dashed;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
# restore Here.y
|
||
move to ($1.x, old_y)
|
||
}
|