rubypost 0.0.1
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.
- data/README +14 -0
- data/lib/drawable.rb +230 -0
- data/lib/graph.rb +289 -0
- data/lib/objects.rb +161 -0
- data/lib/options.rb +177 -0
- data/lib/redefine_float_to_s_for_metapost.rb +10 -0
- data/lib/revert_float_to_s.rb +6 -0
- data/lib/rubypost.rb +5 -0
- data/tests/test_cricle.rb +24 -0
- data/tests/test_graph.rb +30 -0
- data/tests/test_pair.rb +31 -0
- data/tests/test_path.rb +116 -0
- data/tests/test_square.rb +44 -0
- metadata +58 -0
data/README
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#RubyPost 0.0.1 README
|
2
|
+
|
3
|
+
Run
|
4
|
+
|
5
|
+
rake
|
6
|
+
|
7
|
+
to build the gem, then install the gem into your ruby installation.
|
8
|
+
This is a very early version, it's likely to undergo some significant
|
9
|
+
modifications. Currently working is the graphing part
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
|
data/lib/drawable.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
#require 'vector'
|
3
|
+
|
4
|
+
|
5
|
+
module RubyPost
|
6
|
+
|
7
|
+
#rubypost drawable
|
8
|
+
#base class for anything that actually gets drawn on a figure
|
9
|
+
class Drawable < Object
|
10
|
+
|
11
|
+
attr_reader :draw_type
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@options = Array.new
|
15
|
+
@draw_type = 'draw'
|
16
|
+
end
|
17
|
+
|
18
|
+
#normal path draw
|
19
|
+
def draw
|
20
|
+
@draw_type = 'draw'
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
#fill the path
|
25
|
+
def fill
|
26
|
+
@draw_type = 'fill'
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
#arrowhead at end of the path
|
31
|
+
def arrow
|
32
|
+
@draw_type = 'drawarrow'
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
#arrowhead at both ends
|
37
|
+
def dblarrow
|
38
|
+
@draw_type = 'drawdblarrow'
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_option(o)
|
43
|
+
@options.push(o)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
#utility function to compile all the options
|
48
|
+
def compile_options
|
49
|
+
str = String.new
|
50
|
+
@options.each { |o| str = str + ' ' + o.compile }
|
51
|
+
return str
|
52
|
+
end
|
53
|
+
|
54
|
+
#macro for setting the colour of a drawable
|
55
|
+
def colour(r,g,b)
|
56
|
+
add_option(Colour.new(r,g,b))
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
#macro for setting the colour of a drawable.
|
61
|
+
#Spelt incorrectly.
|
62
|
+
def color(r,g,b)
|
63
|
+
colour(r,g,b)
|
64
|
+
end
|
65
|
+
|
66
|
+
#macro for scaling a drawable. This is the
|
67
|
+
#same as using add_option(Scale.new(s))
|
68
|
+
def scale(s)
|
69
|
+
add_option(Scale.new(s))
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
#macro for translating a drawable. This is the
|
74
|
+
#same as using add_option(Translate.new(x,y))
|
75
|
+
def translate(x,y)
|
76
|
+
add_option(Translate.new(x,y))
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
#macro for rotating a drawable. This is the
|
81
|
+
#same as using add_option(Rotate.new(a))
|
82
|
+
def rotate(a)
|
83
|
+
add_option(Rotate.new(a))
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
#A custom drawable. Send it a metapost string
|
90
|
+
class CustomDrawable < Drawable
|
91
|
+
|
92
|
+
attr_writer :command
|
93
|
+
|
94
|
+
def initialize(c=String.new)
|
95
|
+
super()
|
96
|
+
@command = c
|
97
|
+
end
|
98
|
+
|
99
|
+
def compile
|
100
|
+
@command.compile + compile_options + ";\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
#Cartesion point.
|
106
|
+
class Pair < Drawable
|
107
|
+
|
108
|
+
attr :x
|
109
|
+
attr :y
|
110
|
+
|
111
|
+
#Can set the value and individual scales of the x and y point
|
112
|
+
def initialize(x=0,y=0)
|
113
|
+
super()
|
114
|
+
@x = x
|
115
|
+
@y = y
|
116
|
+
end
|
117
|
+
|
118
|
+
def compile
|
119
|
+
'(' + @x.compile + ', ' + @y.compile + ')'
|
120
|
+
end
|
121
|
+
|
122
|
+
#returns the addition of the pairs
|
123
|
+
def +(p)
|
124
|
+
Pair.new(@x + p.x, @y + p.y)
|
125
|
+
end
|
126
|
+
|
127
|
+
#returns the subtraction of the pairs
|
128
|
+
def -(p)
|
129
|
+
Pair.new(@x - p.x, @y - p.y)
|
130
|
+
end
|
131
|
+
|
132
|
+
#returns the pair multiplied by a scalar
|
133
|
+
def *(n)
|
134
|
+
Pair.new(@x*n, @y*n)
|
135
|
+
end
|
136
|
+
|
137
|
+
#returns the pair divided by a scalar
|
138
|
+
def /(n)
|
139
|
+
Pair.new(@x/n, @y/n)
|
140
|
+
end
|
141
|
+
|
142
|
+
#returns true if the pairs are equal
|
143
|
+
def ==(p)
|
144
|
+
@x == p.x && @y == p.y
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
#sequence of pairs connected as a metapost path
|
150
|
+
class Path < Drawable
|
151
|
+
|
152
|
+
def initialize
|
153
|
+
super
|
154
|
+
@p = Array.new
|
155
|
+
straight
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_pair(p)
|
159
|
+
@p.push(p)
|
160
|
+
end
|
161
|
+
|
162
|
+
#returns a pair that is the centroid of the pairs
|
163
|
+
#of this path
|
164
|
+
def center(p)
|
165
|
+
ret = Pair.new
|
166
|
+
@p.each { |p| ret = ret + p }
|
167
|
+
return ret/p.length
|
168
|
+
end
|
169
|
+
|
170
|
+
#reverse the path. <br>
|
171
|
+
#Note, this reverses the pairs that have so far
|
172
|
+
#been added. Anything added after calling reverse
|
173
|
+
#will be appended to the end of the array as usual
|
174
|
+
def reverse
|
175
|
+
@p = @p.reverse
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
#set the path to have straight line segments
|
180
|
+
def straight
|
181
|
+
@line_type = '--'
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
#set the path to have curved line segments
|
186
|
+
def curved
|
187
|
+
@line_type = '..'
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
def compile
|
192
|
+
str = '('
|
193
|
+
(@p.length-1).times do |i|
|
194
|
+
str = str + @p[i].compile + @line_type
|
195
|
+
end
|
196
|
+
str = str + @p[-1].compile + ')'
|
197
|
+
str = str + compile_options + ";"
|
198
|
+
str
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
#Wraps teh metapost unitsquare command
|
204
|
+
class Square < Path
|
205
|
+
|
206
|
+
def initialize
|
207
|
+
super
|
208
|
+
@p.push(Pair.new(-0.5,-0.5))
|
209
|
+
@p.push(Pair.new(-0.5,0.5))
|
210
|
+
@p.push(Pair.new(0.5,0.5))
|
211
|
+
@p.push(Pair.new(0.5,-0.5))
|
212
|
+
@p.push('cycle')
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
#Wraps the metapost fullcircle command
|
218
|
+
class Circle < Drawable
|
219
|
+
|
220
|
+
def initialize
|
221
|
+
super
|
222
|
+
end
|
223
|
+
|
224
|
+
def compile
|
225
|
+
'fullcircle ' + compile_options + ";\n"
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
data/lib/graph.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
|
2
|
+
module RubyPost
|
3
|
+
|
4
|
+
#wrapper for the metapost graphing macro
|
5
|
+
#multiple coordinate systems in a single graph is not yet supported
|
6
|
+
#May require pre and post options for graph data which doesn't have
|
7
|
+
#a neat solution. You can ofcourse, still do this with a CustomDrawable.
|
8
|
+
class Graph < Drawable
|
9
|
+
|
10
|
+
#automattically sets the size of the graph to 10cm x 10cm
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
@dati = Array.new
|
14
|
+
$Inputs.add_input('graph')
|
15
|
+
$Inputs.add_input('sarith')
|
16
|
+
self.set_size(10,10)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_data(d)
|
20
|
+
@dati.push(d)
|
21
|
+
end
|
22
|
+
|
23
|
+
#set the size of the graph in cm
|
24
|
+
def set_size(w,h)
|
25
|
+
@width = w
|
26
|
+
@height = h
|
27
|
+
end
|
28
|
+
|
29
|
+
def compile
|
30
|
+
str = "begingraph(" + @width.to_s + "cm, " + @height.to_s + "cm);\n"
|
31
|
+
str = str + compile_options + "\n"
|
32
|
+
@dati.each do |d|
|
33
|
+
str = str + d.compile_pre_commands + "gdraw " + d.compile + d.compile_post_commands + "\n"
|
34
|
+
end
|
35
|
+
str = str + "endgraph;\n"
|
36
|
+
str
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
#Base class for all graph data. There are some commands, such as autogrid
|
42
|
+
#that must be called after the data is entered with gdraw. There are others that
|
43
|
+
#must be set before the call, such as setcoords. These macro ensure that order
|
44
|
+
#is maintained
|
45
|
+
class BaseGraphData < Drawable
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
super
|
49
|
+
@pre_commands = Array.new
|
50
|
+
@post_commands = Array.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_pre_command(c)
|
54
|
+
@pre_commands.push(c)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_post_command(c)
|
58
|
+
@post_commands.push(c)
|
59
|
+
end
|
60
|
+
|
61
|
+
def compile_pre_commands
|
62
|
+
str = String.new
|
63
|
+
@pre_commands.each { |c| str = str + c.compile }
|
64
|
+
return str
|
65
|
+
end
|
66
|
+
|
67
|
+
#add a grid to the graph. This is always done as a post command
|
68
|
+
#in metapost
|
69
|
+
def add_grid(grid)
|
70
|
+
add_post_command(grid)
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_range(range)
|
74
|
+
add_pre_command(range)
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_coords(coords)
|
78
|
+
add_pre_command(coords)
|
79
|
+
end
|
80
|
+
|
81
|
+
def compile_post_commands
|
82
|
+
str = String.new
|
83
|
+
@post_commands.each { |c| str = str + c.compile }
|
84
|
+
return str
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
#set the x and y coordinates arrays independently
|
92
|
+
#This uses temp files in order to store and create
|
93
|
+
#the graph. Graphs created with this type of data
|
94
|
+
#need to be compiled directly to postscript using
|
95
|
+
#RubyPost::File#compile_to_ps unless you want to
|
96
|
+
#copy all of the temporay files too!
|
97
|
+
class GraphData < BaseGraphData
|
98
|
+
|
99
|
+
attr_writer :x, :y
|
100
|
+
|
101
|
+
#class variable to store the number of the temporary
|
102
|
+
#files used and keep the filenames separate
|
103
|
+
@@graph_data_temp_file = 0
|
104
|
+
|
105
|
+
#must make a copy of the arrays as it will be compiled
|
106
|
+
#at some latter time and the referenced memory may not
|
107
|
+
#be around then!
|
108
|
+
def initialize(x=Array.new,y=Array.new)
|
109
|
+
super()
|
110
|
+
@x = Array.new(x)
|
111
|
+
@y = Array.new(y)
|
112
|
+
@filename = 'temp_graph_data_' + @@graph_data_temp_file.to_s + '.txt'
|
113
|
+
@@graph_data_temp_file = @@graph_data_temp_file+1
|
114
|
+
end
|
115
|
+
|
116
|
+
#creates the tempory file with the graph data and send this to the
|
117
|
+
#metapost file. Also note that we force scientific notation for the
|
118
|
+
#number format as it is ,most compatible with metapost graph.
|
119
|
+
def compile
|
120
|
+
min = [@x.length, @y.length].min
|
121
|
+
IO::File.open(@filename, 'w') do |f|
|
122
|
+
min.times { |i| f.puts sprintf("%e", @x[i]) + ' ' + sprintf("%e", @y[i]) }
|
123
|
+
end
|
124
|
+
"\"" + @filename + "\" " + compile_options + ";"
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
#Implements the graph data filename macro
|
130
|
+
class GraphFile < BaseGraphData
|
131
|
+
|
132
|
+
attr_writer :filename
|
133
|
+
|
134
|
+
def initialize(filename=nil)
|
135
|
+
super()
|
136
|
+
@filename = filename
|
137
|
+
end
|
138
|
+
|
139
|
+
def compile
|
140
|
+
"\"" + @filename + "\" " + compile_options + ";"
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
#class for the special options related to graph paths
|
147
|
+
class GraphDataOption < PathOption
|
148
|
+
end
|
149
|
+
|
150
|
+
#wraps the plot option for the graph macro
|
151
|
+
class Plot < GraphDataOption
|
152
|
+
|
153
|
+
attr_writer :dot_type
|
154
|
+
|
155
|
+
#default is a latex bullet
|
156
|
+
def initialize(p='btex $\bullet$ etex')
|
157
|
+
@dot_type = p
|
158
|
+
end
|
159
|
+
|
160
|
+
def compile
|
161
|
+
'plot ' + @dot_type
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
#Option extention for graphs
|
167
|
+
class GraphOption < Option
|
168
|
+
end
|
169
|
+
|
170
|
+
#set the axis coords, can be log or linear scale
|
171
|
+
class Coords < GraphOption
|
172
|
+
|
173
|
+
def initialize(x='linear',y='linear')
|
174
|
+
@x = x
|
175
|
+
@y = y
|
176
|
+
end
|
177
|
+
|
178
|
+
def loglog
|
179
|
+
@x = 'log'
|
180
|
+
@y = 'log'
|
181
|
+
end
|
182
|
+
|
183
|
+
def linearlinear
|
184
|
+
@x = 'log'
|
185
|
+
@y = 'log'
|
186
|
+
end
|
187
|
+
|
188
|
+
def linearlog
|
189
|
+
@x = 'linear'
|
190
|
+
@y = 'log'
|
191
|
+
end
|
192
|
+
|
193
|
+
def loglinear
|
194
|
+
@x = 'log'
|
195
|
+
@y = 'linear'
|
196
|
+
end
|
197
|
+
|
198
|
+
def compile
|
199
|
+
'setcoords(' + @x + ',' + @y + ");\n"
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
#wraps the setrange function in metapost
|
206
|
+
#can use strings or numbers.
|
207
|
+
class Range < GraphOption
|
208
|
+
|
209
|
+
attr_writer :xmin, :xmax, :ymin, :ymax
|
210
|
+
|
211
|
+
def initialize(xmin='whatever', ymin='whatever', xmax='whatever', ymax='whatever')
|
212
|
+
@xmin = xmin
|
213
|
+
@xmax = xmax
|
214
|
+
@ymin = ymin
|
215
|
+
@ymax = ymax
|
216
|
+
end
|
217
|
+
|
218
|
+
def compile
|
219
|
+
'setrange(' + @xmin.to_s + ', ' + @ymin.to_s + ', ' + @xmax.to_s + ', ' + @ymax.to_s + ");\n"
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
#set the position and type of grid tick marks and labels
|
226
|
+
class AutoGrid < Drawable
|
227
|
+
|
228
|
+
attr_writer :x, :y
|
229
|
+
|
230
|
+
def initialize(x=String.new, y=String.new)
|
231
|
+
super()
|
232
|
+
@x = x
|
233
|
+
@y = y
|
234
|
+
end
|
235
|
+
|
236
|
+
def compile
|
237
|
+
'autogrid(' + @x + ', ' + @y + ")" + compile_options + ";\n"
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
#base class for graph labels
|
243
|
+
class GraphLabel < GraphOption
|
244
|
+
|
245
|
+
attr_writer :label
|
246
|
+
|
247
|
+
def initialize(label=nil)
|
248
|
+
@label = label
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
#place an x label on the graph
|
254
|
+
class XLabel < GraphLabel
|
255
|
+
|
256
|
+
def compile
|
257
|
+
'glabel.bot(' + @label + ", OUT);\n"
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
#place an y label on the graph
|
263
|
+
class YLabel < GraphLabel
|
264
|
+
|
265
|
+
def compile
|
266
|
+
'glabel.lft(' + @label + ", OUT);\n"
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
#place an y label on the right hand side of the graph
|
272
|
+
class YLabelRight < GraphLabel
|
273
|
+
|
274
|
+
def compile
|
275
|
+
'glabel.rt(' + @label + ", OUT);\n"
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
#place a title on the top of the graph
|
281
|
+
class GraphTitle < GraphLabel
|
282
|
+
|
283
|
+
def compile
|
284
|
+
'glabel.top(' + @label + ", OUT);\n"
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
data/lib/objects.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
module RubyPost
|
2
|
+
|
3
|
+
#base class for rubypost
|
4
|
+
class Object
|
5
|
+
#returns the string of metapost commmands
|
6
|
+
def compile
|
7
|
+
'%compile not implemented for ' + self.class.to_s
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
#stores the macros that particular drawbles need.
|
12
|
+
#This should really be a private class.
|
13
|
+
class Macros < Object
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@inputs = Hash.new
|
17
|
+
end
|
18
|
+
|
19
|
+
#uses hash to make sure we never input same thing twice
|
20
|
+
def add_input(s)
|
21
|
+
@inputs[s] = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def compile
|
25
|
+
str = String.new
|
26
|
+
@inputs.each_key do
|
27
|
+
|k| str = str + "input " + k + ";\n"
|
28
|
+
end
|
29
|
+
str
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
#global module variable to be altered by drawables that
|
35
|
+
#need a particular metapost macro input.
|
36
|
+
$Inputs = Macros.new
|
37
|
+
|
38
|
+
#metapost file
|
39
|
+
#A metapost file can contain many figures.
|
40
|
+
#Notes: Filenames cannot contain underscores for view to work! <br>
|
41
|
+
#compile_to_str has a dodgy backspace handler.
|
42
|
+
class File < Object
|
43
|
+
|
44
|
+
attr_writer :fname
|
45
|
+
|
46
|
+
@@start_of_file = "prologues := 2;\n"
|
47
|
+
|
48
|
+
#input 'sarith' so that metapost can read exponential notation
|
49
|
+
def initialize(fname = nil)
|
50
|
+
@figures = Array.new
|
51
|
+
@fname = fname
|
52
|
+
end
|
53
|
+
|
54
|
+
#add a new figure to this mpost file
|
55
|
+
def add_figure(f)
|
56
|
+
@figures.push(f)
|
57
|
+
end
|
58
|
+
|
59
|
+
#returns the mp file as a str
|
60
|
+
def compile_to_string
|
61
|
+
str = @@start_of_file + $Inputs.compile
|
62
|
+
@figures.each_index do
|
63
|
+
|i| str = str + 'beginfig(' + (i+1).to_s + ");\n" + @figures[i].compile + "\n"
|
64
|
+
end
|
65
|
+
str = str + "end;\n"
|
66
|
+
#remove the backspaces
|
67
|
+
strback = str.gsub(/.[\b]/, '')
|
68
|
+
if (strback==nil)
|
69
|
+
return str
|
70
|
+
else
|
71
|
+
return strback
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
#writes the string of metapost commands to a file named 'fname.mp'
|
76
|
+
def compile_to_file(fname=@fname)
|
77
|
+
@fname = fname
|
78
|
+
IO::File.open(fname + '.mp','w') { |f| f.puts self.compile_to_string }
|
79
|
+
end
|
80
|
+
|
81
|
+
#calls compile_to_file and writes the
|
82
|
+
#and copmiles the metapost commands if mpost is in the path
|
83
|
+
def compile(fname=@fname)
|
84
|
+
compile_to_file(fname)
|
85
|
+
system('mpost -quiet ' + fname + '.mp')
|
86
|
+
end
|
87
|
+
|
88
|
+
#default view command is view_dvi
|
89
|
+
def view
|
90
|
+
view_dvi
|
91
|
+
end
|
92
|
+
|
93
|
+
#assumes that the file has already been compiled by metapost. ie compile_to_ps
|
94
|
+
#has already been called. This assumes that the yap viewer and tex is in your path. Install
|
95
|
+
#miktex to get these by default. <p>
|
96
|
+
#Notes: Filenames cannot contain underscores for view_dvi to work. The "tex mproof" will
|
97
|
+
#not work with underscores
|
98
|
+
def view_dvi
|
99
|
+
str = 'tex mproof'
|
100
|
+
@figures.each_index { |i| str = str + ' ' + @fname + '.' + (i+1).to_s }
|
101
|
+
system(str)
|
102
|
+
system('yap mproof.dvi')
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
#wrapper for the metapost figure
|
108
|
+
#Figures actually become the figures that will to be viewed
|
109
|
+
class Figure < Object
|
110
|
+
|
111
|
+
def initialize
|
112
|
+
@drawables = Array.new
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_drawable(d)
|
116
|
+
@drawables.push(d)
|
117
|
+
end
|
118
|
+
|
119
|
+
def compile
|
120
|
+
str = String.new
|
121
|
+
@drawables.each do |d|
|
122
|
+
str = str + d.draw_type + ' ' + d.compile + "\n"
|
123
|
+
end
|
124
|
+
str = str + "endfig;\n"
|
125
|
+
return str
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
#Add the compile functionality to Ruby Numeric. Calling
|
133
|
+
#compile will add the cm, inch, bp metapost sizes to the
|
134
|
+
#to_s
|
135
|
+
class Numeric
|
136
|
+
def cm
|
137
|
+
@mpsize = 'cm'
|
138
|
+
self
|
139
|
+
end
|
140
|
+
def inch
|
141
|
+
@mpsize = 'inch'
|
142
|
+
self
|
143
|
+
end
|
144
|
+
def bp
|
145
|
+
@mpsize = 'bp'
|
146
|
+
self
|
147
|
+
end
|
148
|
+
#A nifty thing here is that if you dont specify the size
|
149
|
+
#if wont print anything
|
150
|
+
def compile
|
151
|
+
to_s + @mpsize.to_s
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
#Add the compile functionality to Ruby String class
|
156
|
+
#it just calls to_s
|
157
|
+
class String
|
158
|
+
def compile
|
159
|
+
to_s
|
160
|
+
end
|
161
|
+
end
|
data/lib/options.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
|
2
|
+
module RubyPost
|
3
|
+
|
4
|
+
#return the string s wrapped in btex etex
|
5
|
+
def latex(s)
|
6
|
+
'btex ' + s + ' etex'
|
7
|
+
end
|
8
|
+
|
9
|
+
#options for rubypost drawables. These wrap metapose commands
|
10
|
+
#such as 'withpen', 'scaled' and etc
|
11
|
+
class Option < Object
|
12
|
+
end
|
13
|
+
|
14
|
+
#Can insert pure metapost option commands here.
|
15
|
+
class CustomOption < Option
|
16
|
+
def initialize
|
17
|
+
@command = String.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(c=Sting.new)
|
21
|
+
@command = c
|
22
|
+
end
|
23
|
+
|
24
|
+
def compile
|
25
|
+
@command.compile
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
#wrapper for the metapost withcolor command
|
31
|
+
#Except colour is now spelt correctly.
|
32
|
+
class Colour < Option
|
33
|
+
|
34
|
+
attr :r
|
35
|
+
attr :g
|
36
|
+
attr :b
|
37
|
+
|
38
|
+
def initialize(r=0,g=0,b=0)
|
39
|
+
@r = r
|
40
|
+
@g = g
|
41
|
+
@b = b
|
42
|
+
end
|
43
|
+
|
44
|
+
def compile
|
45
|
+
'withcolor' + '(' + @r.to_s + ',' + @g.to_s + ',' + @b.to_s + ')'
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
#incorrectly spelt Colour alias
|
51
|
+
class Color < Colour
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
#wraps the metapost withpen command
|
56
|
+
class Pen < Option
|
57
|
+
|
58
|
+
attr_writer :pen_type, :scale
|
59
|
+
|
60
|
+
def initialize(pt='pencircle', scale = 1)
|
61
|
+
@pen_type = pt
|
62
|
+
@scale = scale
|
63
|
+
end
|
64
|
+
|
65
|
+
def compile
|
66
|
+
'withpen ' + @pen_type + ' scaled ' + @scale.compile
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
#base class for options related to paths
|
72
|
+
class PathOption < Option
|
73
|
+
end
|
74
|
+
|
75
|
+
#dased path
|
76
|
+
#there are a plethora of dashing options. Only implemented
|
77
|
+
#evenly at present.
|
78
|
+
class Dashed < PathOption
|
79
|
+
|
80
|
+
def initialize(t='evenly')
|
81
|
+
@type = t
|
82
|
+
end
|
83
|
+
|
84
|
+
#evenly dashed line
|
85
|
+
def evenly
|
86
|
+
@type = 'evenly'
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
#dahed line with dots inbetween dashes
|
91
|
+
def withdots
|
92
|
+
@type = 'withdots'
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
#set the type with the metapost command s.
|
97
|
+
def type(s)
|
98
|
+
@type = s
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def compile
|
103
|
+
'dashed ' + @type
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
#Wrapped the scaled metapost command.
|
109
|
+
#Resizes the drawable.
|
110
|
+
class Scale < Option
|
111
|
+
|
112
|
+
attr_writer :scale
|
113
|
+
|
114
|
+
def initialize(scale=1)
|
115
|
+
@scale = scale
|
116
|
+
end
|
117
|
+
|
118
|
+
def compile
|
119
|
+
'scaled ' + @scale.compile
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
#Scale alias
|
125
|
+
class Scaled < Scale
|
126
|
+
end
|
127
|
+
|
128
|
+
#Wraps the rotated metapost command
|
129
|
+
class Rotate < Option
|
130
|
+
|
131
|
+
attr_writer :degrees
|
132
|
+
|
133
|
+
def initialize(degrees=0)
|
134
|
+
@degrees = degrees
|
135
|
+
end
|
136
|
+
|
137
|
+
#set the angle in radiians
|
138
|
+
def radians=(rads)
|
139
|
+
@degrees = 180.0*rads/Math::PI
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def compile
|
144
|
+
'rotated ' + @degrees.to_s
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
#Rotate alias
|
150
|
+
class Rotated < Rotate
|
151
|
+
end
|
152
|
+
|
153
|
+
#Wraps the metapose shifted command. <br>
|
154
|
+
#The translation @t is specifed by a Pair
|
155
|
+
class Translate < Option
|
156
|
+
|
157
|
+
attr_writer :t
|
158
|
+
|
159
|
+
def initialize(p=Pair.new)
|
160
|
+
@t = p
|
161
|
+
end
|
162
|
+
|
163
|
+
def compile
|
164
|
+
'shifted ' + @t.compile
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
#Translate alias
|
170
|
+
class Shifted < Translate
|
171
|
+
end
|
172
|
+
|
173
|
+
#Translate alias
|
174
|
+
class Shift < Translate
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
data/lib/rubypost.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#require 'rubygems'
|
2
|
+
#gem 'rubypost'
|
3
|
+
#require 'rubypost'
|
4
|
+
|
5
|
+
#load the rest of rubypost
|
6
|
+
require '../lib/objects'
|
7
|
+
require '../lib/drawable'
|
8
|
+
require '../lib/options'
|
9
|
+
|
10
|
+
include RubyPost
|
11
|
+
|
12
|
+
#note, can't use underscores in filenames if you are going
|
13
|
+
#to use File#view. tex mproof breaks
|
14
|
+
file = RubyPost::File.new('testcircle')
|
15
|
+
|
16
|
+
#draw a circle
|
17
|
+
fig1 = Figure.new
|
18
|
+
file.add_figure(fig1)
|
19
|
+
circle1 = RubyPost::Circle.new.scale(1.cm)
|
20
|
+
fig1.add_drawable(circle1)
|
21
|
+
|
22
|
+
puts file.compile_to_string
|
23
|
+
file.compile
|
24
|
+
file.view
|
data/tests/test_graph.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#code to generate a plot of the data in metapost format
|
2
|
+
require 'rubypost'
|
3
|
+
require 'drawable'
|
4
|
+
require 'options'
|
5
|
+
require 'graph'
|
6
|
+
|
7
|
+
include RubyPost
|
8
|
+
|
9
|
+
file = RubyPost::File.new
|
10
|
+
fig = Figure.new
|
11
|
+
file.add_figure(fig)
|
12
|
+
|
13
|
+
graph = Graph.new
|
14
|
+
graph.add_option(XLabel.new(latex('x label')))
|
15
|
+
graph.add_option(GraphTitle.new(latex('A graph title')))
|
16
|
+
graph.add_option(YLabel.new(latex('the y label')))
|
17
|
+
graph.add_option(YLabelRight.new(latex('the y label on the right')))
|
18
|
+
fig.add_drawable(graph)
|
19
|
+
|
20
|
+
#plot y = x^2
|
21
|
+
x = (1..20).to_a
|
22
|
+
y = Array.new
|
23
|
+
x.each { |v| y.push(v**2) }
|
24
|
+
gd = GraphData.new(x, y)
|
25
|
+
gd.add_option(RubyPost::Colour.new(0.0,1.0,0.0))
|
26
|
+
graph.add_data(gd)
|
27
|
+
|
28
|
+
file.compile('figures')
|
29
|
+
|
30
|
+
file.view
|
data/tests/test_pair.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
#require 'rubygems'
|
4
|
+
#gem 'rubypost'
|
5
|
+
#require 'rubypost'
|
6
|
+
|
7
|
+
#load the rest of rubypost
|
8
|
+
require '../lib/objects'
|
9
|
+
require '../lib/drawable'
|
10
|
+
require '../lib/options'
|
11
|
+
|
12
|
+
include RubyPost
|
13
|
+
|
14
|
+
|
15
|
+
class TestPair < Test::Unit::TestCase
|
16
|
+
def testcompare
|
17
|
+
assert_equal(Pair.new(1,2), Pair.new(1,2))
|
18
|
+
end
|
19
|
+
def testadd
|
20
|
+
assert_equal(Pair.new(3,5), Pair.new(1,2)+Pair.new(2,3))
|
21
|
+
end
|
22
|
+
def testsubtract
|
23
|
+
assert_equal(Pair.new(3,5), Pair.new(4,4)-Pair.new(1,-1))
|
24
|
+
end
|
25
|
+
def testmultiply
|
26
|
+
assert_equal(Pair.new(2,2), Pair.new(4,4)*0.5)
|
27
|
+
end
|
28
|
+
def testdivide
|
29
|
+
assert_equal(Pair.new(2,2), Pair.new(4,4)/2)
|
30
|
+
end
|
31
|
+
end
|
data/tests/test_path.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#require 'rubygems'
|
2
|
+
#gem 'rubypost'
|
3
|
+
#require 'rubypost'
|
4
|
+
|
5
|
+
#load the rest of rubypost
|
6
|
+
require '../lib/objects'
|
7
|
+
require '../lib/drawable'
|
8
|
+
require '../lib/options'
|
9
|
+
|
10
|
+
include RubyPost
|
11
|
+
|
12
|
+
#note, can't use underscores in filenames if you are going
|
13
|
+
#to use File#view. tex mproof breaks
|
14
|
+
file = RubyPost::File.new('testpath')
|
15
|
+
|
16
|
+
#draw a curved path
|
17
|
+
fig1 = Figure.new
|
18
|
+
file.add_figure(fig1)
|
19
|
+
path = Path.new
|
20
|
+
path.curved
|
21
|
+
8.times{ |i| path.add_pair(Pair.new(i.cm, ((i/4.0)**2).cm)) }
|
22
|
+
fig1.add_drawable(path)
|
23
|
+
|
24
|
+
#draw the same path but with an arrow at the end
|
25
|
+
fig2 = Figure.new
|
26
|
+
file.add_figure(fig2)
|
27
|
+
arrow = Path.new.arrow
|
28
|
+
arrow.curved
|
29
|
+
8.times{ |i| arrow.add_pair(Pair.new(i.cm, ((i/4.0)**2).cm)) }
|
30
|
+
fig2.add_drawable(arrow)
|
31
|
+
|
32
|
+
#draw the same path but with an arrow on both ends
|
33
|
+
fig3 = Figure.new
|
34
|
+
file.add_figure(fig3)
|
35
|
+
#see the ruby reference for clone symatics
|
36
|
+
arrow2 = arrow.clone
|
37
|
+
arrow2.dblarrow
|
38
|
+
fig3.add_drawable(arrow2)
|
39
|
+
|
40
|
+
#draw the same path but with an arrow at the start
|
41
|
+
#this is achieved by reversing the path
|
42
|
+
fig4 = Figure.new
|
43
|
+
file.add_figure(fig4)
|
44
|
+
#see the ruby reference for clone symatics
|
45
|
+
arrow3 = arrow.clone
|
46
|
+
arrow3.reverse
|
47
|
+
fig4.add_drawable(arrow3)
|
48
|
+
|
49
|
+
#draw the arrow but scale it by 0.5 and make it dashed
|
50
|
+
fig5 = Figure.new
|
51
|
+
file.add_figure(fig5)
|
52
|
+
arrow4 = Path.new.arrow
|
53
|
+
arrow4.add_option(Scale.new(0.5))
|
54
|
+
arrow4.add_option(Dashed.new)
|
55
|
+
8.times{ |i| arrow4.add_pair(Pair.new(i.cm, ((i/4.0)**2).cm)) }
|
56
|
+
fig5.add_drawable(arrow4)
|
57
|
+
|
58
|
+
#draw the same arrow but scale it by 0.5 and rotate 140 degrees
|
59
|
+
#and make it red
|
60
|
+
fig6 = Figure.new
|
61
|
+
file.add_figure(fig6)
|
62
|
+
arrow5 = Path.new.arrow
|
63
|
+
arrow5.add_option(Rotate.new(140))
|
64
|
+
arrow5.add_option(Scale.new(0.5))
|
65
|
+
arrow5.add_option(Colour.new(1.0,0.0,0.0))
|
66
|
+
8.times{ |i| arrow5.add_pair(Pair.new(i.cm, ((i/4.0)**2).cm)) }
|
67
|
+
fig6.add_drawable(arrow5)
|
68
|
+
|
69
|
+
#draw arrow5 again and draw another arrow but translate right by 5cm.
|
70
|
+
#note the order of translation and rotation is important.
|
71
|
+
#also make the colour blue and use a square pen that is
|
72
|
+
#thickened by a factor of 2
|
73
|
+
fig7 = Figure.new
|
74
|
+
file.add_figure(fig7)
|
75
|
+
arrow6 = Path.new.arrow
|
76
|
+
arrow6.add_option(Rotate.new(140))
|
77
|
+
arrow6.add_option(Scale.new(0.5))
|
78
|
+
arrow6.add_option(Translate.new(Pair.new(5.0.cm,0.0)))
|
79
|
+
arrow6.add_option(Colour.new(0.0,0.0,1.0))
|
80
|
+
arrow6.add_option(Pen.new('pensquare', 2.0))
|
81
|
+
8.times{ |i| arrow6.add_pair(Pair.new(i.cm, ((i/4.0)**2).cm)) }
|
82
|
+
fig7.add_drawable(arrow6)
|
83
|
+
fig7.add_drawable(arrow5)
|
84
|
+
|
85
|
+
#draw the path, make it cycle
|
86
|
+
fig8 = Figure.new
|
87
|
+
file.add_figure(fig8)
|
88
|
+
cycled_path = Path.new
|
89
|
+
8.times{ |i| cycled_path.add_pair(Pair.new(i.cm, ((i/3.0)**2).cm)) }
|
90
|
+
cycled_path.add_pair('cycle')
|
91
|
+
fig8.add_drawable(cycled_path)
|
92
|
+
|
93
|
+
#draw the path, make it cycle and filled with colour
|
94
|
+
fig9 = Figure.new
|
95
|
+
file.add_figure(fig9)
|
96
|
+
filled_path = Path.new.fill
|
97
|
+
8.times{ |i| filled_path.add_pair(Pair.new(i.cm, ((i/3.0)**2).cm)) }
|
98
|
+
filled_path.add_pair('cycle')
|
99
|
+
fig9.add_drawable(filled_path)
|
100
|
+
|
101
|
+
#draw the path, make it cycle and filled with colour
|
102
|
+
fig10 = Figure.new
|
103
|
+
file.add_figure(fig10)
|
104
|
+
test_center = Path.new
|
105
|
+
test_center.add_pair(Pair.new(0.0,0.0))
|
106
|
+
test_center.add_pair(Pair.new(0.0,3.0.cm))
|
107
|
+
test_center.add_pair(Pair.new(3.0.cm,3.0.cm))
|
108
|
+
test_center.add_pair(Pair.new(3.0.cm,0.0))
|
109
|
+
test_center.add_pair(Pair.new('cycle'))
|
110
|
+
|
111
|
+
filled_path.add_pair('cycle')
|
112
|
+
fig10.add_drawable(filled_path)
|
113
|
+
|
114
|
+
puts file.compile_to_string
|
115
|
+
file.compile
|
116
|
+
file.view
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#require 'rubygems'
|
2
|
+
#gem 'rubypost'
|
3
|
+
#require 'rubypost'
|
4
|
+
|
5
|
+
#load the rest of rubypost
|
6
|
+
require '../lib/objects'
|
7
|
+
require '../lib/drawable'
|
8
|
+
require '../lib/options'
|
9
|
+
|
10
|
+
include RubyPost
|
11
|
+
|
12
|
+
#note, can't use underscores in filenames if you are going
|
13
|
+
#to use File#view. tex mproof breaks
|
14
|
+
file = RubyPost::File.new('testsquare')
|
15
|
+
|
16
|
+
#draw a square
|
17
|
+
fig1 = Figure.new
|
18
|
+
file.add_figure(fig1)
|
19
|
+
square1 = RubyPost::Square.new.scale(1.cm)
|
20
|
+
fig1.add_drawable(square1)
|
21
|
+
|
22
|
+
#draw a curved path
|
23
|
+
fig2 = Figure.new
|
24
|
+
file.add_figure(fig2)
|
25
|
+
square2 = RubyPost::Square.new
|
26
|
+
square2.add_option(Rotate.new(45))
|
27
|
+
square2.add_option(Scale.new(2.cm))
|
28
|
+
square2.add_option(Colour.new(1.0,0.0,1.0))
|
29
|
+
square2.add_option(Dashed.new)
|
30
|
+
square2.add_option(Pen.new('pencircle', 2.0))
|
31
|
+
fig2.add_drawable(square2)
|
32
|
+
|
33
|
+
#draw a green filled square
|
34
|
+
#we are using the colour method for Drawable. It actually just
|
35
|
+
#calls add_option(Colour.new(r,g,b))
|
36
|
+
fig3 = Figure.new
|
37
|
+
file.add_figure(fig3)
|
38
|
+
square3= RubyPost::Square.new.fill.scale(1.cm).colour(0.0,1.0,0.0)
|
39
|
+
fig3.add_drawable(square3)
|
40
|
+
|
41
|
+
|
42
|
+
puts file.compile_to_string
|
43
|
+
file.compile
|
44
|
+
file.view
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: rubypost
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2008-01-22 00:00:00 +10:00
|
8
|
+
summary: Ruby wrapper for the MetaPost drawing language
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: harprobey@gmail.com
|
12
|
+
homepage:
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: lib/rubypost
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Robby McKilliam
|
31
|
+
files:
|
32
|
+
- tests/test_cricle.rb
|
33
|
+
- tests/test_graph.rb
|
34
|
+
- tests/test_pair.rb
|
35
|
+
- tests/test_path.rb
|
36
|
+
- tests/test_square.rb
|
37
|
+
- lib/drawable.rb
|
38
|
+
- lib/graph.rb
|
39
|
+
- lib/objects.rb
|
40
|
+
- lib/options.rb
|
41
|
+
- lib/redefine_float_to_s_for_metapost.rb
|
42
|
+
- lib/revert_float_to_s.rb
|
43
|
+
- lib/rubypost.rb
|
44
|
+
- README
|
45
|
+
test_files:
|
46
|
+
- tests/test_path.rb
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
extra_rdoc_files:
|
50
|
+
- README
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
dependencies: []
|
58
|
+
|