petrinet 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/Makefile +33 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/examples/cucumber-protocol/cucumber-protocol.xml +900 -0
- data/examples/cucumber-protocol/transition_sample_1.gif +0 -0
- data/examples/cucumber-protocol/transition_sample_1.txt +5 -0
- data/examples/voting/voting.xml +234 -0
- data/examples/x-ray-machine/v1-problem.gif +0 -0
- data/examples/x-ray-machine/v1-problem.txt +5 -0
- data/examples/x-ray-machine/v2-fixed.gif +0 -0
- data/examples/x-ray-machine/v2-fixed.txt +11 -0
- data/examples/x-ray-machine/x-ray-machine-1.xml +212 -0
- data/examples/x-ray-machine/x-ray-machine-2.xml +284 -0
- data/exe/petrinet +33 -0
- data/lib/petrinet/animated_gif_builder.rb +37 -0
- data/lib/petrinet/graphviz_builder.rb +178 -0
- data/lib/petrinet/marking_transition_script.rb +30 -0
- data/lib/petrinet/net.rb +158 -0
- data/lib/petrinet/pnml_builder.rb +71 -0
- data/lib/petrinet/version.rb +3 -0
- data/lib/petrinet.rb +8 -0
- data/petrinet.gemspec +36 -0
- metadata +131 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<pnml>
|
3
|
+
<net>
|
4
|
+
<token id="Default" red="0" green="0" blue="0"/>
|
5
|
+
<place id="wait">
|
6
|
+
<graphics>
|
7
|
+
<position x="161.0" y="248.0"/>
|
8
|
+
</graphics>
|
9
|
+
<name>
|
10
|
+
<value>P0</value>
|
11
|
+
<graphics>
|
12
|
+
<offset x="-5.0" y="35.0"/>
|
13
|
+
</graphics>
|
14
|
+
</name>
|
15
|
+
<capacity>
|
16
|
+
<value>0</value>
|
17
|
+
</capacity>
|
18
|
+
<initialMarking>
|
19
|
+
<graphics>
|
20
|
+
<offset x="0.0" y="0.0"/>
|
21
|
+
</graphics>
|
22
|
+
<value>Default,3</value>
|
23
|
+
</initialMarking>
|
24
|
+
</place>
|
25
|
+
<place id="before">
|
26
|
+
<graphics>
|
27
|
+
<position x="366.0" y="274.0"/>
|
28
|
+
</graphics>
|
29
|
+
<name>
|
30
|
+
<value>P0</value>
|
31
|
+
<graphics>
|
32
|
+
<offset x="-5.0" y="35.0"/>
|
33
|
+
</graphics>
|
34
|
+
</name>
|
35
|
+
<capacity>
|
36
|
+
<value>0</value>
|
37
|
+
</capacity>
|
38
|
+
<initialMarking>
|
39
|
+
<graphics>
|
40
|
+
<offset x="0.0" y="0.0"/>
|
41
|
+
</graphics>
|
42
|
+
<value></value>
|
43
|
+
</initialMarking>
|
44
|
+
</place>
|
45
|
+
<place id="after">
|
46
|
+
<graphics>
|
47
|
+
<position x="599.0" y="273.0"/>
|
48
|
+
</graphics>
|
49
|
+
<name>
|
50
|
+
<value>P0</value>
|
51
|
+
<graphics>
|
52
|
+
<offset x="-5.0" y="35.0"/>
|
53
|
+
</graphics>
|
54
|
+
</name>
|
55
|
+
<capacity>
|
56
|
+
<value>0</value>
|
57
|
+
</capacity>
|
58
|
+
<initialMarking>
|
59
|
+
<graphics>
|
60
|
+
<offset x="0.0" y="0.0"/>
|
61
|
+
</graphics>
|
62
|
+
<value></value>
|
63
|
+
</initialMarking>
|
64
|
+
</place>
|
65
|
+
<place id="free">
|
66
|
+
<graphics>
|
67
|
+
<position x="481.0" y="145.0"/>
|
68
|
+
</graphics>
|
69
|
+
<name>
|
70
|
+
<value>P0</value>
|
71
|
+
<graphics>
|
72
|
+
<offset x="-5.0" y="35.0"/>
|
73
|
+
</graphics>
|
74
|
+
</name>
|
75
|
+
<capacity>
|
76
|
+
<value>0</value>
|
77
|
+
</capacity>
|
78
|
+
<initialMarking>
|
79
|
+
<graphics>
|
80
|
+
<offset x="0.0" y="0.0"/>
|
81
|
+
</graphics>
|
82
|
+
<value>Default,1</value>
|
83
|
+
</initialMarking>
|
84
|
+
</place>
|
85
|
+
<place id="occupied">
|
86
|
+
<graphics>
|
87
|
+
<position x="487.0" y="403.0"/>
|
88
|
+
</graphics>
|
89
|
+
<name>
|
90
|
+
<value>P0</value>
|
91
|
+
<graphics>
|
92
|
+
<offset x="-5.0" y="35.0"/>
|
93
|
+
</graphics>
|
94
|
+
</name>
|
95
|
+
<capacity>
|
96
|
+
<value>0</value>
|
97
|
+
</capacity>
|
98
|
+
<initialMarking>
|
99
|
+
<graphics>
|
100
|
+
<offset x="0.0" y="0.0"/>
|
101
|
+
</graphics>
|
102
|
+
<value></value>
|
103
|
+
</initialMarking>
|
104
|
+
</place>
|
105
|
+
<place id="gone">
|
106
|
+
<graphics>
|
107
|
+
<position x="794.0" y="279.0"/>
|
108
|
+
</graphics>
|
109
|
+
<name>
|
110
|
+
<value>P0</value>
|
111
|
+
<graphics>
|
112
|
+
<offset x="-5.0" y="35.0"/>
|
113
|
+
</graphics>
|
114
|
+
</name>
|
115
|
+
<capacity>
|
116
|
+
<value>0</value>
|
117
|
+
</capacity>
|
118
|
+
<initialMarking>
|
119
|
+
<graphics>
|
120
|
+
<offset x="0.0" y="0.0"/>
|
121
|
+
</graphics>
|
122
|
+
<value></value>
|
123
|
+
</initialMarking>
|
124
|
+
</place>
|
125
|
+
<transition id="leave">
|
126
|
+
<graphics>
|
127
|
+
<position x="701.0" y="264.0"/>
|
128
|
+
</graphics>
|
129
|
+
<name>
|
130
|
+
<value>T0</value>
|
131
|
+
<graphics>
|
132
|
+
<offset x="-5.0" y="35.0"/>
|
133
|
+
</graphics>
|
134
|
+
</name>
|
135
|
+
<infiniteServer>
|
136
|
+
<value>false</value>
|
137
|
+
</infiniteServer>
|
138
|
+
<timed>
|
139
|
+
<value>false</value>
|
140
|
+
</timed>
|
141
|
+
<priority>
|
142
|
+
<value>1</value>
|
143
|
+
</priority>
|
144
|
+
<orientation>
|
145
|
+
<value>0</value>
|
146
|
+
</orientation>
|
147
|
+
<rate>
|
148
|
+
<value>1</value>
|
149
|
+
</rate>
|
150
|
+
</transition>
|
151
|
+
<transition id="enter">
|
152
|
+
<graphics>
|
153
|
+
<position x="264.0" y="262.0"/>
|
154
|
+
</graphics>
|
155
|
+
<name>
|
156
|
+
<value>T0</value>
|
157
|
+
<graphics>
|
158
|
+
<offset x="-5.0" y="35.0"/>
|
159
|
+
</graphics>
|
160
|
+
</name>
|
161
|
+
<infiniteServer>
|
162
|
+
<value>false</value>
|
163
|
+
</infiniteServer>
|
164
|
+
<timed>
|
165
|
+
<value>false</value>
|
166
|
+
</timed>
|
167
|
+
<priority>
|
168
|
+
<value>1</value>
|
169
|
+
</priority>
|
170
|
+
<orientation>
|
171
|
+
<value>0</value>
|
172
|
+
</orientation>
|
173
|
+
<rate>
|
174
|
+
<value>1</value>
|
175
|
+
</rate>
|
176
|
+
</transition>
|
177
|
+
<transition id="make_photo">
|
178
|
+
<graphics>
|
179
|
+
<position x="491.0" y="268.0"/>
|
180
|
+
</graphics>
|
181
|
+
<name>
|
182
|
+
<value>T0</value>
|
183
|
+
<graphics>
|
184
|
+
<offset x="-5.0" y="35.0"/>
|
185
|
+
</graphics>
|
186
|
+
</name>
|
187
|
+
<infiniteServer>
|
188
|
+
<value>false</value>
|
189
|
+
</infiniteServer>
|
190
|
+
<timed>
|
191
|
+
<value>false</value>
|
192
|
+
</timed>
|
193
|
+
<priority>
|
194
|
+
<value>1</value>
|
195
|
+
</priority>
|
196
|
+
<orientation>
|
197
|
+
<value>0</value>
|
198
|
+
</orientation>
|
199
|
+
<rate>
|
200
|
+
<value>1</value>
|
201
|
+
</rate>
|
202
|
+
</transition>
|
203
|
+
<arc id="enter TO occupied" source="enter" target="occupied">
|
204
|
+
<arcpath id="" x="274.0" y="277.0" curvePoint="false"/>
|
205
|
+
<arcpath id="" x="489.0" y="410.0" curvePoint="false"/>
|
206
|
+
<type value="normal"/>
|
207
|
+
<inscription>
|
208
|
+
<value>Default,1</value>
|
209
|
+
</inscription>
|
210
|
+
</arc>
|
211
|
+
<arc id="enter TO before" source="enter" target="before">
|
212
|
+
<arcpath id="" x="274.0" y="277.0" curvePoint="false"/>
|
213
|
+
<arcpath id="" x="366.0" y="287.0" curvePoint="false"/>
|
214
|
+
<type value="normal"/>
|
215
|
+
<inscription>
|
216
|
+
<value>Default,1</value>
|
217
|
+
</inscription>
|
218
|
+
</arc>
|
219
|
+
<arc id="leave TO free" source="leave" target="free">
|
220
|
+
<arcpath id="" x="701.0" y="279.0" curvePoint="false"/>
|
221
|
+
<arcpath id="" x="509.0" y="167.0" curvePoint="false"/>
|
222
|
+
<type value="normal"/>
|
223
|
+
<inscription>
|
224
|
+
<value>Default,1</value>
|
225
|
+
</inscription>
|
226
|
+
</arc>
|
227
|
+
<arc id="make_photo TO after" source="make_photo" target="after">
|
228
|
+
<arcpath id="" x="501.0" y="283.0" curvePoint="false"/>
|
229
|
+
<arcpath id="" x="599.0" y="287.0" curvePoint="false"/>
|
230
|
+
<type value="normal"/>
|
231
|
+
<inscription>
|
232
|
+
<value>Default,1</value>
|
233
|
+
</inscription>
|
234
|
+
</arc>
|
235
|
+
<arc id="leave TO gone" source="leave" target="gone">
|
236
|
+
<arcpath id="" x="711.0" y="279.0" curvePoint="false"/>
|
237
|
+
<arcpath id="" x="794.0" y="292.0" curvePoint="false"/>
|
238
|
+
<type value="normal"/>
|
239
|
+
<inscription>
|
240
|
+
<value>Default,1</value>
|
241
|
+
</inscription>
|
242
|
+
</arc>
|
243
|
+
<arc id="before TO make_photo" source="before" target="make_photo">
|
244
|
+
<arcpath id="" x="396.0" y="288.0" curvePoint="false"/>
|
245
|
+
<arcpath id="" x="491.0" y="283.0" curvePoint="false"/>
|
246
|
+
<type value="normal"/>
|
247
|
+
<inscription>
|
248
|
+
<value>Default,1</value>
|
249
|
+
</inscription>
|
250
|
+
</arc>
|
251
|
+
<arc id="after TO leave" source="after" target="leave">
|
252
|
+
<arcpath id="" x="629.0" y="287.0" curvePoint="false"/>
|
253
|
+
<arcpath id="" x="701.0" y="279.0" curvePoint="false"/>
|
254
|
+
<type value="normal"/>
|
255
|
+
<inscription>
|
256
|
+
<value>Default,1</value>
|
257
|
+
</inscription>
|
258
|
+
</arc>
|
259
|
+
<arc id="free TO enter" source="free" target="enter">
|
260
|
+
<arcpath id="" x="483.0" y="167.0" curvePoint="false"/>
|
261
|
+
<arcpath id="" x="274.0" y="277.0" curvePoint="false"/>
|
262
|
+
<type value="normal"/>
|
263
|
+
<inscription>
|
264
|
+
<value>Default,1</value>
|
265
|
+
</inscription>
|
266
|
+
</arc>
|
267
|
+
<arc id="wait TO enter" source="wait" target="enter">
|
268
|
+
<arcpath id="" x="191.0" y="265.0" curvePoint="false"/>
|
269
|
+
<arcpath id="" x="264.0" y="277.0" curvePoint="false"/>
|
270
|
+
<type value="normal"/>
|
271
|
+
<inscription>
|
272
|
+
<value>Default,1</value>
|
273
|
+
</inscription>
|
274
|
+
</arc>
|
275
|
+
<arc id="occupied TO leave" source="occupied" target="leave">
|
276
|
+
<arcpath id="" x="514.0" y="410.0" curvePoint="false"/>
|
277
|
+
<arcpath id="" x="701.0" y="279.0" curvePoint="false"/>
|
278
|
+
<type value="normal"/>
|
279
|
+
<inscription>
|
280
|
+
<value>Default,1</value>
|
281
|
+
</inscription>
|
282
|
+
</arc>
|
283
|
+
</net>
|
284
|
+
</pnml>
|
data/exe/petrinet
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require "petrinet"
|
5
|
+
|
6
|
+
options = { transitions: [] }
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.banner = "Usage: petrinet [options]"
|
9
|
+
|
10
|
+
opts.on("-t", "--transition=TRANSITION", "Specify a transition to fire. Can be specified multiple times.") do |t|
|
11
|
+
options[:transitions] << t.to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on("-o", "--output=PATH", "Where to write the animated gif") do |o|
|
15
|
+
options[:output] = o
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on("-s", "--script=SCRIPT", "Specify a script file") do |o|
|
19
|
+
options[:script] = o
|
20
|
+
end
|
21
|
+
end.parse!
|
22
|
+
|
23
|
+
pnml = ARGV[0]
|
24
|
+
net = Petrinet::Net.from_pnml(IO.read(pnml))
|
25
|
+
transitions = options[:transitions]
|
26
|
+
output = options[:output]
|
27
|
+
if options[:script]
|
28
|
+
script = Petrinet::MarkingTransitionScript.new(IO.read(options[:script]))
|
29
|
+
net = net.mark(script.marking)
|
30
|
+
transitions = script.transitions + transitions
|
31
|
+
output ||= options[:script].gsub(/\.txt$/, '.gif')
|
32
|
+
end
|
33
|
+
net.to_animated_gif(transitions, output)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Petrinet
|
2
|
+
class AnimatedGifBuilder
|
3
|
+
def initialize(net)
|
4
|
+
@net = net
|
5
|
+
end
|
6
|
+
|
7
|
+
def write(transition_names, gif_path)
|
8
|
+
@image_number = 0
|
9
|
+
Dir.mktmpdir('petrinet-animation') do |tmpdir|
|
10
|
+
net = @net
|
11
|
+
|
12
|
+
write_png(net, tmpdir)
|
13
|
+
transition_names.each do |transition_name|
|
14
|
+
firing = net.prefire(transition_name)
|
15
|
+
write_png(firing, tmpdir)
|
16
|
+
net = net.fire(transition_name)
|
17
|
+
write_png(net, tmpdir)
|
18
|
+
end
|
19
|
+
|
20
|
+
STDOUT.write "🎬\n"
|
21
|
+
`convert -delay 100 -loop 0 #{tmpdir}/*.png #{gif_path}`
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def write_png(net, tmpdir)
|
28
|
+
number_string = '%04d' % @image_number
|
29
|
+
svg_path = "#{tmpdir}/#{number_string}.svg"
|
30
|
+
png_path = "#{tmpdir}/#{number_string}.png"
|
31
|
+
File.open(svg_path, 'w:UTF-8') {|io| io.puts(net.to_svg)}
|
32
|
+
STDOUT.write "👀️"
|
33
|
+
`convert #{svg_path} #{png_path}`
|
34
|
+
@image_number += 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Petrinet
|
5
|
+
class GraphvizBuilder
|
6
|
+
def initialize(net, transition_vector_by_transition_name, place_name_by_place_index, state_vector)
|
7
|
+
@net = net
|
8
|
+
@transition_vectors_by_transition_name = transition_vector_by_transition_name
|
9
|
+
@place_name_by_place_index = place_name_by_place_index
|
10
|
+
@state_vector = state_vector
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generates an SVG for a net
|
14
|
+
def svg
|
15
|
+
dot_source = dot
|
16
|
+
dotfile = Tempfile.new("petrinet.dot")
|
17
|
+
dotfile.write(dot_source)
|
18
|
+
dotfile.close
|
19
|
+
svgfile = Tempfile.new('petrinet.svg')
|
20
|
+
# circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopi
|
21
|
+
`dot -T svg -Kdot #{dotfile.path} -o #{svgfile.path}`
|
22
|
+
`cat #{svgfile.path}`
|
23
|
+
svg = svgfile.read
|
24
|
+
processed_svg(svg)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def processed_svg(svg)
|
30
|
+
doc = Nokogiri::XML(svg)
|
31
|
+
doc = draw_tokens(doc)
|
32
|
+
doc = remove_rectangles(doc)
|
33
|
+
doc.to_xml
|
34
|
+
end
|
35
|
+
|
36
|
+
def dot
|
37
|
+
transition_vectors_by_transition_name = Hash[@transition_vectors_by_transition_name.sort]
|
38
|
+
place_name_by_place_index = Hash[@place_name_by_place_index.sort]
|
39
|
+
|
40
|
+
dot = <<-EOS
|
41
|
+
digraph PetriNet {
|
42
|
+
graph [
|
43
|
+
bgcolor=white,
|
44
|
+
labeljust=l,
|
45
|
+
labelloc=t,
|
46
|
+
nodesep=0.5,
|
47
|
+
penwidth=0,
|
48
|
+
ranksep=0.5,
|
49
|
+
style=filled
|
50
|
+
];
|
51
|
+
node [label="\\N"];
|
52
|
+
EOS
|
53
|
+
|
54
|
+
place_name_by_place_index.each do |place_index, place_name|
|
55
|
+
marking = @state_vector[place_index]
|
56
|
+
dot += <<-EOS
|
57
|
+
subgraph "cluster_place_#{place_name}" {
|
58
|
+
graph [
|
59
|
+
label="#{place_name}",
|
60
|
+
];
|
61
|
+
node [shape=circle];
|
62
|
+
"place_#{place_name}" [
|
63
|
+
label=#{marking},
|
64
|
+
width=0.75
|
65
|
+
];
|
66
|
+
}
|
67
|
+
EOS
|
68
|
+
end
|
69
|
+
|
70
|
+
transition_vectors_by_transition_name.each do |transition_name, transition_vectors|
|
71
|
+
fillcolor = if @net.prefire_transition_name == transition_name
|
72
|
+
'green'
|
73
|
+
else
|
74
|
+
@net.fireable?(transition_name) ? 'red' : 'black'
|
75
|
+
end
|
76
|
+
|
77
|
+
dot += <<-EOS
|
78
|
+
subgraph "cluster_transition_#{transition_name}" {
|
79
|
+
graph [
|
80
|
+
label="#{transition_name}",
|
81
|
+
];
|
82
|
+
node [
|
83
|
+
shape=box,
|
84
|
+
fillcolor="#{fillcolor}",
|
85
|
+
style="solid, filled",
|
86
|
+
height=0.1,
|
87
|
+
width=0.5
|
88
|
+
];
|
89
|
+
"transition_#{transition_name}" [
|
90
|
+
label="",
|
91
|
+
height=0.1,
|
92
|
+
width=0.5
|
93
|
+
];
|
94
|
+
}
|
95
|
+
EOS
|
96
|
+
transition_vectors.each do |transition_vector|
|
97
|
+
transition_vector.each_with_index do |direction, place_index|
|
98
|
+
place_name = place_name_by_place_index[place_index]
|
99
|
+
raise "No place_name for index #{place_index}: #{place_name_by_place_index}" if place_name.nil?
|
100
|
+
if direction < 0
|
101
|
+
dot += %Q{ "place_#{place_name}" -> "transition_#{transition_name}"\n}
|
102
|
+
elsif direction > 0
|
103
|
+
dot += %Q{ "transition_#{transition_name}" -> "place_#{place_name}"\n}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
dot += "}\n"
|
110
|
+
dot
|
111
|
+
end
|
112
|
+
|
113
|
+
# Replaces the place labels (which are numbers) with black dots.
|
114
|
+
def draw_tokens(doc)
|
115
|
+
# place radius (outer)
|
116
|
+
pr = 6
|
117
|
+
# place radius (inner = with padding)
|
118
|
+
pri = pr * 0.8
|
119
|
+
|
120
|
+
texts = doc.search('text')
|
121
|
+
texts.each do |text|
|
122
|
+
circle = (text.parent.search('ellipse') || text.parent.search('circle'))[0]
|
123
|
+
if circle
|
124
|
+
cx = circle[:cx].to_i
|
125
|
+
cy = circle[:cy].to_i
|
126
|
+
case text.text
|
127
|
+
when '0'
|
128
|
+
when '1'
|
129
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx}" cy="#{cy}" r="#{pri}" />}
|
130
|
+
when '2'
|
131
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - pr}" cy="#{cy}" r="#{pri}" />}
|
132
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + pr}" cy="#{cy}" r="#{pri}" />}
|
133
|
+
when '3'
|
134
|
+
fx_bot = 1
|
135
|
+
fy_bot = Math.tan(rad(30))
|
136
|
+
fx_top = 0
|
137
|
+
fy_top = 1 / Math.cos(rad(30))
|
138
|
+
|
139
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />}
|
140
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />}
|
141
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />}
|
142
|
+
when '4'
|
143
|
+
fx_bot = fy_bot = fx_top = fy_top = 1
|
144
|
+
|
145
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />}
|
146
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />}
|
147
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />}
|
148
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />}
|
149
|
+
when '5'
|
150
|
+
fx_bot = fy_bot = fx_top = fy_top = 2 * Math.sin(rad(45))
|
151
|
+
|
152
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />}
|
153
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_top * pr}" cy="#{cy - fy_top * pr}" r="#{pri}" />}
|
154
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx - fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />}
|
155
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx + fx_bot * pr}" cy="#{cy + fy_bot * pr}" r="#{pri}" />}
|
156
|
+
text.add_next_sibling %Q{<circle fill="#000000" stroke="none" cx="#{cx}" cy="#{cy}" r="#{pri}" />}
|
157
|
+
else
|
158
|
+
raise "Cannot draw dots for #{text.text} tokens"
|
159
|
+
end
|
160
|
+
text.remove
|
161
|
+
end
|
162
|
+
end
|
163
|
+
doc
|
164
|
+
end
|
165
|
+
|
166
|
+
def remove_rectangles(doc)
|
167
|
+
polygons = doc.xpath('//svg:polygon[@fill="#ffffff" and @stroke="#000000"]', 'svg' => 'http://www.w3.org/2000/svg')
|
168
|
+
polygons.each do |polygon|
|
169
|
+
polygon.remove
|
170
|
+
end
|
171
|
+
doc
|
172
|
+
end
|
173
|
+
|
174
|
+
def rad(y)
|
175
|
+
y % 360 * Math::PI / 180
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Petrinet
|
2
|
+
class MarkingTransitionScript
|
3
|
+
def initialize(source)
|
4
|
+
@source = source
|
5
|
+
end
|
6
|
+
|
7
|
+
def marking
|
8
|
+
pairs = lines.select do |line|
|
9
|
+
line =~ /:\d+\s*$/
|
10
|
+
end.map do |line|
|
11
|
+
parts = line.split(':')
|
12
|
+
[parts[0].to_sym, parts[1].to_i]
|
13
|
+
end
|
14
|
+
Hash[pairs]
|
15
|
+
end
|
16
|
+
|
17
|
+
def transitions
|
18
|
+
pairs = lines.reject do |line|
|
19
|
+
line =~ /:\d+\s*$/
|
20
|
+
end.map(&:to_sym)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def lines
|
26
|
+
@source.split(/\n/)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|