rubypost 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|