markdoc 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 32babb3c9102691be23dbfe29f9ecff5eea66e04
4
+ data.tar.gz: af86bce269a2efd78fe16473d47231fc0686904f
5
+ SHA512:
6
+ metadata.gz: dd656fd8911787e57b6093abe07cdef482e3fe31e9675782d4438509517981a23376920355a601d3c89b95b438185a23d67645e90c5bfdcc5fbe4aa81bb8893d
7
+ data.tar.gz: 76329a964b16c93cde6b014ccb2c2d68d28ed78adf02dc4f8aa0a01847d0effbcf6d4c02bdaffa4ab48c86181130cebc5cf9b5be810bc69619d379de461e4713
@@ -1,211 +1,323 @@
1
1
  module Markdoc
2
2
  module Sequence
3
- def self.draw(code, format = :svg)
4
- parser = Parser.new(code)
3
+ DEFAULTS = {
4
+ diagram: {
5
+ offsetx: 10,
6
+ offsety: 10,
7
+ width: 900,
8
+ height: 600
9
+ },
10
+ role: {
11
+ font: "'Roboto Condensed', sans-serif",
12
+ border: '#3c4260',
13
+ fill: '#dcd7d7',
14
+ radius: 2,
15
+ spacing: 100,
16
+ width: 100,
17
+ height: 55,
18
+ line: 3,
19
+ },
20
+ message: {
21
+ color: '#3c4260',
22
+ font: "'Roboto Condensed', sans-serif",
23
+ size: 11,
24
+ spacing: 40,
25
+ offset: 100, # from top
26
+ line: 3,
27
+ dash: '4,2'
28
+ }
29
+ }
5
30
 
6
- digest = Digest::MD5.hexdigest code
31
+ def self.draw(code)
32
+ diagram = Diagram.new(code)
33
+ diagram.parse
34
+ diagram.print
35
+ end
36
+
37
+ class Role
38
+ include Comparable
39
+
40
+ attr_accessor :id, :label, :messages, :column,
41
+ :offsetx, :offsety, :border, :fill, :radius, :spacing, :width, :height, :line, :font,
42
+ :prev, :succ
7
43
 
8
- pic = nil
9
- Tempfile.open([digest, '.pic']) do |file|
10
- file.write parser.parse
11
- pic = file.path
44
+ def initialize(args)
45
+ self.messages = []
46
+ self.id = args[:id].strip
47
+ self.label = args[:label].strip
48
+
49
+ # ui settings
50
+ self.column = args[:column]
51
+ self.offsetx = args[:diagram][:offsetx]
52
+ self.offsety = args[:diagram][:offsety]
53
+ self.border = args[:ui][:border]
54
+ self.fill = args[:ui][:fill]
55
+ self.radius = args[:ui][:radius]
56
+ self.spacing = args[:ui][:spacing]
57
+ self.width = args[:ui][:width]
58
+ self.height = args[:ui][:height]
59
+ self.line = args[:ui][:line]
60
+ self.font = args[:ui][:font]
12
61
  end
13
62
 
14
- if format == :pic
15
- return IO.read(pic)
63
+ def <=> o
64
+ column <=> o.column
16
65
  end
17
66
 
18
- image = Tempfile.new([digest, ".#{format}"])
19
- image.close
67
+ def type
68
+ case label
69
+ when /actor/i
70
+ :actor
71
+ when /database/i
72
+ :database
73
+ when /site|web/i
74
+ :website
75
+ when /application|system/i
76
+ :system
77
+ else
78
+ :component
79
+ end
80
+ end
20
81
 
21
- if system("pic2plot -T#{format} #{pic} > #{image.path}")
22
- IO.read image
23
- else
24
- raise "Can't generate sequence diagram"
82
+ def center
83
+ x + width/2
25
84
  end
26
- end
27
85
 
28
- class Role
29
- attr_accessor :type, :id, :label, :active
30
- def initialize(type, id, label)
31
- self.type, self.id, self.label = type, id, label
32
- self.active = false
86
+ def x
87
+ offsetx + column*(width + spacing)
33
88
  end
34
- def activate
35
- return if active
36
- self.active = true
37
- "active(#{id});"
89
+
90
+ def y
91
+ offsety
38
92
  end
39
- def deactivate
40
- return unless active
41
- self.active = false
42
- "inactive(#{id});"
93
+
94
+ def print
95
+ elements = []
96
+ case type
97
+ when :actor
98
+ elements << %Q[<g transform="translate(#{x+10},0)"><path d="M74,64 a30,30 0 0,0 -27,-27 a16,18 0 1,0 -16,0 a30,30 0, 0,0 -27,27 z" stroke-width="#{line}" fill="#{fill}" stroke="#{border}"/></g>]
99
+ elements << %Q[<text x="#{x+46-2*id.size}" y="#{y+height-5}" font-family="#{font}" font-size="12" fill="#{border}">#{id}</text>]
100
+ else
101
+ elements << %Q[<rect fill="#{fill}" stroke="#{border}" rx="#{radius}" ry="#{radius}" x="#{x}" y="#{y}" width="#{width}" height="#{height}" stroke-width="#{line}"/>]
102
+ elements << %Q[<text x="#{x+10}" y="#{y+20}" font-family="#{font}" font-size="12" fill="#{border}">#{label}</text>]
103
+ end
104
+
105
+ x1 = center
106
+ y1 = offsety + height
107
+ x2 = center
108
+ y2 = messages.last.y + 10
109
+ elements << %Q[<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{border}" stroke-width="#{line}"/>]
110
+
111
+ elements.join("\n")
43
112
  end
44
113
  end
45
114
 
46
115
  class Message
47
- attr_accessor :type, :source, :dest, :label, :comment
48
- def initialize(type, source, dest, label, comment)
49
- self.type, self.source, self.dest, self.label, self.comment = type, source, dest, label.strip, comment.strip
50
- end
116
+ attr_accessor :source, :dest, :op, :label, :comment, :row,
117
+ :offset, :color, :font, :size, :spacing, :line, :dash,
118
+ :options
119
+
120
+ def initialize(args)
121
+ self.options = []
122
+ self.label = args[:label].strip
123
+ self.comment = args[:comment].strip
124
+
125
+ self.op = args[:op]
126
+ self.row = args[:row]
127
+ # ui
128
+ self.offset = args[:ui][:offset]
129
+ self.color = args[:ui][:color]
130
+ self.font = args[:ui][:font]
131
+ self.size = args[:ui][:size]
132
+ self.spacing = args[:ui][:spacing]
133
+ self.line = args[:ui][:line]
134
+ self.dash = args[:ui][:dash]
135
+
136
+ if op.index('~')
137
+ options << %Q[stroke-dasharray="#{dash}"]
138
+ elsif op.index('-').nil?
139
+ raise "Message direction must be one of ->, ~>, <-, <~"
140
+ end
141
+
142
+ if op.index('>')
143
+ self.source, self.dest = args[:role1], args[:role2]
144
+ elsif op.index('<')
145
+ self.source, self.dest = args[:role2], args[:role1]
146
+ else
147
+ raise "Message direction must be one of ->, ~>, <-, <~"
148
+ end
51
149
 
52
- def roles
53
- [source, dest]
150
+ source.messages << self
151
+ dest.messages << self unless source.eql?(dest)
54
152
  end
55
153
 
56
- def deliver
57
- %Q[#{type}(#{source.id},#{dest.id}, "#{label}");]
154
+ def print
155
+ role1, role2 = *(source < dest ? [source, dest] : [dest, source])
156
+ elements = []
157
+
158
+ if role1.eql?(role2)
159
+ x1 = role1.center
160
+ y1 = y
161
+ x2 = x1 + 50
162
+ y2 = y1 + spacing
163
+
164
+ elements << %Q(<polyline points="#{x1},#{y1} #{x2},#{y1} #{x2},#{y2} #{x1+5},#{y2}" fill="none" stroke-width="2" stroke-linejoin="round" stroke="#{color}" #{options.join ' '}/>)
165
+ elements << %Q(<polygon points="#{x1+10},#{y2-5} #{x1},#{y2} #{x1+10},#{y2+5}" fill="#{color}"/>)
166
+ elements << %Q(<text x="#{x1+10}" y="#{y1-5}" font-family="#{font}" font-size="#{size}" fill="#{color}">#{label}</text>)
167
+ else
168
+ x1 = role1.center
169
+ x2 = role2.center
170
+
171
+ if role1 == source
172
+ x2 -= 10
173
+ elements << %Q(<polygon points="#{x2},#{y-5} #{x2+10},#{y} #{x2},#{y+5}" fill="#{color}"/>)
174
+ else
175
+ x1 += 10
176
+ elements << %Q(<polygon points="#{x1},#{y-5} #{x1-10},#{y} #{x1},#{y+5}" fill="#{color}"/>)
177
+ end
178
+
179
+ elements << %Q(<line x1="#{x1}" y1="#{y}" x2="#{x2}" y2="#{y}" stroke="#{color}" stroke-width="2" #{options.join ' '}/>)
180
+ elements << %Q(<text x="#{x1+40}" y="#{y-5}" font-family="#{font}" font-size="#{size}" fill="#{color}">#{label}</text>)
181
+
182
+ if comment.size > 0
183
+ x = role2.prev.center + 15
184
+ elements << %Q(<path fill="#eeeeee" d="M#{x2-30},#{y+1} L#{x2-30},#{y+10} H#{x} V#{y+2*spacing - 25} H#{x2} V#{y+10} H#{x2-20} z" />)
185
+ elements << %Q(<text x="#{x+5}" y="#{y+23}" font-family="#{font}" font-size="#{size}" fill="#{color}">)
186
+ split(comment).each_with_index do |line, i|
187
+ elements << %Q(<tspan x="#{x+5}" y="#{y+23+13*i}">#{line}</tspan>)
188
+ end
189
+ elements << '</text>'
190
+ end
191
+ end
192
+
193
+ elements.join("\n")
58
194
  end
59
195
 
60
- def describe
61
- return if comment.empty?
62
- width = comment.length > 10 ? 1 : comment.length * 0.13
63
- width = 0.5 if width < 0.5
64
- %Q[comment(#{dest.id},C,up 0.2 right, wid #{'%.1f' % width} ht 0.3 "#{comment}");]
196
+ def y
197
+ offset + row*spacing
65
198
  end
66
199
 
67
- def height
68
- source == dest ? 2 : 1
200
+ private
201
+
202
+ def split(text, max = 35)
203
+ ary = []
204
+ line = ''
205
+ text.split.each do |word|
206
+ if (line + word).length < max
207
+ line << ' ' << word
208
+ else
209
+ ary << line
210
+ line = word
211
+ end
212
+ end
213
+ ary << line if line.length > 0
214
+ ary
69
215
  end
216
+
70
217
  end
71
218
 
72
- class Parser
73
- attr_accessor :source, :variables, :roles, :messages, :output
74
-
75
- def defaults
76
- {
77
- boxht: '0.5', # Object box height
78
- boxwid: '1.3', # Object box width
79
- awid: '0.1', # Active lifeline width
80
- spacing: '0.25', # Spacing between messages
81
- movewid: '0.75', # Spacing between objects
82
- dashwid: '0.05', # Interval for dashed lines
83
- maxpswid: '20', # Maximum width of picture
84
- maxpsht: '20', # Maximum height of picture
85
- underline: '0', # Underline the name of objects
86
- }
87
- end
219
+ class Diagram
220
+ attr_accessor :input, :output, :attributes, :roles, :messages, :rows
88
221
 
89
- def initialize(source, options = {})
90
- self.source = source
222
+ def initialize(input, options = {})
223
+ self.input = input
91
224
  self.output = []
92
225
  self.roles = []
93
226
  self.messages = []
94
- self.variables = defaults.dup
227
+ self.rows = 0
228
+ self.attributes = DEFAULTS.dup
229
+
95
230
  options.each do |key, value|
96
- variables[key] = value
231
+ attributes.merge!(key => value)
97
232
  end
98
233
  end
99
234
 
100
235
  def find(id)
101
- id.strip!
102
- roles.detect{|role| role.id == id}
236
+ roles.detect{|role| role.id == id.strip} or raise("Non-declared role: #{id}")
103
237
  end
104
238
 
105
239
  def parse
106
- source.split("\n").each do |line|
240
+ input.split("\n").each do |line|
107
241
  next if line.strip.empty?
108
- if match = line.match(/^([a-zA-Z0-9_ \t]+) *= *([a-zA-Z0-9_ \t]+)/)
109
- if match[2] =~ /Actor/
110
- roles << Role.new(:actor, match[1].strip, match[1].strip)
111
- else
112
- roles << Role.new(:object, match[1].strip, match[2].strip)
113
- end
114
- elsif match = line.match(/^([a-zA-Z0-9_ \t]+) *([<\-~>]{2}) *([a-zA-Z0-9_ \t]+):([^#]+)#?(.*)/)
115
- role1, role2, op = find(match[1]), find(match[3]), match[2]
116
-
117
- if op.index('>')
118
- source, dest = role1, role2
119
- elsif op.index('<')
120
- source, dest = role2, role1
121
- else
122
- raise "Message direction must be one of ->, ~>, <-, <~"
123
- end
124
242
 
125
- if op.index('-')
126
- type = :message
127
- elsif op.index('~')
128
- type = :rmessage
129
- else
130
- raise "Message direction must be one of ->, ~>, <-, <~"
131
- end
132
-
133
- message = Message.new(type, source, dest, match[4], match[5])
134
- # deactivate prev messages roles
135
- if last = messages.last
136
- (last.roles - message.roles).each do |role|
137
- output << role.deactivate if role.active
138
- end
139
- end
140
- # activate source before send message
141
- output << source.activate unless source.active
142
- output << message.deliver
143
- output << message.describe
144
- # activate dest
145
- output << dest.activate unless dest.active
146
- messages << message
147
- elsif match = line.match(/^([a-zA-Z0-9_ \t]+) *:([^#]+)#?(.*)/)
148
- role = find(match[1])
149
- message = Message.new(:message, role, role, match[2], match[3])
150
- # deactivate prev messages roles
151
- if last = messages.last
152
- (last.roles - message.roles).each do |role|
153
- output << role.deactivate if role.active
154
- end
155
- end
156
- # activate role before send message
157
- output << role.activate unless role.active
158
- output << message.deliver
159
- output << message.describe
160
- messages << message
243
+ if matches = line.match(/^(?<id>[a-zA-Z0-9_ \t]+) *= *(?<label>[a-zA-Z0-9_ \t]+)/)
244
+ # User = Actor
245
+ roles << Role.new(
246
+ id: matches[:id],
247
+ label: matches[:label],
248
+ column: roles.size,
249
+ diagram: attributes[:diagram],
250
+ ui: attributes[:role]
251
+ )
252
+ elsif matches = line.match(/^(?<role1>[a-zA-Z0-9_ \t]+) *(?<op>[<\-~>]{2}) *(?<role2>[a-zA-Z0-9_ \t]+):(?<label>[^#]+)#?(?<comment>.*)/)
253
+ # User -> Web : Login
254
+ messages << Message.new(
255
+ role1: find(matches[:role1]),
256
+ role2: find(matches[:role2]),
257
+ row: rows,
258
+ op: matches[:op],
259
+ label: matches[:label],
260
+ comment: matches[:comment],
261
+ diagram: attributes[:diagram],
262
+ ui: attributes[:message]
263
+ )
264
+ self.rows += 1
265
+ # comment takes 1 more row
266
+ self.rows += 1 if matches[:comment].length > 0
267
+ elsif matches = line.match(/^(?<role>[a-zA-Z0-9_ \t]+) *:(?<label>[^#]+)#?(?<comment>.*)/)
268
+ # Web : Save the form
269
+ messages << Message.new(
270
+ role1: find(matches[:role]),
271
+ role2: find(matches[:role]),
272
+ row: rows,
273
+ op: '->',
274
+ label: matches[:label],
275
+ comment: matches[:comment],
276
+ diagram: attributes[:diagram],
277
+ ui: attributes[:message]
278
+ )
279
+ # self message takes 2 rows
280
+ self.rows += 2
161
281
  end
162
282
  end
163
283
 
164
- header
165
- footer
284
+ prev = nil
285
+ roles.each do |succ|
286
+ succ.prev = prev
287
+ prev.succ = succ if prev
288
+ prev = succ
289
+ end
290
+ end
166
291
 
167
- output.compact.join("\n")
292
+ def print
293
+ template % {
294
+ width: width,
295
+ height: height,
296
+ content: (roles + messages).map(&:print).join("\n")
297
+ }
168
298
  end
169
299
 
170
- def macros
171
- IO.read File.join(File.dirname(__FILE__), 'sequence.pic')
300
+ def height
301
+ attributes[:message][:offset] +
302
+ attributes[:message][:spacing] * rows +
303
+ attributes[:diagram][:offsety] * 2
172
304
  end
173
305
 
174
- def header
175
- headers = []
176
- headers << '.PS'
177
- headers << ''
178
- headers << macros
179
- headers << ''
180
- headers << '# Variables'
181
- # calculate height
306
+ def width
307
+ (attributes[:role][:spacing] + attributes[:role][:width]) * roles.size +
308
+ attributes[:diagram][:offsetx]
309
+ end
182
310
 
183
- variables[:maxpsht] = ((variables[:spacing].to_f *
184
- messages.map(&:height).reduce(:+)) +
185
- variables[:boxht].to_f).ceil
311
+ private
186
312
 
187
- variables.each do |key, value|
188
- headers << "#{key} = #{value};"
189
- end
190
- headers << '# Actors and Objects'
191
- roles.each do |object|
192
- headers << %Q[#{object.type}(#{object.id},"#{object.label}");]
193
- end
194
- headers << 'step();'
195
- headers << ''
196
- self.output = headers + output
313
+ def template
314
+ '<svg xmlns="http://www.w3.org/2000/svg" '+
315
+ 'xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" '+
316
+ 'width="%{width}" height="%{height}" '+
317
+ 'viewBox="0 0 %{width} %{height}">'+
318
+ '%{content}</svg>'
197
319
  end
198
320
 
199
- def footer
200
- output << ''
201
- output << 'step();'
202
- output << '# Complete the lifelines'
203
- roles.each do |object|
204
- output << "complete(#{object.id});"
205
- end
206
- output << ''
207
- output << '.PE'
208
- end
209
321
  end
210
322
  end
211
323
  end
@@ -1,3 +1,3 @@
1
1
  module Markdoc
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end
metadata CHANGED
@@ -1,80 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Lkhagva Ochirkhuyag
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-04-10 00:00:00.000000000 Z
11
+ date: 2017-11-24 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: polyglot
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0.3'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0.3'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: redcarpet
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ~>
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
33
  version: '3.2'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ~>
38
+ - - "~>"
44
39
  - !ruby/object:Gem::Version
45
40
  version: '3.2'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: treetop
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ~>
45
+ - - "~>"
52
46
  - !ruby/object:Gem::Version
53
47
  version: '1.6'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ~>
52
+ - - "~>"
60
53
  - !ruby/object:Gem::Version
61
54
  version: '1.6'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: pygments.rb
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ~>
59
+ - - "~>"
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0.6'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ~>
66
+ - - "~>"
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: 10.0.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: 10.0.0
78
83
  description: Markdown with support for pseudocode to flowchart and sequence diagram
79
84
  generation
80
85
  email: ochkoo@gmail.com
@@ -85,41 +90,39 @@ executables:
85
90
  extensions: []
86
91
  extra_rdoc_files: []
87
92
  files:
93
+ - bin/markdoc
94
+ - bin/pseudo2svg
95
+ - bin/sequence2svg
96
+ - css/pygments.css
97
+ - css/style.css
88
98
  - lib/markdoc.rb
89
99
  - lib/markdoc/pseudocode.rb
90
100
  - lib/markdoc/pseudocode.treetop
91
101
  - lib/markdoc/renderer.rb
92
102
  - lib/markdoc/sequence.rb
93
- - lib/markdoc/sequence.pic
94
103
  - lib/markdoc/version.rb
95
- - css/style.css
96
- - css/pygments.css
97
- - bin/markdoc
98
- - bin/pseudo2svg
99
- - bin/sequence2svg
100
104
  homepage: https://github.com/ochko/markdoc
101
105
  licenses:
102
106
  - MIT
107
+ metadata: {}
103
108
  post_install_message:
104
109
  rdoc_options: []
105
110
  require_paths:
106
111
  - lib
107
112
  required_ruby_version: !ruby/object:Gem::Requirement
108
- none: false
109
113
  requirements:
110
- - - ! '>='
114
+ - - ">="
111
115
  - !ruby/object:Gem::Version
112
116
  version: 1.9.2
113
117
  required_rubygems_version: !ruby/object:Gem::Requirement
114
- none: false
115
118
  requirements:
116
- - - ! '>='
119
+ - - ">="
117
120
  - !ruby/object:Gem::Version
118
121
  version: '0'
119
122
  requirements: []
120
123
  rubyforge_project:
121
- rubygems_version: 1.8.25
124
+ rubygems_version: 2.5.1
122
125
  signing_key:
123
- specification_version: 3
126
+ specification_version: 4
124
127
  summary: Markdown to HTML converter with diagrams
125
128
  test_files: []
@@ -1,406 +0,0 @@
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
23
- spacing = 0.25;
24
- # Active box width
25
- awid = .1;
26
- # Box height
27
- boxht = 0.3;
28
- # Commend folding
29
- corner_fold=awid
30
- # Comment distance
31
- define comment_default_move {up 0.25 right 0.25};
32
- # Comment height
33
- comment_default_ht=0.5;
34
- # Comment width
35
- comment_default_wid=1;
36
- # Underline object name
37
- underline=1;
38
-
39
- # Create a new object(name,label)
40
- define 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)
57
- define 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)
73
- define 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
-
82
- define pobject {
83
- placeholder_object($1);
84
- }
85
-
86
- define 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
106
- define 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)
115
- define 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)
147
- define 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
-
157
- define lconstraint {
158
- lifeline_constraint($1,$2);
159
- }
160
-
161
- # Display an object constraint(label)
162
- # for the last object drawn
163
- define object_constraint {
164
- { box invis with .s at last box .nw $1 ljust; }
165
- }
166
-
167
- define oconstraint {
168
- object_constraint($1);
169
- }
170
-
171
- # Draw a creation message(from_object,to_object,object_label)
172
- define 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
-
202
- define cmessage {
203
- create_message($1,$2,$3);
204
- }
205
-
206
- # Draw an X for a given object
207
- define 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)
215
- define 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
-
228
- define dmessage {
229
- destroy_message($1,$2);
230
- }
231
-
232
- # An object deletes itself: delete(object)
233
- define 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)
240
- define 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
-
265
- define rmessage {
266
- return_message($1,$2,$3);
267
- }
268
-
269
- # Object becomes active
270
- # Can be nested to show recursion
271
- define 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
280
- define 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
290
- define step {
291
- down;
292
- move spacing;
293
- }
294
-
295
- # Switch to asynchronous messages
296
- define async {
297
- arrowhead = 0;
298
- arrowwid = arrowwid * 2;
299
- }
300
-
301
- # Switch to synchronous messages
302
- define sync {
303
- arrowhead = 1;
304
- arrowwid = arrowwid / 2;
305
- }
306
-
307
- # same as lifeline_constraint, but Text and empty string are exchanged.
308
- define 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);
319
- define 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);
332
- define 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);
344
- define 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);
384
- define 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
- }